From python-checkins at python.org Sat Feb 1 01:51:40 2014 From: python-checkins at python.org (brian.quinlan) Date: Sat, 1 Feb 2014 01:51:40 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320319=3A_concurre?= =?utf-8?q?nt=2Efutures=2Ewait=28=29_can_block_forever_even_if_Futures_hav?= =?utf-8?q?e?= Message-ID: <3fGGvw1c58z7Ll9@mail.python.org> http://hg.python.org/cpython/rev/0bcf23a52d55 changeset: 88869:0bcf23a52d55 user: Brian Quinlan date: Sat Feb 01 11:49:04 2014 +1100 summary: Issue #20319: concurrent.futures.wait() can block forever even if Futures have completed files: Lib/concurrent/futures/_base.py | 6 ++++-- Misc/NEWS | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py --- a/Lib/concurrent/futures/_base.py +++ b/Lib/concurrent/futures/_base.py @@ -225,7 +225,8 @@ finally: for f in fs: - f._waiters.remove(waiter) + with f._condition: + f._waiters.remove(waiter) DoneAndNotDoneFutures = collections.namedtuple( 'DoneAndNotDoneFutures', 'done not_done') @@ -272,7 +273,8 @@ waiter.event.wait(timeout) for f in fs: - f._waiters.remove(waiter) + with f._condition: + f._waiters.remove(waiter) done.update(waiter.finished_futures) return DoneAndNotDoneFutures(done, set(fs) - done) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -69,6 +69,12 @@ - Issue #17481: inspect.getfullargspec() now uses inspect.signature() API. +- Issue #15304: concurrent.futures.wait() can block forever even if + Futures have completed. Patch by Glenn Langford. + +Fix warning message when `os.chdir()` fails inside + `test.support.temp_cwd()`. Patch by Chris Jerdonek. + IDLE ---- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 02:08:26 2014 From: python-checkins at python.org (brian.quinlan) Date: Sat, 1 Feb 2014 02:08:26 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_extra_line_added_in_0b?= =?utf-8?q?cf23a52d55?= Message-ID: <3fGHHG3qG2zMFN@mail.python.org> http://hg.python.org/cpython/rev/4f59b836fa54 changeset: 88870:4f59b836fa54 user: Brian Quinlan date: Sat Feb 01 12:07:54 2014 +1100 summary: Fix extra line added in 0bcf23a52d55 files: Misc/NEWS | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -72,9 +72,6 @@ - Issue #15304: concurrent.futures.wait() can block forever even if Futures have completed. Patch by Glenn Langford. -Fix warning message when `os.chdir()` fails inside - `test.support.temp_cwd()`. Patch by Chris Jerdonek. - IDLE ---- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 02:22:31 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 02:22:31 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320452=3A_test=5Fa?= =?utf-8?q?syncio_checks_also_the_granularity?= Message-ID: <3fGHbW6mkgz7Lk9@mail.python.org> http://hg.python.org/cpython/rev/a090804862f8 changeset: 88871:a090804862f8 user: Victor Stinner date: Sat Feb 01 02:17:54 2014 +0100 summary: Issue #20452: test_asyncio checks also the granularity files: Lib/test/test_asyncio/test_events.py | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1179,6 +1179,14 @@ calls.append(self.loop._run_once_counter) self.assertEqual(calls, [1, 3, 5, 6]) + def test_granularity(self): + granularity = self.loop._granularity + self.assertGreater(granularity, 0.0) + # Worst expected granularity: 1 ms on Linux (limited by poll/epoll + # resolution), 15.6 ms on Windows (limited by time.monotonic + # resolution) + self.assertLess(granularity, 0.050) + class SubprocessTestsMixin: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 02:22:33 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 02:22:33 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320452=3A_Fix_test?= =?utf-8?q?=5Ftime=5Fand=5Fcall=5Fat=28=29_of_test=5Fasyncio_on_Windows?= Message-ID: <3fGHbY1270z7Lk9@mail.python.org> http://hg.python.org/cpython/rev/60a960434e5c changeset: 88872:60a960434e5c user: Victor Stinner date: Sat Feb 01 02:18:52 2014 +0100 summary: Issue #20452: Fix test_time_and_call_at() of test_asyncio on Windows Use the granularity to check the minimum time delta, instead of arbitrary value. files: Lib/test/test_asyncio/test_base_events.py | 15 +++++----- 1 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -116,17 +116,18 @@ self.loop.stop() self.loop._process_events = unittest.mock.Mock() - when = self.loop.time() + 0.1 + delay = 0.1 + + when = self.loop.time() + delay self.loop.call_at(when, cb) t0 = self.loop.time() self.loop.run_forever() dt = self.loop.time() - t0 - self.assertTrue(0.09 <= dt <= 0.9, - # Issue #20452: add more info in case of failure, - # to try to investigate the bug - (dt, - self.loop._granularity, - time.get_clock_info('monotonic'))) + + self.assertGreaterEqual(dt, delay - self.loop._granularity, dt) + # tolerate a difference of +800 ms because some Python buildbots + # are really slow + self.assertLessEqual(dt, 0.9, dt) def test_run_once_in_executor_handle(self): def cb(): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 03:19:20 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 03:19:20 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_doc=3A_document_th?= =?utf-8?q?e_granularity_of_the_event_loop?= Message-ID: <3fGJs40XyFz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/3fea5a721344 changeset: 88873:3fea5a721344 user: Victor Stinner date: Sat Feb 01 02:36:43 2014 +0100 summary: asyncio doc: document the granularity of the event loop Improve also the "Logging" section files: Doc/library/asyncio-dev.rst | 17 +++++++++++------ Doc/library/asyncio-eventloop.rst | 18 +++++++++++++++++- Doc/library/asyncio-task.rst | 3 +++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -7,6 +7,8 @@ This page lists common traps and explains how to avoid them. +.. _asyncio-handle-blocking: + Handle correctly blocking functions ----------------------------------- @@ -21,17 +23,20 @@ different process, to not block the thread of the event loop. See the :func:`BaseEventLoop.run_in_executor` function. +.. seealso:: + + The :ref:`Delayed calls ` section details how the + event loop handles time. + .. _asyncio-logger: -Logger ------- +Logging +------- -.. data:: asyncio.logger.log +The :mod:`asyncio` module logs information with the :mod:`logging` module in +the logger ``'asyncio'``. - :class:`logging.Logger` instance used by :mod:`asyncio` to log messages. - -The logger name is ``'asyncio'``. .. _asyncio-coroutine-not-scheduled: diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -108,6 +108,8 @@ Like :meth:`call_soon`, but thread safe. +.. _asyncio-delayed-calls: + Delayed calls ------------- @@ -116,6 +118,20 @@ implementation; ideally it is a monotonic clock. This will generally be a different clock than :func:`time.time`. +The granularity of the event loop depends on the resolution of the +:meth:`~BaseEventLoop.time` method and the resolution of the selector. It is +usually between 1 ms and 16 ms. For example, a granularity of 1 ms means that +in the best case, the difference between the expected delay and the real +elapsed time is between -1 ms and +1 ms: a call scheduled in 1 nanosecond may +be called in 1 ms, and a call scheduled in 100 ms may be called in 99 ms. + +The granularity is the best difference in theory. In practice, it depends on +the system load and the the time taken by tasks executed by the event loop. +For example, if a task blocks the event loop for 1 second, all tasks scheduled +in this second will be delayed. The :ref:`Handle correctly blocking functions +` section explains how to avoid such issue. + + .. method:: BaseEventLoop.call_later(delay, callback, *args) Arrange for the *callback* to be called after the given *delay* @@ -290,7 +306,7 @@ On Windows, the default event loop uses :class:`selectors.SelectSelector` which only supports sockets. The - :class:`ProactorEventLoop` should be used instead. + :class:`ProactorEventLoop` should be used to support subprocesses. .. note:: diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -441,6 +441,9 @@ time (in seconds). If *result* is provided, it is produced to the caller when the coroutine completes. + The resolution of the sleep depends on the :ref:`granularity of the event + loop `. + .. function:: shield(arg, \*, loop=None) Wait for a future, shielding it from cancellation. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 03:19:21 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 03:19:21 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_doc=3A_add_=22Conc?= =?utf-8?q?urrency_and_multithreading=22_section?= Message-ID: <3fGJs53YxFz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/a3b4f4b1aedd changeset: 88874:a3b4f4b1aedd user: Victor Stinner date: Sat Feb 01 03:18:58 2014 +0100 summary: asyncio doc: add "Concurrency and multithreading" section files: Doc/library/asyncio-dev.rst | 30 +++++++++++++++++++++-- Doc/library/asyncio-sync.rst | 2 +- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -7,6 +7,32 @@ This page lists common traps and explains how to avoid them. +.. _asyncio-multithreading: + +Concurrency and multithreading +------------------------------ + +An event loop runs in a thread and executes all callbacks and tasks in the same +thread. If a callback should be scheduled from a different thread, the +:meth:`BaseEventLoop.call_soon_threadsafe` method should be used. + +While a task in running in the event loop, no other task is running in the same +thread. But when the task uses ``yield from``, the task is suspended and the +event loop executes the next task. + +To handle signals and to execute subprocesses, the event loop must be run in +the main thread. + +The :meth:`BaseEventLoop.run_in_executor` method can be used with a thread pool +executor to execute a callback in different thread to not block the thread of +the event loop. + +.. seealso:: + + See the :ref:`Synchronization primitives ` section to + synchronize tasks. + + .. _asyncio-handle-blocking: Handle correctly blocking functions @@ -21,7 +47,7 @@ An executor can be used to run a task in a different thread or even in a different process, to not block the thread of the event loop. See the -:func:`BaseEventLoop.run_in_executor` function. +:meth:`BaseEventLoop.run_in_executor` method. .. seealso:: @@ -213,5 +239,3 @@ yield from asyncio.sleep(2.0) loop.stop() -.. XXX: Document "poll xxx" log message? - diff --git a/Doc/library/asyncio-sync.rst b/Doc/library/asyncio-sync.rst --- a/Doc/library/asyncio-sync.rst +++ b/Doc/library/asyncio-sync.rst @@ -1,5 +1,5 @@ .. currentmodule:: asyncio -.. _sync: +.. _asyncio-sync: Synchronization primitives ========================== -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 03:39:20 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 03:39:20 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320162=3A_Fix_an_a?= =?utf-8?q?lignment_issue_in_the_siphash24=28=29_hash_function_which?= Message-ID: <3fGKJ80hmMz7Llc@mail.python.org> http://hg.python.org/cpython/rev/caebb4f231da changeset: 88875:caebb4f231da user: Victor Stinner date: Sat Feb 01 03:38:56 2014 +0100 summary: Issue #20162: Fix an alignment issue in the siphash24() hash function which caused a crash on PowerPC 64-bit (ppc64). files: Misc/NEWS | 3 +++ Python/pyhash.c | 2 +- 2 files changed, 4 insertions(+), 1 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #20162: Fix an alignment issue in the siphash24() hash function which + caused a crash on PowerPC 64-bit (ppc64). + Library ------- diff --git a/Python/pyhash.c b/Python/pyhash.c --- a/Python/pyhash.c +++ b/Python/pyhash.c @@ -399,7 +399,7 @@ case 7: pt[6] = m[6]; case 6: pt[5] = m[5]; case 5: pt[4] = m[4]; - case 4: *((PY_UINT32_T*)&pt[0]) = *((PY_UINT32_T*)&m[0]); break; + case 4: Py_MEMCPY(pt, m, sizeof(PY_UINT32_T)); break; case 3: pt[2] = m[2]; case 2: pt[1] = m[1]; case 1: pt[0] = m[0]; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 03:54:12 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 03:54:12 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320354=3A_Fix_alig?= =?utf-8?q?nment_issue_in_the_tracemalloc_module_on_64-bit?= Message-ID: <3fGKdJ6n07z7Lk8@mail.python.org> http://hg.python.org/cpython/rev/fb2cdec2c70c changeset: 88876:fb2cdec2c70c user: Victor Stinner date: Sat Feb 01 03:43:58 2014 +0100 summary: Issue #20354: Fix alignment issue in the tracemalloc module on 64-bit platforms. Bug seen on 64-bit Linux when using "make profile-opt". Only align the "frame_t" structure on 32-bit when Visual Studio is used. Before the alignment to 32-bit was applied to the whole file any compiler supporting "#pragma pack(4)" which includes GCC. files: Modules/_tracemalloc.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -61,10 +61,11 @@ architectures: 12 bytes instead of 16. This optimization might produce SIGBUS on architectures not supporting unaligned memory accesses (64-bit IPS CPU?): on such architecture, the structure must not be packed. */ -#pragma pack(4) typedef struct #ifdef __GNUC__ __attribute__((packed)) +#elif defined(_MSC_VER) +_declspec(align(4)) #endif { PyObject *filename; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 04:07:20 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 04:07:20 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320354=3A_Mention_?= =?utf-8?q?the_fix_in_Misc/NEWS?= Message-ID: <3fGKwS2ks1z7LkH@mail.python.org> http://hg.python.org/cpython/rev/44b554454971 changeset: 88877:44b554454971 user: Victor Stinner date: Sat Feb 01 03:58:07 2014 +0100 summary: Issue #20354: Mention the fix in Misc/NEWS files: Misc/NEWS | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,9 @@ Library ------- +- Issue #20354: Fix an alignment issue in the tracemalloc module on 64-bit + platforms. Bug seen on 64-bit Linux when using "make profile-opt". + - Issue #17159: inspect.signature now accepts duck types of functions, which adds support for Cython functions. Initial patch by Stefan Behnel. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 04:07:21 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 04:07:21 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_tracemalloc=3A_Fix_slicing?= =?utf-8?q?_traces_and_fix_slicing_a_traceback=2E?= Message-ID: <3fGKwT4D3tz7Llf@mail.python.org> http://hg.python.org/cpython/rev/72bd45facbd7 changeset: 88878:72bd45facbd7 user: Victor Stinner date: Sat Feb 01 04:07:02 2014 +0100 summary: tracemalloc: Fix slicing traces and fix slicing a traceback. files: Lib/test/test_tracemalloc.py | 10 ++++++++-- Lib/tracemalloc.py | 12 ++++++++---- Misc/NEWS | 2 ++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -123,7 +123,6 @@ self.assertEqual(len(traceback), 1) self.assertEqual(traceback, obj_traceback) - def find_trace(self, traces, traceback): for trace in traces: if trace[1] == traceback._frames: @@ -147,7 +146,6 @@ tracemalloc.stop() self.assertEqual(tracemalloc._get_traces(), []) - def test_get_traces_intern_traceback(self): # dummy wrappers to get more useful and identical frames in the traceback def allocate_bytes2(size): @@ -503,6 +501,14 @@ self.assertEqual(str(stat), 'a.py:5: size=5002 B (+5000 B), count=2 (+1), average=2501 B') + def test_slices(self): + snapshot, snapshot2 = create_snapshots() + self.assertEqual(snapshot.traces[:2], + (snapshot.traces[0], snapshot.traces[1])) + + traceback = snapshot.traces[0].traceback + self.assertEqual(traceback[:2], + (traceback[0], traceback[1])) class TestFilters(unittest.TestCase): diff --git a/Lib/tracemalloc.py b/Lib/tracemalloc.py --- a/Lib/tracemalloc.py +++ b/Lib/tracemalloc.py @@ -182,8 +182,10 @@ return len(self._frames) def __getitem__(self, index): - trace = self._frames[index] - return Frame(trace) + if isinstance(index, slice): + return tuple(Frame(trace) for trace in self._frames[index]) + else: + return Frame(self._frames[index]) def __contains__(self, frame): return frame._frame in self._frames @@ -259,8 +261,10 @@ return len(self._traces) def __getitem__(self, index): - trace = self._traces[index] - return Trace(trace) + if isinstance(index, slice): + return tuple(Trace(trace) for trace in self._traces[index]) + else: + return Trace(self._traces[index]) def __contains__(self, trace): return trace._trace in self._traces diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,8 @@ Library ------- +- tracemalloc: Fix slicing traces and fix slicing a traceback. + - Issue #20354: Fix an alignment issue in the tracemalloc module on 64-bit platforms. Bug seen on 64-bit Linux when using "make profile-opt". -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 04:11:28 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 04:11:28 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_test=5Fasyncio=3A_relax_ti?= =?utf-8?q?ming=2C_the_=22AMD64_Windows_Server_2008_=5BSB=5D_3=2Ex=22_buil?= =?utf-8?q?dbot?= Message-ID: <3fGL1D2K1jz7LjS@mail.python.org> http://hg.python.org/cpython/rev/0bee98291117 changeset: 88879:0bee98291117 user: Victor Stinner date: Sat Feb 01 04:11:16 2014 +0100 summary: test_asyncio: relax timing, the "AMD64 Windows Server 2008 [SB] 3.x" buildbot looks to be slow files: Lib/test/test_asyncio/test_windows_events.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -105,7 +105,7 @@ self.loop.run_until_complete(f) elapsed = self.loop.time() - start self.assertFalse(f.result()) - self.assertTrue(0.18 < elapsed < 0.5, elapsed) + self.assertTrue(0.18 < elapsed < 0.9, elapsed) _overlapped.SetEvent(event) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 04:26:58 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 04:26:58 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_test=5Fhash=3A_Fix_a_Bytes?= =?utf-8?q?Warning_in_get=5Fhash=5Fcommand=28=29?= Message-ID: <3fGLM60G0Yz7LjW@mail.python.org> http://hg.python.org/cpython/rev/a29f05545ab5 changeset: 88880:a29f05545ab5 user: Victor Stinner date: Sat Feb 01 04:26:46 2014 +0100 summary: test_hash: Fix a BytesWarning in get_hash_command() files: Lib/test/test_hash.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_hash.py b/Lib/test/test_hash.py --- a/Lib/test/test_hash.py +++ b/Lib/test/test_hash.py @@ -172,7 +172,7 @@ # an object to be tested def get_hash_command(self, repr_): - return 'print(hash(eval(%s.decode("utf-8"))))' % repr_.encode("utf-8") + return 'print(hash(eval(%r.decode("utf-8"))))' % repr_.encode("utf-8") def get_hash(self, repr_, seed=None): env = os.environ.copy() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 04:30:59 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 04:30:59 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_test=5Fhash_on_=22SPAR?= =?utf-8?q?C_Solaris_10_=28cc=252C_64b=29_=5BSB=5D_3=2Ex=22_buildbot?= Message-ID: <3fGLRl3pCnz7LjW@mail.python.org> http://hg.python.org/cpython/rev/22e88355acd6 changeset: 88881:22e88355acd6 user: Victor Stinner date: Sat Feb 01 04:30:48 2014 +0100 summary: Fix test_hash on "SPARC Solaris 10 (cc%2C 64b) [SB] 3.x" buildbot I picked the value from the error message: ====================================================================== FAIL: test_ucs2_string (test.test_hash.StrHashRandomizationTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/cpython/buildslave/cc-64/3.x.snakebite-sol10-sparc-cc-64/build/Lib/test/test_hash.py", line 292, in test_ucs2_string self.assertEqual(self.get_hash(self.repr_ucs2, seed=42), h) AssertionError: -3927695501187247084 != None files: Lib/test/test_hash.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_hash.py b/Lib/test/test_hash.py --- a/Lib/test/test_hash.py +++ b/Lib/test/test_hash.py @@ -233,7 +233,8 @@ [44402817, 8998297579845987431, -1956240331, -782697888614047887], # seed 42, '????' - [-283066365, -4576729883824601543, -271871407, None], + [-283066365, -4576729883824601543, -271871407, + -3927695501187247084], ] } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 07:03:33 2014 From: python-checkins at python.org (larry.hastings) Date: Sat, 1 Feb 2014 07:03:33 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=23Issue_20456=3A_Several_?= =?utf-8?q?improvements_and_bugfixes_for_Argument_Clinic=2C?= Message-ID: <3fGPqn0mLFz7Ljb@mail.python.org> http://hg.python.org/cpython/rev/19d81cc213d7 changeset: 88882:19d81cc213d7 user: Larry Hastings date: Fri Jan 31 22:03:12 2014 -0800 summary: #Issue 20456: Several improvements and bugfixes for Argument Clinic, including correctly generating code for Clinic blocks inside C preprocessor conditional blocks. files: Doc/howto/clinic.rst | 172 +++-- Misc/NEWS | 14 + Modules/_cursesmodule.c | 4 +- Modules/_dbmmodule.c | 42 +- Modules/_opcode.c | 50 +- Modules/clinic/zlibmodule.c.h | 18 +- Modules/posixmodule.c | 36 +- Python/import.c | 12 +- Tools/clinic/clinic.py | 586 ++++++++++++--------- Tools/clinic/clinic_test.py | 49 +- Tools/clinic/cpp.py | 191 +++++++ 11 files changed, 762 insertions(+), 412 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -561,8 +561,8 @@ to allow full expressions like ``CONSTANT - 1``.) -Renaming the C functions generated by Argument Clinic ------------------------------------------------------ +Renaming the C functions and variables generated by Argument Clinic +------------------------------------------------------------------- Argument Clinic automatically names the functions it generates for you. Occasionally this may cause a problem, if the generated name collides with @@ -584,6 +584,25 @@ and the impl function would now be named ``pickler_dumper_impl()``. +Similarly, you may have a problem where you want to give a parameter +a specific Python name, but that name may be inconvenient in C. Argument +Clinic allows you to give a parameter different names in Python and in C, +using the same ``"as"`` syntax:: + + /*[clinic input] + pickle.Pickler.dump + + obj: object + file as file_obj: object + protocol: object = NULL + * + fix_imports: bool = True + +Here, the name used in Python (in the signature and the ``keywords`` +array) would be ``file``, but the C variable would be named ``file_obj``. + +You can use this to rename the ``self`` parameter too! + Converting functions using PyArg_UnpackTuple -------------------------------------------- @@ -1308,74 +1327,6 @@ (If your function doesn't support keywords, the parsing function generated will throw an exception if it receives any.) -The #ifdef trick ----------------------------------------------- - -If you're converting a function that isn't available on all platforms, -there's a trick you can use to make life a little easier. The existing -code probably looks like this:: - - #ifdef HAVE_FUNCTIONNAME - static module_functionname(...) - { - ... - } - #endif /* HAVE_FUNCTIONNAME */ - -And then in the ``PyMethodDef`` structure at the bottom you'll have:: - - #ifdef HAVE_FUNCTIONNAME - {'functionname', ... }, - #endif /* HAVE_FUNCTIONNAME */ - -In this scenario, you should change the code to look like the following:: - - #ifdef HAVE_FUNCTIONNAME - /*[clinic input] - module.functionname - ... - [clinic start generated code]*/ - static module_functionname(...) - { - ... - } - #endif /* HAVE_FUNCTIONNAME */ - -Run Argument Clinic on the code in this state, then refresh the file in -your editor. Now you'll have the generated code, including the #define -for the ``PyMethodDef``, like so:: - - #ifdef HAVE_FUNCTIONNAME - /*[clinic input] - ... - [clinic start generated code]*/ - ... - #define MODULE_FUNCTIONNAME \ - {'functionname', ... }, - ... - /*[clinic end generated code: checksum=...]*/ - static module_functionname(...) - { - ... - } - #endif /* HAVE_FUNCTIONNAME */ - -Change the #endif at the bottom as follows:: - - #else - #define MODULE_FUNCTIONNAME - #endif /* HAVE_FUNCTIONNAME */ - -Now you can remove the #ifdefs around the ``PyMethodDef`` structure -at the end, and replace those three lines with ``MODULE_FUNCTIONNAME``. -If the function is available, the macro turns into the ``PyMethodDef`` -static value, including the trailing comma; if the function isn't -available, the macro turns into nothing. Perfect! - -(This is the preferred approach for optional functions; in the future, -Argument Clinic may generate the entire ``PyMethodDef`` structure.) - - Changing and redirecting Clinic's output ---------------------------------------- @@ -1491,8 +1442,9 @@ ``output preset`` sets Clinic's output to one of several built-in preset configurations, as follows: - ``original`` - Clinic's starting configuration. + ``block`` + Clinic's original starting configuration. Writes everything + immediately after the input block. Suppress the ``parser_prototype`` and ``docstring_prototype``, write everything else to ``block``. @@ -1640,6 +1592,82 @@ the file was not modified by hand before it gets overwritten. +The #ifdef trick +---------------------------------------------- + +If you're converting a function that isn't available on all platforms, +there's a trick you can use to make life a little easier. The existing +code probably looks like this:: + + #ifdef HAVE_FUNCTIONNAME + static module_functionname(...) + { + ... + } + #endif /* HAVE_FUNCTIONNAME */ + +And then in the ``PyMethodDef`` structure at the bottom the existing code +will have:: + + #ifdef HAVE_FUNCTIONNAME + {'functionname', ... }, + #endif /* HAVE_FUNCTIONNAME */ + +In this scenario, you should enclose the body of your impl function inside the ``#ifdef``, +like so:: + + #ifdef HAVE_FUNCTIONNAME + /*[clinic input] + module.functionname + ... + [clinic start generated code]*/ + static module_functionname(...) + { + ... + } + #endif /* HAVE_FUNCTIONNAME */ + +Then, remove those three lines from the ``PyMethodDef`` structure, +replacing them with the macro Argument Clinic generated:: + + MODULE_FUNCTIONNAME_METHODDEF + +(You can find the real name for this macro inside the generated code. +Or you can calculate it yourself: it's the name of your function as defined +on the first line of your block, but with periods changed to underscores, +uppercased, and ``"_METHODDEF"`` added to the end.) + +Perhaps you're wondering: what if ``HAVE_FUNCTIONNAME`` isn't defined? +The ``MODULE_FUNCTIONNAME_METHODDEF`` macro won't be defined either! + +Here's where Argument Clinic gets very clever. It actually detects that the +Argument Clinic block might be deactivated by the ``#ifdef``. When that +happens, it generates a little extra code that looks like this:: + + #ifndef MODULE_FUNCTIONNAME_METHODDEF + #define MODULE_FUNCTIONNAME_METHODDEF + #endif /* !defined(MODULE_FUNCTIONNAME_METHODDEF) */ + +That means the macro always works. If the function is defined, this turns +into the correct structure, including the trailing comma. If the function is +undefined, this turns into nothing. + +However, this causes one ticklish problem: where should Argument Clinic put this +extra code when using the "block" output preset? It can't go in the output block, +because that could be decativated by the ``#ifdef``. (That's the whole point!) + +In this situation, Argument Clinic writes the extra code to the "buffer" destination. +This may mean that you get a complaint from Argument Clinic:: + + Warning in file "Modules/posixmodule.c" on line 12357: + Destination buffer 'buffer' not empty at end of file, emptying. + +When this happens, just open your file, find the ``dump buffer`` block that +Argument Clinic added to your file (it'll be at the very bottom), then +move it above the ``PyMethodDef`` structure where that macro is used. + + + Using Argument Clinic in Python files ------------------------------------- diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -95,6 +95,20 @@ Tools/Demos ----------- +- #Issue 20456: Argument Clinic now observes the C preprocessor conditional + compilation statements of the C files it parses. When a Clinic block is + inside a conditional code, it adjusts its output to match, including + automatically generating an empty methoddef macro. + +- #Issue 20456: Cloned functions in Argument Clinic now use the correct + name, not the name of the function they were cloned from, for text + strings inside generated code. + +- #Issue 20456: Fixed Argument Clinic's test suite and "--converters" feature. + +- #Issue 20456: Argument Clinic now allows specifying different names + for a parameter in Python and C, using "as" on the parameter line. + - Issue #20326: Argument Clinic now uses a simple, unique signature to annotate text signatures in docstrings, resulting in fewer false positives. "self" parameters are also explicitly marked, allowing diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -584,7 +584,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(curses_window_addch__doc__, -"addch(self, [x, y,] ch, [attr])\n" +"addch([x, y,] ch, [attr])\n" "Paint character ch at (y, x) with attributes attr.\n" "\n" " x\n" @@ -651,7 +651,7 @@ static PyObject * curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr) -/*[clinic end generated code: output=e1cdbd4f4e42fc6b input=fe7e3711d5bbf1f6]*/ +/*[clinic end generated code: output=43acb91a5c98f615 input=fe7e3711d5bbf1f6]*/ { PyCursesWindowObject *cwself = (PyCursesWindowObject *)self; int coordinates_group = group_left_1; diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -52,10 +52,11 @@ /*[python input] class dbmobject_converter(self_converter): type = "dbmobject *" - def converter_init(self): + def pre_render(self): + super().pre_render() self.name = 'dp' [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=8a69ac1827811128]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=6ad536357913879a]*/ static PyObject * newdbmobject(const char *file, int flags, int mode) @@ -270,23 +271,21 @@ self: dbmobject key: str(length=True) - [ - default: object - ] + default: object = None / Return the value for key if present, otherwise default. [clinic start generated code]*/ PyDoc_STRVAR(dbm_dbm_get__doc__, -"get(self, key, [default])\n" +"sig=($self, key, default=None)\n" "Return the value for key if present, otherwise default."); #define DBM_DBM_GET_METHODDEF \ {"get", (PyCFunction)dbm_dbm_get, METH_VARARGS, dbm_dbm_get__doc__}, static PyObject * -dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length, int group_right_1, PyObject *default_value); +dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length, PyObject *default_value); static PyObject * dbm_dbm_get(dbmobject *dp, PyObject *args) @@ -294,37 +293,24 @@ PyObject *return_value = NULL; const char *key; Py_ssize_clean_t key_length; - int group_right_1 = 0; - PyObject *default_value = NULL; + PyObject *default_value = Py_None; - switch (PyTuple_GET_SIZE(args)) { - case 1: - if (!PyArg_ParseTuple(args, "s#:get", &key, &key_length)) - goto exit; - break; - case 2: - if (!PyArg_ParseTuple(args, "s#O:get", &key, &key_length, &default_value)) - goto exit; - group_right_1 = 1; - break; - default: - PyErr_SetString(PyExc_TypeError, "dbm.dbm.get requires 1 to 2 arguments"); - goto exit; - } - return_value = dbm_dbm_get_impl(dp, key, key_length, group_right_1, default_value); + if (!PyArg_ParseTuple(args, + "s#|O:get", + &key, &key_length, &default_value)) + goto exit; + return_value = dbm_dbm_get_impl(dp, key, key_length, default_value); exit: return return_value; } static PyObject * -dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length, int group_right_1, PyObject *default_value) -/*[clinic end generated code: output=31d5180d6b36f1ea input=43a561dc2bd1db3b]*/ +dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length, PyObject *default_value) +/*[clinic end generated code: output=2bbaf9a187f9b6bf input=aecf5efd2f2b1a3b]*/ { datum dbm_key, val; - if (!group_right_1) - default_value = Py_None; dbm_key.dptr = (char *)key; dbm_key.dsize = key_length; check_dbmobject_open(dp); diff --git a/Modules/_opcode.c b/Modules/_opcode.c --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -11,49 +11,35 @@ _opcode.stack_effect -> int opcode: int - - [ - oparg: int - ] + oparg: object = None / Compute the stack effect of the opcode. [clinic start generated code]*/ PyDoc_STRVAR(_opcode_stack_effect__doc__, -"stack_effect(module, opcode, [oparg])\n" +"sig=($module, opcode, oparg=None)\n" "Compute the stack effect of the opcode."); #define _OPCODE_STACK_EFFECT_METHODDEF \ {"stack_effect", (PyCFunction)_opcode_stack_effect, METH_VARARGS, _opcode_stack_effect__doc__}, static int -_opcode_stack_effect_impl(PyModuleDef *module, int opcode, int group_right_1, int oparg); +_opcode_stack_effect_impl(PyModuleDef *module, int opcode, PyObject *oparg); static PyObject * _opcode_stack_effect(PyModuleDef *module, PyObject *args) { PyObject *return_value = NULL; int opcode; - int group_right_1 = 0; - int oparg = 0; + PyObject *oparg = Py_None; int _return_value; - switch (PyTuple_GET_SIZE(args)) { - case 1: - if (!PyArg_ParseTuple(args, "i:stack_effect", &opcode)) - goto exit; - break; - case 2: - if (!PyArg_ParseTuple(args, "ii:stack_effect", &opcode, &oparg)) - goto exit; - group_right_1 = 1; - break; - default: - PyErr_SetString(PyExc_TypeError, "_opcode.stack_effect requires 1 to 2 arguments"); - goto exit; - } - _return_value = _opcode_stack_effect_impl(module, opcode, group_right_1, oparg); + if (!PyArg_ParseTuple(args, + "i|O:stack_effect", + &opcode, &oparg)) + goto exit; + _return_value = _opcode_stack_effect_impl(module, opcode, oparg); if ((_return_value == -1) && PyErr_Occurred()) goto exit; return_value = PyLong_FromLong((long)_return_value); @@ -63,23 +49,31 @@ } static int -_opcode_stack_effect_impl(PyModuleDef *module, int opcode, int group_right_1, int oparg) -/*[clinic end generated code: output=4689140ffda2494a input=056816407c3d4284]*/ +_opcode_stack_effect_impl(PyModuleDef *module, int opcode, PyObject *oparg) +/*[clinic end generated code: output=4fe636f5db87c0a9 input=2d0a9ee53c0418f5]*/ { int effect; + int oparg_int = 0; if (HAS_ARG(opcode)) { - if (!group_right_1) { + PyObject *i_object; + if (oparg == Py_None) { PyErr_SetString(PyExc_ValueError, "stack_effect: opcode requires oparg but oparg was not specified"); return -1; } + i_object = PyNumber_Index(oparg); + if (!i_object) + return -1; + oparg_int = (int)PyLong_AsLong(oparg); + if ((oparg_int == -1) && PyErr_Occurred()) + return -1; } - else if (group_right_1) { + else if (oparg != Py_None) { PyErr_SetString(PyExc_ValueError, "stack_effect: opcode does not permit oparg but oparg was specified"); return -1; } - effect = PyCompile_OpcodeStackEffect(opcode, oparg); + effect = PyCompile_OpcodeStackEffect(opcode, oparg_int); if (effect == PY_INVALID_STACK_EFFECT) { PyErr_SetString(PyExc_ValueError, "invalid opcode or oparg"); diff --git a/Modules/clinic/zlibmodule.c.h b/Modules/clinic/zlibmodule.c.h --- a/Modules/clinic/zlibmodule.c.h +++ b/Modules/clinic/zlibmodule.c.h @@ -276,6 +276,8 @@ return return_value; } +#if defined(HAVE_ZLIB_COPY) + PyDoc_STRVAR(zlib_Compress_copy__doc__, "sig=($self)\n" "Return a copy of the compression object."); @@ -292,6 +294,14 @@ return zlib_Compress_copy_impl(self); } +#endif /* defined(HAVE_ZLIB_COPY) */ + +#ifndef ZLIB_COMPRESS_COPY_METHODDEF + #define ZLIB_COMPRESS_COPY_METHODDEF +#endif /* !defined(ZLIB_COMPRESS_COPY_METHODDEF) */ + +#if defined(HAVE_ZLIB_COPY) + PyDoc_STRVAR(zlib_Decompress_copy__doc__, "sig=($self)\n" "Return a copy of the decompression object."); @@ -308,6 +318,12 @@ return zlib_Decompress_copy_impl(self); } +#endif /* defined(HAVE_ZLIB_COPY) */ + +#ifndef ZLIB_DECOMPRESS_COPY_METHODDEF + #define ZLIB_DECOMPRESS_COPY_METHODDEF +#endif /* !defined(ZLIB_DECOMPRESS_COPY_METHODDEF) */ + PyDoc_STRVAR(zlib_Decompress_flush__doc__, "sig=($self, length=DEF_BUF_SIZE)\n" "Return a bytes object containing any remaining decompressed data.\n" @@ -408,4 +424,4 @@ return return_value; } -/*[clinic end generated code: output=ad23316b49faf7e6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=21556008559f839c input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -2366,21 +2366,26 @@ converter = 'path_converter' def converter_init(self, *, allow_fd=False, nullable=False): + # right now path_t doesn't support default values. + # to support a default value, you'll need to override initialize(). + if self.default is not unspecified: + fail("Can't specify a default to the path_t converter!") + + if self.c_default is not None: + fail("Can't specify a c_default to the path_t converter!") + + self.nullable = nullable + self.allow_fd = allow_fd + + def pre_render(self): def strify(value): return str(int(bool(value))) - # right now path_t doesn't support default values. - # to support a default value, you'll need to override initialize(). - - assert self.default is unspecified - - self.nullable = nullable - self.allow_fd = allow_fd - + # add self.py_name here when merging with posixmodule conversion self.c_default = 'PATH_T_INITIALIZE("{}", {}, {})'.format( self.function.name, - strify(nullable), - strify(allow_fd), + strify(self.nullable), + strify(self.allow_fd), ) def cleanup(self): @@ -2397,7 +2402,7 @@ [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=d702d58a8469cc7d]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=5c9f456f53244fc3]*/ /*[clinic input] @@ -11122,6 +11127,15 @@ #endif /* MS_WINDOWS */ +/*[clinic input] +dump buffer +[clinic start generated code]*/ + +#ifndef OS_TTYNAME_METHODDEF + #define OS_TTYNAME_METHODDEF +#endif /* !defined(OS_TTYNAME_METHODDEF) */ +/*[clinic end generated code: output=5d071bbc8f49ea12 input=524ce2e021e4eba6]*/ + static PyMethodDef posix_methods[] = { diff --git a/Python/import.c b/Python/import.c --- a/Python/import.c +++ b/Python/import.c @@ -2215,6 +2215,15 @@ #endif /* HAVE_DYNAMIC_LOADING */ +/*[clinic input] +dump buffer +[clinic start generated code]*/ + +#ifndef _IMP_LOAD_DYNAMIC_METHODDEF + #define _IMP_LOAD_DYNAMIC_METHODDEF +#endif /* !defined(_IMP_LOAD_DYNAMIC_METHODDEF) */ +/*[clinic end generated code: output=d07c1d4a343a9579 input=524ce2e021e4eba6]*/ + PyDoc_STRVAR(doc_imp, "(Extremely) low-level import machinery bits as used by importlib and imp."); @@ -2230,9 +2239,7 @@ _IMP_INIT_FROZEN_METHODDEF _IMP_IS_BUILTIN_METHODDEF _IMP_IS_FROZEN_METHODDEF -#ifdef HAVE_DYNAMIC_LOADING _IMP_LOAD_DYNAMIC_METHODDEF -#endif _IMP__FIX_CO_FILENAME_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -2324,3 +2331,4 @@ #ifdef __cplusplus } #endif + diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -10,6 +10,8 @@ import atexit import collections import contextlib +import copy +import cpp import functools import hashlib import inspect @@ -359,10 +361,16 @@ stop_line = "" checksum_line = "" + def __init__(self, filename): + pass + @abc.abstractmethod def render(self, clinic, signatures): pass + def parse_line(self, line): + pass + def validate(self): def assert_only_one(attr, *additional_fields): """ @@ -489,6 +497,30 @@ return tuple(accumulator) +def strip_leading_and_trailing_blank_lines(s): + lines = s.rstrip().split('\n') + while lines: + line = lines[0] + if line.strip(): + break + del lines[0] + return '\n'.join(lines) + + at functools.lru_cache() +def normalize_snippet(s, *, indent=0): + """ + Reformats s: + * removes leading and trailing blank lines + * ensures that it does not end with a newline + * dedents so the first nonwhite character on any line is at column "indent" + """ + s = strip_leading_and_trailing_blank_lines(s) + s = textwrap.dedent(s) + if indent: + s = textwrap.indent(s, ' ' * indent) + return s + + class CLanguage(Language): body_prefix = "#" @@ -498,6 +530,14 @@ stop_line = "[{dsl_name} start generated code]*/" checksum_line = "/*[{dsl_name} end generated code: {arguments}]*/" + def __init__(self, filename): + super().__init__(filename) + self.cpp = cpp.Monitor(filename) + self.cpp.fail = fail + + def parse_line(self, line): + self.cpp.writeline(line) + def render(self, clinic, signatures): function = None for o in signatures: @@ -519,184 +559,6 @@ add('"') return ''.join(text) - _templates = {} - # the templates will be run through str.format(), - # so actual curly-braces need to be doubled up. - templates_source = """ -__________________________________________________ - -docstring_prototype - -PyDoc_VAR({c_basename}__doc__); -__________________________________________________ - -docstring_definition - -PyDoc_STRVAR({c_basename}__doc__, -{docstring}); -__________________________________________________ - -impl_definition - -static {impl_return_type} -{c_basename}_impl({impl_parameters}) -__________________________________________________ - -parser_prototype_noargs - -static PyObject * -{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) -__________________________________________________ - -parser_prototype_meth_o - -# SLIGHT HACK -# METH_O uses {impl_parameters} for the parser! - -static PyObject * -{c_basename}({impl_parameters}) -__________________________________________________ - -parser_prototype_varargs - -static PyObject * -{c_basename}({self_type}{self_name}, PyObject *args) -__________________________________________________ - -parser_prototype_keyword - -static PyObject * -{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs) -__________________________________________________ - -parser_prototype_init - -static int -{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs) -__________________________________________________ - -parser_definition_simple_no_parsing - -{{ - return {c_basename}_impl({impl_arguments}); -}} -__________________________________________________ - -parser_definition_start - -{{ - {return_value_declaration} - {declarations} - {initializers} -{empty line} -__________________________________________________ - -parser_definition_end - - {return_conversion} - -{exit_label} - {cleanup} - return return_value; -}} -__________________________________________________ - -parser_definition_impl_call - - {modifications} - {return_value} = {c_basename}_impl({impl_arguments}); -__________________________________________________ - -parser_definition_unpack_tuple - - if (!PyArg_UnpackTuple(args, "{name}", - {unpack_min}, {unpack_max}, - {parse_arguments})) - goto exit; -__________________________________________________ - -parser_definition_parse_tuple - - if (!PyArg_ParseTuple(args, - "{format_units}:{name}", - {parse_arguments})) - goto exit; -__________________________________________________ - -parser_definition_option_groups - {option_group_parsing} - -__________________________________________________ - -parser_definition_parse_tuple_and_keywords - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "{format_units}:{name}", _keywords, - {parse_arguments})) - goto exit; -__________________________________________________ - -parser_definition_no_positional - - if ({self_type_check}!_PyArg_NoPositional("{name}", args)) - goto exit; - -__________________________________________________ - -parser_definition_no_keywords - - if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) - goto exit; - -__________________________________________________ - -methoddef_define - -#define {methoddef_name} \\ - {{"{name}", (PyCFunction){c_basename}, {methoddef_flags}, {c_basename}__doc__}}, -__________________________________________________ -""".rstrip() - - title = '' - buffer = [] - line = None - for line in templates_source.split('\n'): - line = line.rstrip() - if line.startswith('# '): - # comment - continue - if line.startswith("_____"): - if not buffer: - continue - assert title not in _templates, "defined template twice: " + repr(title) - buffer = '\n'.join(buffer).rstrip() - buffer = buffer.replace('{empty line}', '') - _templates[title] = buffer - buffer = [] - title = '' - continue - if not title: - if not line: - continue - title = line - continue - if not (line or buffer): - # throw away leading blank lines - continue - buffer.append(line) - - assert not title, 'ensure templates_source ends with ______ (still adding to ' + repr(title) + ")" - - del templates_source - del title - del buffer - del line - - # for name, value in _templates.items(): - # print(name + ":") - # pprint.pprint(value) - # print() - def output_templates(self, f): parameters = list(f.parameters.values()) assert parameters @@ -731,7 +593,7 @@ converters[0].format_unit == 'O' and not new_or_init) - # we have to set seven things before we're done: + # we have to set these things before we're done: # # docstring_prototype # docstring_definition @@ -740,33 +602,72 @@ # parser_prototype # parser_definition # impl_definition - - templates = self._templates + # cpp_if + # cpp_endif + # methoddef_ifndef return_value_declaration = "PyObject *return_value = NULL;" - methoddef_define = templates['methoddef_define'] + methoddef_define = normalize_snippet(""" + #define {methoddef_name} \\ + {{"{name}", (PyCFunction){c_basename}, {methoddef_flags}, {c_basename}__doc__}}, + """) if new_or_init and not f.docstring: docstring_prototype = docstring_definition = '' else: - docstring_prototype = templates['docstring_prototype'] - docstring_definition = templates['docstring_definition'] - impl_definition = templates['impl_definition'] + docstring_prototype = normalize_snippet(""" + PyDoc_VAR({c_basename}__doc__); + """) + docstring_definition = normalize_snippet(""" + PyDoc_STRVAR({c_basename}__doc__, + {docstring}); + """) + impl_definition = normalize_snippet(""" + static {impl_return_type} + {c_basename}_impl({impl_parameters}) + """) impl_prototype = parser_prototype = parser_definition = None + parser_prototype_keyword = normalize_snippet(""" + static PyObject * + {c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs) + """) + + parser_prototype_varargs = normalize_snippet(""" + static PyObject * + {c_basename}({self_type}{self_name}, PyObject *args) + """) + + # parser_body_fields remembers the fields passed in to the + # previous call to parser_body. this is used for an awful hack. parser_body_fields = () def parser_body(prototype, *fields): nonlocal parser_body_fields add, output = text_accumulator() add(prototype) parser_body_fields = fields + fields = list(fields) - fields.insert(0, 'parser_definition_start') - fields.append('parser_definition_impl_call') - fields.append('parser_definition_end') + fields.insert(0, normalize_snippet(""" + {{ + {return_value_declaration} + {declarations} + {initializers} + """) + "\n") + # just imagine--your code is here in the middle + fields.append(normalize_snippet(""" + {modifications} + {return_value} = {c_basename}_impl({impl_arguments}); + {return_conversion} + + {exit_label} + {cleanup} + return return_value; + }} + """)) for field in fields: add('\n') - add(templates[field]) + add(field) return output() def insert_keywords(s): @@ -777,26 +678,39 @@ flags = "METH_NOARGS" - parser_prototype = templates['parser_prototype_noargs'] + parser_prototype = normalize_snippet(""" + static PyObject * + {c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) + """) parser_definition = parser_prototype if default_return_converter: - parser_definition = parser_prototype + '\n' + templates['parser_definition_simple_no_parsing'] + parser_definition = parser_prototype + '\n' + normalize_snippet(""" + {{ + return {c_basename}_impl({impl_arguments}); + }} + """) else: parser_definition = parser_body(parser_prototype) elif meth_o: flags = "METH_O" - # impl_definition = templates['parser_prototype_meth_o'] + + meth_o_prototype = normalize_snippet(""" + static PyObject * + {c_basename}({impl_parameters}) + """) if default_return_converter: # maps perfectly to METH_O, doesn't need a return converter. # so we skip making a parse function # and call directly into the impl function. impl_prototype = parser_prototype = parser_definition = '' - impl_definition = templates['parser_prototype_meth_o'] + impl_definition = meth_o_prototype else: - parser_prototype = templates['parser_prototype_meth_o'] + # SLIGHT HACK + # use impl_parameters for the parser here! + parser_prototype = meth_o_prototype parser_definition = parser_body(parser_prototype) elif has_option_groups: @@ -805,9 +719,9 @@ # in a big switch statement) flags = "METH_VARARGS" - parser_prototype = templates['parser_prototype_varargs'] - - parser_definition = parser_body(parser_prototype, 'parser_definition_option_groups') + parser_prototype = parser_prototype_varargs + + parser_definition = parser_body(parser_prototype, ' {option_group_parsing}') elif positional and all_boring_objects: # positional-only, but no option groups, @@ -815,26 +729,47 @@ # PyArg_UnpackTuple! flags = "METH_VARARGS" - parser_prototype = templates['parser_prototype_varargs'] - - parser_definition = parser_body(parser_prototype, 'parser_definition_unpack_tuple') + parser_prototype = parser_prototype_varargs + + parser_definition = parser_body(parser_prototype, normalize_snippet(""" + if (!PyArg_UnpackTuple(args, "{name}", + {unpack_min}, {unpack_max}, + {parse_arguments})) + goto exit; + """, indent=4)) elif positional: # positional-only, but no option groups # we only need one call to PyArg_ParseTuple flags = "METH_VARARGS" - parser_prototype = templates['parser_prototype_varargs'] - - parser_definition = parser_body(parser_prototype, 'parser_definition_parse_tuple') + parser_prototype = parser_prototype_varargs + + parser_definition = parser_body(parser_prototype, normalize_snippet(""" + if (!PyArg_ParseTuple(args, + "{format_units}:{name}", + {parse_arguments})) + goto exit; + """, indent=4)) else: # positional-or-keyword arguments flags = "METH_VARARGS|METH_KEYWORDS" - parser_prototype = templates['parser_prototype_keyword'] - - parser_definition = parser_body(parser_prototype, 'parser_definition_parse_tuple_and_keywords') + parser_prototype = parser_prototype_keyword + + body = normalize_snippet(""" + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "{format_units}:{name}", _keywords, + {parse_arguments})) + goto exit; + """, indent=4) + parser_definition = parser_body(parser_prototype, normalize_snippet(""" + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "{format_units}:{name}", _keywords, + {parse_arguments})) + goto exit; + """, indent=4)) parser_definition = insert_keywords(parser_definition) @@ -842,10 +777,13 @@ methoddef_define = '' if f.kind == METHOD_NEW: - parser_prototype = templates['parser_prototype_keyword'] + parser_prototype = parser_prototype_keyword else: return_value_declaration = "int return_value = -1;" - parser_prototype = templates['parser_prototype_init'] + parser_prototype = normalize_snippet(""" + static int + {c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs) + """) fields = list(parser_body_fields) parses_positional = 'METH_NOARGS' not in flags @@ -854,9 +792,15 @@ assert parses_positional if not parses_keywords: - fields.insert(0, 'parser_definition_no_keywords') + fields.insert(0, normalize_snippet(""" + if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) + goto exit; + """, indent=4)) if not parses_positional: - fields.insert(0, 'parser_definition_no_positional') + fields.insert(0, normalize_snippet(""" + if ({self_type_check}!_PyArg_NoPositional("{name}", args)) + goto exit; + """, indent=4)) parser_definition = parser_body(parser_prototype, *fields) if parses_keywords: @@ -868,6 +812,22 @@ methoddef_define = methoddef_define.replace('{methoddef_flags}', flags) + methoddef_ifndef = '' + conditional = self.cpp.condition() + if not conditional: + cpp_if = cpp_endif = '' + else: + cpp_if = "#if " + conditional + cpp_endif = "#endif /* " + conditional + " */" + + if methoddef_define: + methoddef_ifndef = normalize_snippet(""" + #ifndef {methoddef_name} + #define {methoddef_name} + #endif /* !defined({methoddef_name}) */ + """) + + # add ';' to the end of parser_prototype and impl_prototype # (they mustn't be None, but they could be an empty string.) assert parser_prototype is not None @@ -890,6 +850,9 @@ "parser_prototype" : parser_prototype, "parser_definition" : parser_definition, "impl_definition" : impl_definition, + "cpp_if" : cpp_if, + "cpp_endif" : cpp_endif, + "methoddef_ifndef" : methoddef_ifndef, } # make sure we didn't forget to assign something, @@ -1007,9 +970,8 @@ add, output = text_accumulator() data = CRenderData() - parameters = list(f.parameters.values()) - assert parameters, "We should always have a 'self' at this point!" - + assert f.parameters, "We should always have a 'self' at this point!" + parameters = f.render_parameters converters = [p.converter for p in parameters] templates = self.output_templates(f) @@ -1289,7 +1251,9 @@ def _line(self): self.line_number += 1 - return self.input.pop() + line = self.input.pop() + self.language.parse_line(line) + return line def parse_verbatim_block(self): add, output = text_accumulator() @@ -1515,13 +1479,25 @@ # The callable should not call builtins.print. return_converters = {} +clinic = None class Clinic: presets_text = """ +preset block +everything block +docstring_prototype suppress +parser_prototype suppress +cpp_if suppress +cpp_endif suppress +methoddef_ifndef buffer + preset original everything block docstring_prototype suppress parser_prototype suppress +cpp_if suppress +cpp_endif suppress +methoddef_ifndef buffer preset file everything file @@ -1581,12 +1557,15 @@ d = self.destinations.get self.field_destinations = collections.OrderedDict(( + ('cpp_if', d('suppress')), ('docstring_prototype', d('suppress')), ('docstring_definition', d('block')), ('methoddef_define', d('block')), ('impl_prototype', d('block')), ('parser_prototype', d('suppress')), ('parser_definition', d('block')), + ('cpp_endif', d('suppress')), + ('methoddef_ifndef', d('buffer')), ('impl_definition', d('block')), )) @@ -1752,7 +1731,7 @@ fail("Can't extract file type for file " + repr(filename)) try: - language = extensions[extension]() + language = extensions[extension](filename) except KeyError: fail("Can't identify file type for file " + repr(filename)) @@ -1934,6 +1913,19 @@ self.self_converter = None self.suppress_signature = suppress_signature + self.rendered_parameters = None + + __render_parameters__ = None + @property + def render_parameters(self): + if not self.__render_parameters__: + self.__render_parameters__ = l = [] + for p in self.parameters.values(): + p = p.copy() + p.converter.pre_render() + l.append(p) + return self.__render_parameters__ + @property def methoddef_flags(self): if self.kind in (METHOD_INIT, METHOD_NEW): @@ -1952,6 +1944,25 @@ def __repr__(self): return '' + def copy(self, **overrides): + kwargs = { + 'name': self.name, 'module': self.module, 'parameters': self.parameters, + 'cls': self.cls, 'c_basename': self.c_basename, + 'full_name': self.full_name, + 'return_converter': self.return_converter, 'return_annotation': self.return_annotation, + 'docstring': self.docstring, 'kind': self.kind, 'coexist': self.coexist, + 'suppress_signature': self.suppress_signature, + } + kwargs.update(overrides) + f = Function(**kwargs) + + parameters = collections.OrderedDict() + for name, value in f.parameters.items(): + value = value.copy(function=f) + parameters[name] = value + f.parameters = parameters + return f + class Parameter: """ @@ -1976,6 +1987,34 @@ def is_keyword_only(self): return self.kind == inspect.Parameter.KEYWORD_ONLY + def copy(self, **overrides): + kwargs = { + 'name': self.name, 'kind': self.kind, 'default':self.default, + 'function': self.function, 'converter': self.converter, 'annotation': self.annotation, + 'docstring': self.docstring, 'group': self.group, + } + kwargs.update(overrides) + if 'converter' not in overrides: + converter = copy.copy(self.converter) + converter.function = kwargs['function'] + kwargs['converter'] = converter + return Parameter(**kwargs) + + + +class LandMine: + # try to access any + def __init__(self, message): + self.__message__ = message + + def __repr__(self): + return '" + + def __getattribute__(self, name): + if name in ('__repr__', '__message__'): + return super().__getattribute__(name) + # raise RuntimeError(repr(name)) + fail("Stepped on a land mine, trying to access attribute " + repr(name) + ":\n" + self.__message__) def add_c_converter(f, name=None): @@ -1994,6 +2033,8 @@ if ((cls.format_unit != 'O&') and (cls.format_unit not in legacy_converters)): legacy_converters[cls.format_unit] = cls + if cls.format_unit: + legacy_converters[cls.format_unit] = cls return cls def add_legacy_c_converter(format_unit, **kwargs): @@ -2005,7 +2046,8 @@ added_f = f else: added_f = functools.partial(f, **kwargs) - legacy_converters[format_unit] = added_f + if format_unit: + legacy_converters[format_unit] = added_f return f return closure @@ -2021,6 +2063,12 @@ parameters must be keyword-only. """ + # The C name to use for this variable. + name = None + + # The Python name to use for this variable. + py_name = None + # The C type to use for this variable. # 'type' should be a Python string specifying the type, e.g. "int". # If this is a pointer type, the type string should end with ' *'. @@ -2109,9 +2157,9 @@ signature_name = None # keep in sync with self_converter.__init__! - def __init__(self, name, function, default=unspecified, *, c_default=None, py_default=None, annotation=unspecified, **kwargs): - self.function = function + def __init__(self, name, py_name, function, default=unspecified, *, c_default=None, py_default=None, annotation=unspecified, **kwargs): self.name = name + self.py_name = py_name if default is not unspecified: if self.default_type and not isinstance(default, (self.default_type, Unknown)): @@ -2130,7 +2178,14 @@ if annotation != unspecified: fail("The 'annotation' parameter is not currently permitted.") + + # this is deliberate, to prevent you from caching information + # about the function in the init. + # (that breaks if we get cloned.) + # so after this change we will noisily fail. + self.function = LandMine("Don't access members of self.function inside converter_init!") self.converter_init(**kwargs) + self.function = function def converter_init(self): pass @@ -2174,7 +2229,7 @@ data.modifications.append('/* modifications for ' + name + ' */\n' + modifications.rstrip()) # keywords - data.keywords.append(original_name) + data.keywords.append(parameter.name) # format_units if self.is_optional() and '|' not in data.format_units: @@ -2291,6 +2346,14 @@ """ return "" + def pre_render(self): + """ + A second initialization function, like converter_init, + called just before rendering. + You are permitted to examine self.function here. + """ + pass + class bool_converter(CConverter): type = 'int' @@ -2609,12 +2672,14 @@ type = None format_unit = '' - def converter_init(self, *, type=None): + self.specified_type = type + + def pre_render(self): f = self.function default_type, default_name = correct_name_for_self(f) self.signature_name = default_name - self.type = type or self.type or default_type + self.type = self.specified_type or self.type or default_type kind = self.function.kind new_or_init = kind in (METHOD_NEW, METHOD_INIT) @@ -3053,7 +3118,7 @@ return if field not in fd: - fail("Invalid field " + repr(field) + ", must be one of:\n " + ", ".join(valid_fields)) + fail("Invalid field " + repr(field) + ", must be one of:\n preset push pop print everything " + " ".join(fd)) fd[field] = d def directive_dump(self, name): @@ -3132,6 +3197,18 @@ # self.block = self.ClinicOutputBlock(self) if self.ignore_line(line): return + + # is it a directive? + fields = shlex.split(line) + directive_name = fields[0] + directive = self.directives.get(directive_name, None) + if directive: + try: + directive(*fields[1:]) + except TypeError as e: + fail(str(e)) + return + self.next(self.state_modulename_name, line) def state_modulename_name(self, line): @@ -3156,17 +3233,6 @@ self.indent.infer(line) - # is it a directive? - fields = shlex.split(line) - directive_name = fields[0] - directive = self.directives.get(directive_name, None) - if directive: - try: - directive(*fields[1:]) - except TypeError as e: - fail(str(e)) - return - # are we cloning? before, equals, existing = line.rpartition('=') if equals: @@ -3188,7 +3254,7 @@ else: existing_function = None if not existing_function: - print("class", cls, "module", module, "exsiting", existing) + print("class", cls, "module", module, "existing", existing) print("cls. functions", cls.functions) fail("Couldn't find existing function " + repr(existing) + "!") @@ -3198,10 +3264,7 @@ if not (existing_function.kind == self.kind and existing_function.coexist == self.coexist): fail("'kind' of function and cloned function don't match! (@classmethod/@staticmethod/@coexist)") - self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename, - return_converter=existing_function.return_converter, kind=existing_function.kind, coexist=existing_function.coexist) - - self.function.parameters = existing_function.parameters.copy() + self.function = existing_function.copy(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename, docstring='') self.block.signatures.append(self.function) (cls or module).functions.append(self.function) @@ -3272,7 +3335,7 @@ kwargs = {} if cls and type == "PyObject *": kwargs['type'] = cls.typedef - sc = self.function.self_converter = self_converter(name, self.function, **kwargs) + sc = self.function.self_converter = self_converter(name, name, self.function, **kwargs) p_self = Parameter(sc.name, inspect.Parameter.POSITIONAL_ONLY, function=self.function, converter=sc) self.function.parameters[sc.name] = p_self @@ -3411,6 +3474,22 @@ else: fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".a)") + # handle "as" for parameters too + c_name = None + name, have_as_token, trailing = line.partition(' as ') + if have_as_token: + name = name.strip() + if ' ' not in name: + fields = trailing.strip().split(' ') + if not fields: + fail("Invalid 'as' clause!") + c_name = fields[0] + if c_name.endswith(':'): + name += ':' + c_name = c_name[:-1] + fields[0] = name + line = ' '.join(fields) + base, equals, default = line.rpartition('=') if not equals: base = default @@ -3559,7 +3638,9 @@ legacy_str = "legacy " if legacy else "" if name not in dict: fail('{} is not a valid {}converter'.format(name, legacy_str)) - converter = dict[name](parameter_name, self.function, value, **kwargs) + # if you use a c_name for the parameter, we just give that name to the converter + # but the parameter object gets the python name + converter = dict[name](c_name or parameter_name, parameter_name, self.function, value, **kwargs) kind = inspect.Parameter.KEYWORD_ONLY if self.keyword_only else inspect.Parameter.POSITIONAL_OR_KEYWORD @@ -3703,7 +3784,7 @@ return f.docstring add, output = text_accumulator() - parameters = list(f.parameters.values()) + parameters = f.render_parameters ## ## docstring first line @@ -3772,15 +3853,20 @@ name = p.converter.signature_name or p.name a = [] - if isinstance(p.converter, self_converter) and not f.suppress_signature: - # annotate first parameter as being a "self". - # - # if inspect.Signature gets this function, and it's already bound, - # the self parameter will be stripped off. - # - # if it's not bound, it should be marked as positional-only. - a.append('$') - a.append(name) + if isinstance(p.converter, self_converter): + if f.suppress_signature: + continue + else: + # annotate first parameter as being a "self". + # + # if inspect.Signature gets this function, and it's already bound, + # the self parameter will be stripped off. + # + # if it's not bound, it should be marked as positional-only. + a.append('$') + a.append(name) + else: + a.append(name) if p.converter.is_optional(): a.append('=') value = p.converter.py_default diff --git a/Tools/clinic/clinic_test.py b/Tools/clinic/clinic_test.py --- a/Tools/clinic/clinic_test.py +++ b/Tools/clinic/clinic_test.py @@ -13,6 +13,7 @@ import unittest from unittest import TestCase + class FakeConverter: def __init__(self, name, args): self.name = name @@ -41,10 +42,11 @@ def __init__(self): self.converters = FakeConvertersDict() self.legacy_converters = FakeConvertersDict() - self.language = clinic.CLanguage() + self.language = clinic.CLanguage(None) self.filename = None self.block_parser = clinic.BlockParser('', self.language) self.modules = collections.OrderedDict() + self.classes = collections.OrderedDict() clinic.clinic = self self.name = "FakeClinic" self.line_prefix = self.line_suffix = '' @@ -92,7 +94,7 @@ # so it woudl spit out an end line for you. # and since you really already had one, # the last line of the block got corrupted. - c = clinic.Clinic(clinic.CLanguage()) + c = clinic.Clinic(clinic.CLanguage(None)) raw = "/*[clinic]\nfoo\n[clinic]*/" cooked = c.parse(raw).splitlines() end_line = cooked[2].rstrip() @@ -220,7 +222,7 @@ class ClinicBlockParserTest(TestCase): def _test(self, input, output): - language = clinic.CLanguage() + language = clinic.CLanguage(None) blocks = list(clinic.BlockParser(input, language)) writer = clinic.BlockPrinter(language) @@ -250,7 +252,7 @@ """) def _test_clinic(self, input, output): - language = clinic.CLanguage() + language = clinic.CLanguage(None) c = clinic.Clinic(language) c.parsers['inert'] = InertParser(c) c.parsers['copy'] = CopyParser(c) @@ -265,7 +267,7 @@ def [copy start generated code]*/ abc -/*[copy end generated code: checksum=03cfd743661f07975fa2f1220c5194cbaff48451]*/ +/*[copy end generated code: output=03cfd743661f0797 input=7b18d017f89f61cf]*/ xyz """, """ verbatim text here @@ -274,7 +276,7 @@ def [copy start generated code]*/ def -/*[copy end generated code: checksum=7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/ +/*[copy end generated code: output=7b18d017f89f61cf input=7b18d017f89f61cf]*/ xyz """) @@ -297,7 +299,7 @@ def test_param(self): function = self.parse_function("module os\nos.access\n path: int") self.assertEqual("access", function.name) - self.assertEqual(1, len(function.parameters)) + self.assertEqual(2, len(function.parameters)) p = function.parameters['path'] self.assertEqual('path', p.name) self.assertIsInstance(p.converter, clinic.int_converter) @@ -326,10 +328,21 @@ module os os.access follow_symlinks: bool = True + something_else: str = ''""") + p = function.parameters['follow_symlinks'] + self.assertEqual(3, len(function.parameters)) + self.assertIsInstance(function.parameters['something_else'].converter, clinic.str_converter) + + def test_param_default_parameters_out_of_order(self): + s = self.parse_function_should_fail(""" +module os +os.access + follow_symlinks: bool = True something_else: str""") - p = function.parameters['follow_symlinks'] - self.assertEqual(2, len(function.parameters)) - self.assertIsInstance(function.parameters['something_else'].converter, clinic.str_converter) + self.assertEqual(s, """Error on line 0: +Can't have a parameter without a default ('something_else') +after a parameter with a default! +""") def disabled_test_converter_arguments(self): function = self.parse_function("module os\nos.access\n path: path_t(allow_fd=1)") @@ -346,7 +359,7 @@ Perform a stat system call on the given path.""") self.assertEqual(""" -stat(path) +sig=($module, path) Perform a stat system call on the given path. path @@ -366,7 +379,7 @@ Okay, we're done here. """) self.assertEqual(""" -bar(x, y) +sig=($module, x, y) This is the documentation for foo. x @@ -382,7 +395,7 @@ path: str This/used to break Clinic! """) - self.assertEqual("os.stat(path)\n\nThis/used to break Clinic!", function.docstring) + self.assertEqual("sig=($module, path)\n\nThis/used to break Clinic!", function.docstring) def test_c_name(self): function = self.parse_function("module os\nos.stat as os_stat_fn") @@ -538,7 +551,7 @@ """) self.assertEqual(s, ('Error on line 0:\n' - 'Function two_top_groups_on_left has an unsupported group configuration. (Unexpected state 2)\n')) + 'Function two_top_groups_on_left has an unsupported group configuration. (Unexpected state 2.b)\n')) def test_disallowed_grouping__two_top_groups_on_right(self): self.parse_function_should_fail(""" @@ -611,8 +624,8 @@ Docstring """) - self.assertEqual("bar()\nDocstring", function.docstring) - self.assertEqual(0, len(function.parameters)) + self.assertEqual("sig=($module)\nDocstring", function.docstring) + self.assertEqual(1, len(function.parameters)) # self! def test_illegal_module_line(self): self.parse_function_should_fail(""" @@ -706,7 +719,7 @@ Not at column 0! """) self.assertEqual(""" -bar(x, *, y) +sig=($module, x, *, y) Not at column 0! x @@ -720,7 +733,7 @@ path: str This/used to break Clinic! """) - self.assertEqual("stat(path)\nThis/used to break Clinic!", function.docstring) + self.assertEqual("sig=($module, path)\nThis/used to break Clinic!", function.docstring) def test_directive(self): c = FakeClinic() diff --git a/Tools/clinic/cpp.py b/Tools/clinic/cpp.py new file mode 100644 --- /dev/null +++ b/Tools/clinic/cpp.py @@ -0,0 +1,191 @@ +import re +import sys + +def negate(condition): + """ + Returns a CPP conditional that is the opposite of the conditional passed in. + """ + if condition.startswith('!'): + return condition[1:] + return "!" + condition + +class Monitor: + """ + A simple C preprocessor that scans C source and computes, line by line, + what the current C preprocessor #if state is. + + Doesn't handle everything--for example, if you have /* inside a C string, + without a matching */ (also inside a C string), or with a */ inside a C + string but on another line and with preprocessor macros in between... + the parser will get lost. + + Anyway this implementation seems to work well enough for the CPython sources. + """ + + is_a_simple_defined = re.compile(r'^defined\s*\(\s*[A-Za-z0-9_]+\s*\)$').match + + def __init__(self, filename=None, *, verbose=False): + self.stack = [] + self.in_comment = False + self.continuation = None + self.line_number = 0 + self.filename = filename + self.verbose = verbose + + def __repr__(self): + return ''.join(( + '")) + + def status(self): + return str(self.line_number).rjust(4) + ": " + self.condition() + + def condition(self): + """ + Returns the current preprocessor state, as a single #if condition. + """ + return " && ".join(condition for token, condition in self.stack) + + def fail(self, *a): + if self.filename: + filename = " " + self.filename + else: + filename = '' + print("Error at" + filename, "line", self.line_number, ":") + print(" ", ' '.join(str(x) for x in a)) + sys.exit(-1) + + def close(self): + if self.stack: + self.fail("Ended file while still in a preprocessor conditional block!") + + def write(self, s): + for line in s.split("\n"): + self.writeline(line) + + def writeline(self, line): + self.line_number += 1 + line = line.strip() + + def pop_stack(): + if not self.stack: + self.fail("#" + token + " without matching #if / #ifdef / #ifndef!") + return self.stack.pop() + + if self.continuation: + line = self.continuation + line + self.continuation = None + + if not line: + return + + if line.endswith('\\'): + self.continuation = line[:-1].rstrip() + " " + return + + # we have to ignore preprocessor commands inside comments + # + # we also have to handle this: + # /* start + # ... + # */ /* <-- tricky! + # ... + # */ + # and this: + # /* start + # ... + # */ /* also tricky! */ + if self.in_comment: + if '*/' in line: + # snip out the comment and continue + # + # GCC allows + # /* comment + # */ #include + # maybe other compilers too? + _, _, line = line.partition('*/') + self.in_comment = False + + while True: + if '/*' in line: + if self.in_comment: + self.fail("Nested block comment!") + + before, _, remainder = line.partition('/*') + comment, comment_ends, after = remainder.partition('*/') + if comment_ends: + # snip out the comment + line = before.rstrip() + ' ' + after.lstrip() + continue + # comment continues to eol + self.in_comment = True + line = before.rstrip() + break + + # we actually have some // comments + # (but block comments take precedence) + before, line_comment, comment = line.partition('//') + if line_comment: + line = before.rstrip() + + if not line.startswith('#'): + return + + line = line[1:].lstrip() + assert line + + fields = line.split() + token = fields[0].lower() + condition = ' '.join(fields[1:]).strip() + + if_tokens = {'if', 'ifdef', 'ifndef'} + all_tokens = if_tokens | {'elif', 'else', 'endif'} + + if token not in all_tokens: + return + + # cheat a little here, to reuse the implementation of if + if token == 'elif': + pop_stack() + token = 'if' + + if token in if_tokens: + if not condition: + self.fail("Invalid format for #" + token + " line: no argument!") + if token == 'if': + if not self.is_a_simple_defined(condition): + condition = "(" + condition + ")" + else: + fields = condition.split() + if len(fields) != 1: + self.fail("Invalid format for #" + token + " line: should be exactly one argument!") + symbol = fields[0] + condition = 'defined(' + symbol + ')' + if token == 'ifndef': + condition = '!' + condition + + self.stack.append(("if", condition)) + if self.verbose: + print(self.status()) + return + + previous_token, previous_condition = pop_stack() + + if token == 'else': + self.stack.append(('else', negate(previous_condition))) + elif token == 'endif': + pass + if self.verbose: + print(self.status()) + +if __name__ == '__main__': + for filename in sys.argv[1:]: + with open(filename, "rt") as f: + cpp = Monitor(filename, verbose=True) + print() + print(filename) + for line_number, line in enumerate(f.read().split('\n'), 1): + cpp.writeline(line) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Feb 1 09:44:41 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 01 Feb 2014 09:44:41 +0100 Subject: [Python-checkins] Daily reference leaks (a3b4f4b1aedd): sum=0 Message-ID: results for a3b4f4b1aedd on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogtKgKC2', '-x'] From python-checkins at python.org Sat Feb 1 17:32:53 2014 From: python-checkins at python.org (mark.dickinson) Date: Sat, 1 Feb 2014 17:32:53 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5Njgz?= =?utf-8?q?=3A_Add_=5F=5Fclosure=5F=5F_and_other_missing_attributes_to_fun?= =?utf-8?q?ction_docs=2E?= Message-ID: <3fGgnx11BNz7Lk8@mail.python.org> http://hg.python.org/cpython/rev/fed468670866 changeset: 88883:fed468670866 branch: 2.7 parent: 88859:ea1f2f1f84a7 user: Mark Dickinson date: Sat Feb 01 16:32:40 2014 +0000 summary: Issue #19683: Add __closure__ and other missing attributes to function docs. files: Doc/reference/datamodel.rst | 54 +++++++++++++----------- 1 files changed, 30 insertions(+), 24 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -486,44 +486,39 @@ +-----------------------+-------------------------------+-----------+ | Attribute | Meaning | | +=======================+===============================+===========+ - | :attr:`func_doc` | The function's documentation | Writable | - | | string, or ``None`` if | | - | | unavailable | | + | :attr:`__doc__` | The function's documentation | Writable | + | :attr:`func_doc` | string, or ``None`` if | | + | | unavailable. | | +-----------------------+-------------------------------+-----------+ - | :attr:`__doc__` | Another way of spelling | Writable | - | | :attr:`func_doc` | | - +-----------------------+-------------------------------+-----------+ - | :attr:`func_name` | The function's name | Writable | - +-----------------------+-------------------------------+-----------+ - | :attr:`__name__` | Another way of spelling | Writable | - | | :attr:`func_name` | | + | :attr:`__name__` | The function's name. | Writable | + | :attr:`func_name` | | | +-----------------------+-------------------------------+-----------+ | :attr:`__module__` | The name of the module the | Writable | | | function was defined in, or | | | | ``None`` if unavailable. | | +-----------------------+-------------------------------+-----------+ - | :attr:`func_defaults` | A tuple containing default | Writable | - | | argument values for those | | + | :attr:`__defaults__` | A tuple containing default | Writable | + | :attr:`func_defaults` | argument values for those | | | | arguments that have defaults, | | | | or ``None`` if no arguments | | - | | have a default value | | + | | have a default value. | | +-----------------------+-------------------------------+-----------+ - | :attr:`func_code` | The code object representing | Writable | - | | the compiled function body. | | + | :attr:`__code__` | The code object representing | Writable | + | :attr:`func_code` | the compiled function body. | | +-----------------------+-------------------------------+-----------+ - | :attr:`func_globals` | A reference to the dictionary | Read-only | - | | that holds the function's | | + | :attr:`__globals__` | A reference to the dictionary | Read-only | + | :attr:`func_globals` | that holds the function's | | | | global variables --- the | | | | global namespace of the | | | | module in which the function | | | | was defined. | | +-----------------------+-------------------------------+-----------+ - | :attr:`func_dict` | The namespace supporting | Writable | - | | arbitrary function | | + | :attr:`__dict__` | The namespace supporting | Writable | + | :attr:`func_dict` | arbitrary function | | | | attributes. | | +-----------------------+-------------------------------+-----------+ - | :attr:`func_closure` | ``None`` or a tuple of cells | Read-only | - | | that contain bindings for the | | + | :attr:`__closure__` | ``None`` or a tuple of cells | Read-only | + | :attr:`func_closure` | that contain bindings for the | | | | function's free variables. | | +-----------------------+-------------------------------+-----------+ @@ -532,6 +527,12 @@ .. versionchanged:: 2.4 ``func_name`` is now writable. + .. versionchanged:: 2.6 + The double-underscore attributes ``__closure__``, ``__code__``, + ``__defaults__``, and ``__globals__`` were introduced as aliases for + the corresponding ``func_*`` attributes for forwards compatibility + with Python 3. + Function objects also support getting and setting arbitrary attributes, which can be used, for example, to attach metadata to functions. Regular attribute dot-notation is used to get and set such attributes. *Note that the current @@ -542,16 +543,21 @@ code object; see the description of internal types below. .. index:: - single: func_doc (function attribute) single: __doc__ (function attribute) single: __name__ (function attribute) single: __module__ (function attribute) single: __dict__ (function attribute) + single: __defaults__ (function attribute) + single: __code__ (function attribute) + single: __globals__ (function attribute) + single: __closure__ (function attribute) + single: func_doc (function attribute) + single: func_name (function attribute) + single: func_dict (function attribute) single: func_defaults (function attribute) - single: func_closure (function attribute) single: func_code (function attribute) single: func_globals (function attribute) - single: func_dict (function attribute) + single: func_closure (function attribute) pair: global; namespace User-defined methods -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 18:27:18 2014 From: python-checkins at python.org (r.david.murray) Date: Sat, 1 Feb 2014 18:27:18 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_whatsnew=3A_move_of_reload?= =?utf-8?q?=2C_update_new_windows-only_ssl_functions_entry=2E?= Message-ID: <3fGj0k1zTdz7Llp@mail.python.org> http://hg.python.org/cpython/rev/b3f034f5000f changeset: 88884:b3f034f5000f parent: 88882:19d81cc213d7 user: R David Murray date: Sat Feb 01 12:27:07 2014 -0500 summary: whatsnew: move of reload, update new windows-only ssl functions entry. files: Doc/whatsnew/3.4.rst | 10 ++++++++-- 1 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -765,6 +765,10 @@ it will normally be desirable to override the default implementation for performance reasons. (Contributed by Brett Cannon in :issue:`18072`.) +The :func:`~importlib.reload` function has been moved from :mod:`imp` +to :mod:`importlib`. The :func:`mod.reload` name is retained for +backward compatibility, but is deprecated. + inspect ------- @@ -1062,8 +1066,10 @@ list of the loaded ``CA`` certificates. (Contributed by Christian Heimes in and :issue:`18147`.) -Add :func:`ssl.enum_cert_store` to retrieve certificates and CRL from Windows' -cert store. (Contributed by Christian Heimes in :issue:`17134`.) +Two new windows-only functions, :func:`~ssl.enum_certificates` and +:func:`~ssl.enum_crls` provide the ability to retrieve certificates, +certificate information, and CRLs from the Windows cert store. (Contributed +by Christian Heimes in :issue:`17134`.) Support for server-side SNI using the new :meth:`ssl.SSLContext.set_servername_callback` method. -- Repository URL: http://hg.python.org/cpython From brett at python.org Sat Feb 1 19:20:48 2014 From: brett at python.org (Brett Cannon) Date: Sat, 1 Feb 2014 13:20:48 -0500 Subject: [Python-checkins] cpython: whatsnew: move of reload, update new windows-only ssl functions entry. In-Reply-To: <3fGj0k1zTdz7Llp@mail.python.org> References: <3fGj0k1zTdz7Llp@mail.python.org> Message-ID: On Sat, Feb 1, 2014 at 12:27 PM, r.david.murray wrote: > http://hg.python.org/cpython/rev/b3f034f5000f > changeset: 88884:b3f034f5000f > parent: 88882:19d81cc213d7 > user: R David Murray > date: Sat Feb 01 12:27:07 2014 -0500 > summary: > whatsnew: move of reload, update new windows-only ssl functions entry. > > files: > Doc/whatsnew/3.4.rst | 10 ++++++++-- > 1 files changed, 8 insertions(+), 2 deletions(-) > > > diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst > --- a/Doc/whatsnew/3.4.rst > +++ b/Doc/whatsnew/3.4.rst > @@ -765,6 +765,10 @@ > it will normally be desirable to override the default implementation > for performance reasons. (Contributed by Brett Cannon in :issue:`18072`.) > > +The :func:`~importlib.reload` function has been moved from :mod:`imp` > +to :mod:`importlib`. The :func:`mod.reload` name is retained for > +backward compatibility, but is deprecated. > + > That wording seems confusing to me. It makes it seem like importlib.reload is deprecated when in fact it's the imp module itself. -Brett > > inspect > ------- > @@ -1062,8 +1066,10 @@ > list of the loaded ``CA`` certificates. (Contributed by Christian Heimes > in > and :issue:`18147`.) > > -Add :func:`ssl.enum_cert_store` to retrieve certificates and CRL from > Windows' > -cert store. (Contributed by Christian Heimes in :issue:`17134`.) > +Two new windows-only functions, :func:`~ssl.enum_certificates` and > +:func:`~ssl.enum_crls` provide the ability to retrieve certificates, > +certificate information, and CRLs from the Windows cert store. > (Contributed > +by Christian Heimes in :issue:`17134`.) > > Support for server-side SNI using the new > :meth:`ssl.SSLContext.set_servername_callback` method. > > -- > Repository URL: http://hg.python.org/cpython > > _______________________________________________ > Python-checkins mailing list > Python-checkins at python.org > https://mail.python.org/mailman/listinfo/python-checkins > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From python-checkins at python.org Sat Feb 1 19:49:48 2014 From: python-checkins at python.org (yury.selivanov) Date: Sat, 1 Feb 2014 19:49:48 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_inspect=2Etests=3A_Fix_tes?= =?utf-8?q?ts_to_work_on_python_built_with_=27--without-doc-strings=27?= Message-ID: <3fGkqw2nxrz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/b1f214165471 changeset: 88885:b1f214165471 user: Yury Selivanov date: Sat Feb 01 13:49:29 2014 -0500 summary: inspect.tests: Fix tests to work on python built with '--without-doc-strings' #20471 files: Lib/test/test_inspect.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) 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 @@ -2193,6 +2193,9 @@ ('bar', 2, ..., "keyword_only")), ...)) + @unittest.skipIf(MISSING_C_DOCSTRINGS, + "Signature information for builtins requires docstrings") + def test_signature_on_class_without_init(self): # Test classes without user-defined __init__ or __new__ class C: pass self.assertEqual(str(inspect.signature(C)), '()') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 20:23:12 2014 From: python-checkins at python.org (ezio.melotti) Date: Sat, 1 Feb 2014 20:23:12 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzIwMjg4OiBmaXgg?= =?utf-8?q?handling_of_invalid_numeric_charrefs_in_HTMLParser=2E?= Message-ID: <3fGlZS3QsPz7LjV@mail.python.org> http://hg.python.org/cpython/rev/0d50b5851f38 changeset: 88886:0d50b5851f38 branch: 2.7 parent: 88883:fed468670866 user: Ezio Melotti date: Sat Feb 01 21:20:22 2014 +0200 summary: #20288: fix handling of invalid numeric charrefs in HTMLParser. files: Lib/HTMLParser.py | 6 +++--- Lib/test/test_htmlparser.py | 6 ++++++ Misc/NEWS | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Lib/HTMLParser.py b/Lib/HTMLParser.py --- a/Lib/HTMLParser.py +++ b/Lib/HTMLParser.py @@ -195,9 +195,9 @@ i = self.updatepos(i, k) continue else: - if ";" in rawdata[i:]: #bail by consuming &# - self.handle_data(rawdata[0:2]) - i = self.updatepos(i, 2) + if ";" in rawdata[i:]: # bail by consuming '&#' + self.handle_data(rawdata[i:i+2]) + i = self.updatepos(i, i+2) break elif startswith('&', i): match = entityref.match(rawdata, i) diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -394,6 +394,12 @@ ("data", "&#bad;"), ("endtag", "p"), ]) + # add the [] as a workaround to avoid buffering (see #20288) + self._run_check(["
&#bad;
"], [ + ("starttag", "div", []), + ("data", "&#bad;"), + ("endtag", "div"), + ]) def test_unescape_function(self): parser = HTMLParser.HTMLParser() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -38,6 +38,8 @@ Library ------- +- Issue #20288: fix handling of invalid numeric charrefs in HTMLParser. + - Issue #19456: ntpath.join() now joins relative paths correctly when a drive is present. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 20:23:13 2014 From: python-checkins at python.org (ezio.melotti) Date: Sat, 1 Feb 2014 20:23:13 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzIwMjg4OiBmaXgg?= =?utf-8?q?handling_of_invalid_numeric_charrefs_in_HTMLParser=2E?= Message-ID: <3fGlZT5HkYz7Ljg@mail.python.org> http://hg.python.org/cpython/rev/32097f193892 changeset: 88887:32097f193892 branch: 3.3 parent: 88862:7a611b7aae38 user: Ezio Melotti date: Sat Feb 01 21:21:01 2014 +0200 summary: #20288: fix handling of invalid numeric charrefs in HTMLParser. files: Lib/html/parser.py | 6 +++--- Lib/test/test_htmlparser.py | 6 ++++++ Misc/NEWS | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Lib/html/parser.py b/Lib/html/parser.py --- a/Lib/html/parser.py +++ b/Lib/html/parser.py @@ -228,9 +228,9 @@ i = self.updatepos(i, k) continue else: - if ";" in rawdata[i:]: #bail by consuming &# - self.handle_data(rawdata[0:2]) - i = self.updatepos(i, 2) + if ";" in rawdata[i:]: # bail by consuming &# + self.handle_data(rawdata[i:i+2]) + i = self.updatepos(i, i+2) break elif startswith('&', i): match = entityref.match(rawdata, i) diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -151,6 +151,12 @@ ("data", "&#bad;"), ("endtag", "p"), ]) + # add the [] as a workaround to avoid buffering (see #20288) + self._run_check(["
&#bad;
"], [ + ("starttag", "div", []), + ("data", "&#bad;"), + ("endtag", "div"), + ]) def test_unclosed_entityref(self): self._run_check("&entityref foo", [ diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,8 @@ Library ------- +- Issue #20288: fix handling of invalid numeric charrefs in HTMLParser. + - Issue #20424: Python implementation of io.StringIO now supports lone surrogates. - Issue #19456: ntpath.join() now joins relative paths correctly when a drive -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 20:23:15 2014 From: python-checkins at python.org (ezio.melotti) Date: Sat, 1 Feb 2014 20:23:15 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogIzIwMjg4OiBtZXJnZSB3aXRoIDMuMy4=?= Message-ID: <3fGlZW00wkz7Ljk@mail.python.org> http://hg.python.org/cpython/rev/92b3928bfde1 changeset: 88888:92b3928bfde1 parent: 88885:b1f214165471 parent: 88887:32097f193892 user: Ezio Melotti date: Sat Feb 01 21:22:26 2014 +0200 summary: #20288: merge with 3.3. files: Lib/html/parser.py | 6 +++--- Lib/test/test_htmlparser.py | 6 ++++++ Misc/NEWS | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Lib/html/parser.py b/Lib/html/parser.py --- a/Lib/html/parser.py +++ b/Lib/html/parser.py @@ -264,9 +264,9 @@ i = self.updatepos(i, k) continue else: - if ";" in rawdata[i:]: #bail by consuming &# - self.handle_data(rawdata[0:2]) - i = self.updatepos(i, 2) + if ";" in rawdata[i:]: # bail by consuming &# + self.handle_data(rawdata[i:i+2]) + i = self.updatepos(i, i+2) break elif startswith('&', i): match = entityref.match(rawdata, i) diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -167,6 +167,12 @@ ("data", "&#bad;"), ("endtag", "p"), ]) + # add the [] as a workaround to avoid buffering (see #20288) + self._run_check(["
&#bad;
"], [ + ("starttag", "div", []), + ("data", "&#bad;"), + ("endtag", "div"), + ]) def test_unclosed_entityref(self): self._run_check("&entityref foo", [ diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -41,6 +41,8 @@ ValueError instead of assert for forbidden subprocess_{shell,exec} arguments. (More to follow -- a convenience API for subprocesses.) +- Issue #20288: fix handling of invalid numeric charrefs in HTMLParser. + - Issue #20424: Python implementation of io.StringIO now supports lone surrogates. - Issue #20308: inspect.signature now works on classes without user-defined -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 1 22:54:38 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 1 Feb 2014 22:54:38 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320400=3A_Merge_Tu?= =?utf-8?q?lip_into_Python=3A_add_the_new_asyncio=2Esubprocess_module?= Message-ID: <3fGpxB4bX4z7Ljm@mail.python.org> http://hg.python.org/cpython/rev/d7ac90c0463a changeset: 88889:d7ac90c0463a user: Victor Stinner date: Sat Feb 01 22:49:59 2014 +0100 summary: Issue #20400: Merge Tulip into Python: add the new asyncio.subprocess module * Add a new asyncio.subprocess module * Add new create_subprocess_exec() and create_subprocess_shell() functions * The new asyncio.subprocess.SubprocessStreamProtocol creates stream readers for stdout and stderr and a stream writer for stdin. * The new asyncio.subprocess.Process class offers an API close to the subprocess.Popen class: - pid, returncode, stdin, stdout and stderr attributes - communicate(), wait(), send_signal(), terminate() and kill() methods * Remove STDIN (0), STDOUT (1) and STDERR (2) constants from base_subprocess and unix_events, to not be confused with the symbols with the same name of subprocess and asyncio.subprocess modules * _ProactorBasePipeTransport.get_write_buffer_size() now counts also the size of the pending write * _ProactorBaseWritePipeTransport._loop_writing() may now pause the protocol if the write buffer size is greater than the high water mark (64 KB by default) files: Lib/asyncio/__init__.py | 2 + Lib/asyncio/base_subprocess.py | 23 +- Lib/asyncio/proactor_events.py | 34 +- Lib/asyncio/subprocess.py | 197 ++++++++++ Lib/asyncio/unix_events.py | 7 +- Lib/test/test_asyncio/test_base_events.py | 15 +- Lib/test/test_asyncio/test_events.py | 8 - Lib/test/test_asyncio/test_subprocess.py | 196 +++++++++ Lib/test/test_asyncio/test_windows_events.py | 2 +- 9 files changed, 434 insertions(+), 50 deletions(-) diff --git a/Lib/asyncio/__init__.py b/Lib/asyncio/__init__.py --- a/Lib/asyncio/__init__.py +++ b/Lib/asyncio/__init__.py @@ -24,6 +24,7 @@ from .protocols import * from .queues import * from .streams import * +from .subprocess import * from .tasks import * from .transports import * @@ -39,5 +40,6 @@ protocols.__all__ + queues.__all__ + streams.__all__ + + subprocess.__all__ + tasks.__all__ + transports.__all__) diff --git a/Lib/asyncio/base_subprocess.py b/Lib/asyncio/base_subprocess.py --- a/Lib/asyncio/base_subprocess.py +++ b/Lib/asyncio/base_subprocess.py @@ -6,11 +6,6 @@ from . import transports -STDIN = 0 -STDOUT = 1 -STDERR = 2 - - class BaseSubprocessTransport(transports.SubprocessTransport): def __init__(self, loop, protocol, args, shell, @@ -22,11 +17,11 @@ self._pipes = {} if stdin == subprocess.PIPE: - self._pipes[STDIN] = None + self._pipes[0] = None if stdout == subprocess.PIPE: - self._pipes[STDOUT] = None + self._pipes[1] = None if stderr == subprocess.PIPE: - self._pipes[STDERR] = None + self._pipes[2] = None self._pending_calls = collections.deque() self._finished = False self._returncode = None @@ -76,19 +71,19 @@ loop = self._loop if proc.stdin is not None: _, pipe = yield from loop.connect_write_pipe( - lambda: WriteSubprocessPipeProto(self, STDIN), + lambda: WriteSubprocessPipeProto(self, 0), proc.stdin) - self._pipes[STDIN] = pipe + self._pipes[0] = pipe if proc.stdout is not None: _, pipe = yield from loop.connect_read_pipe( - lambda: ReadSubprocessPipeProto(self, STDOUT), + lambda: ReadSubprocessPipeProto(self, 1), proc.stdout) - self._pipes[STDOUT] = pipe + self._pipes[1] = pipe if proc.stderr is not None: _, pipe = yield from loop.connect_read_pipe( - lambda: ReadSubprocessPipeProto(self, STDERR), + lambda: ReadSubprocessPipeProto(self, 2), proc.stderr) - self._pipes[STDERR] = pipe + self._pipes[2] = pipe assert self._pending_calls is not None diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -29,6 +29,7 @@ self._buffer = None # None or bytearray. self._read_fut = None self._write_fut = None + self._pending_write = 0 self._conn_lost = 0 self._closing = False # Set when close() called. self._eof_written = False @@ -68,6 +69,7 @@ if self._read_fut: self._read_fut.cancel() self._write_fut = self._read_fut = None + self._pending_write = 0 self._buffer = None self._loop.call_soon(self._call_connection_lost, exc) @@ -128,11 +130,10 @@ self._low_water = low def get_write_buffer_size(self): - # NOTE: This doesn't take into account data already passed to - # send() even if send() hasn't finished yet. - if not self._buffer: - return 0 - return len(self._buffer) + size = self._pending_write + if self._buffer is not None: + size += len(self._buffer) + return size class _ProactorReadPipeTransport(_ProactorBasePipeTransport, @@ -206,7 +207,7 @@ class _ProactorBaseWritePipeTransport(_ProactorBasePipeTransport, - transports.WriteTransport): + transports.WriteTransport): """Transport for write pipes.""" def write(self, data): @@ -252,6 +253,7 @@ try: assert f is self._write_fut self._write_fut = None + self._pending_write = 0 if f: f.result() if data is None: @@ -262,15 +264,21 @@ self._loop.call_soon(self._call_connection_lost, None) if self._eof_written: self._sock.shutdown(socket.SHUT_WR) + # Now that we've reduced the buffer size, tell the + # protocol to resume writing if it was paused. Note that + # we do this last since the callback is called immediately + # and it may add more data to the buffer (even causing the + # protocol to be paused again). + self._maybe_resume_protocol() else: self._write_fut = self._loop._proactor.send(self._sock, data) - self._write_fut.add_done_callback(self._loop_writing) - # Now that we've reduced the buffer size, tell the - # protocol to resume writing if it was paused. Note that - # we do this last since the callback is called immediately - # and it may add more data to the buffer (even causing the - # protocol to be paused again). - self._maybe_resume_protocol() + if not self._write_fut.done(): + assert self._pending_write == 0 + self._pending_write = len(data) + self._write_fut.add_done_callback(self._loop_writing) + self._maybe_pause_protocol() + else: + self._write_fut.add_done_callback(self._loop_writing) except ConnectionResetError as exc: self._force_close(exc) except OSError as exc: diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py new file mode 100644 --- /dev/null +++ b/Lib/asyncio/subprocess.py @@ -0,0 +1,197 @@ +__all__ = ['create_subprocess_exec', 'create_subprocess_shell'] + +import collections +import subprocess + +from . import events +from . import futures +from . import protocols +from . import streams +from . import tasks + + +PIPE = subprocess.PIPE +STDOUT = subprocess.STDOUT +DEVNULL = subprocess.DEVNULL + + +class SubprocessStreamProtocol(streams.FlowControlMixin, + protocols.SubprocessProtocol): + """Like StreamReaderProtocol, but for a subprocess.""" + + def __init__(self, limit, loop): + super().__init__(loop=loop) + self._limit = limit + self.stdin = self.stdout = self.stderr = None + self.waiter = futures.Future(loop=loop) + self._waiters = collections.deque() + self._transport = None + + def connection_made(self, transport): + self._transport = transport + if transport.get_pipe_transport(1): + self.stdout = streams.StreamReader(limit=self._limit, + loop=self._loop) + if transport.get_pipe_transport(2): + self.stderr = streams.StreamReader(limit=self._limit, + loop=self._loop) + stdin = transport.get_pipe_transport(0) + if stdin is not None: + self.stdin = streams.StreamWriter(stdin, + protocol=self, + reader=None, + loop=self._loop) + self.waiter.set_result(None) + + def pipe_data_received(self, fd, data): + if fd == 1: + reader = self.stdout + elif fd == 2: + reader = self.stderr + else: + reader = None + if reader is not None: + reader.feed_data(data) + + def pipe_connection_lost(self, fd, exc): + if fd == 0: + pipe = self.stdin + if pipe is not None: + pipe.close() + self.connection_lost(exc) + return + if fd == 1: + reader = self.stdout + elif fd == 2: + reader = self.stderr + else: + reader = None + if reader != None: + if exc is None: + reader.feed_eof() + else: + reader.set_exception(exc) + + def process_exited(self): + # wake up futures waiting for wait() + returncode = self._transport.get_returncode() + while self._waiters: + waiter = self._waiters.popleft() + waiter.set_result(returncode) + + +class Process: + def __init__(self, transport, protocol, loop): + self._transport = transport + self._protocol = protocol + self._loop = loop + self.stdin = protocol.stdin + self.stdout = protocol.stdout + self.stderr = protocol.stderr + self.pid = transport.get_pid() + + @property + def returncode(self): + return self._transport.get_returncode() + + @tasks.coroutine + def wait(self): + """Wait until the process exit and return the process return code.""" + returncode = self._transport.get_returncode() + if returncode is not None: + return returncode + + waiter = futures.Future(loop=self._loop) + self._protocol._waiters.append(waiter) + yield from waiter + return waiter.result() + + def get_subprocess(self): + return self._transport.get_extra_info('subprocess') + + def _check_alive(self): + if self._transport.get_returncode() is not None: + raise ProcessLookupError() + + def send_signal(self, signal): + self._check_alive() + self._transport.send_signal(signal) + + def terminate(self): + self._check_alive() + self._transport.terminate() + + def kill(self): + self._check_alive() + self._transport.kill() + + @tasks.coroutine + def _feed_stdin(self, input): + self.stdin.write(input) + yield from self.stdin.drain() + self.stdin.close() + + @tasks.coroutine + def _noop(self): + return None + + @tasks.coroutine + def _read_stream(self, fd): + transport = self._transport.get_pipe_transport(fd) + if fd == 2: + stream = self.stderr + else: + assert fd == 1 + stream = self.stdout + output = yield from stream.read() + transport.close() + return output + + @tasks.coroutine + def communicate(self, input=None): + loop = self._transport._loop + if input: + stdin = self._feed_stdin(input) + else: + stdin = self._noop() + if self.stdout is not None: + stdout = self._read_stream(1) + else: + stdout = self._noop() + if self.stderr is not None: + stderr = self._read_stream(2) + else: + stderr = self._noop() + stdin, stdout, stderr = yield from tasks.gather(stdin, stdout, stderr, + loop=loop) + yield from self.wait() + return (stdout, stderr) + + + at tasks.coroutine +def create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, + loop=None, limit=streams._DEFAULT_LIMIT, **kwds): + if loop is None: + loop = events.get_event_loop() + protocol_factory = lambda: SubprocessStreamProtocol(limit=limit, + loop=loop) + transport, protocol = yield from loop.subprocess_shell( + protocol_factory, + cmd, stdin=stdin, stdout=stdout, + stderr=stderr, **kwds) + yield from protocol.waiter + return Process(transport, protocol, loop) + + at tasks.coroutine +def create_subprocess_exec(*args, stdin=None, stdout=None, stderr=None, + loop=None, limit=streams._DEFAULT_LIMIT, **kwds): + if loop is None: + loop = events.get_event_loop() + protocol_factory = lambda: SubprocessStreamProtocol(limit=limit, + loop=loop) + transport, protocol = yield from loop.subprocess_exec( + protocol_factory, + *args, stdin=stdin, stdout=stdout, + stderr=stderr, **kwds) + yield from protocol.waiter + return Process(transport, protocol, loop) diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -21,16 +21,11 @@ from .log import logger -__all__ = ['SelectorEventLoop', 'STDIN', 'STDOUT', 'STDERR', +__all__ = ['SelectorEventLoop', 'AbstractChildWatcher', 'SafeChildWatcher', 'FastChildWatcher', 'DefaultEventLoopPolicy', ] -STDIN = 0 -STDOUT = 1 -STDERR = 2 - - if sys.platform == 'win32': # pragma: no cover raise ImportError('Signals are not really supported on Windows') diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -116,18 +116,17 @@ self.loop.stop() self.loop._process_events = unittest.mock.Mock() - delay = 0.1 - - when = self.loop.time() + delay + when = self.loop.time() + 0.1 self.loop.call_at(when, cb) t0 = self.loop.time() self.loop.run_forever() dt = self.loop.time() - t0 - - self.assertGreaterEqual(dt, delay - self.loop._granularity, dt) - # tolerate a difference of +800 ms because some Python buildbots - # are really slow - self.assertLessEqual(dt, 0.9, dt) + self.assertTrue(0.09 <= dt <= 0.9, + # Issue #20452: add more info in case of failure, + # to try to investigate the bug + (dt, + self.loop._granularity, + time.get_clock_info('monotonic'))) def test_run_once_in_executor_handle(self): def cb(): diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1179,14 +1179,6 @@ calls.append(self.loop._run_once_counter) self.assertEqual(calls, [1, 3, 5, 6]) - def test_granularity(self): - granularity = self.loop._granularity - self.assertGreater(granularity, 0.0) - # Worst expected granularity: 1 ms on Linux (limited by poll/epoll - # resolution), 15.6 ms on Windows (limited by time.monotonic - # resolution) - self.assertLess(granularity, 0.050) - class SubprocessTestsMixin: diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -0,0 +1,196 @@ +from asyncio import subprocess +import asyncio +import signal +import sys +import unittest +from test import support +if sys.platform != 'win32': + from asyncio import unix_events + +# Program exiting quickly +PROGRAM_EXIT_FAST = [sys.executable, '-c', 'pass'] + +# Program blocking +PROGRAM_BLOCKED = [sys.executable, '-c', 'import time; time.sleep(3600)'] + +# Program sleeping during 1 second +PROGRAM_SLEEP_1SEC = [sys.executable, '-c', 'import time; time.sleep(1)'] + +# Program copying input to output +PROGRAM_CAT = [ + sys.executable, '-c', + ';'.join(('import sys', + 'data = sys.stdin.buffer.read()', + 'sys.stdout.buffer.write(data)'))] + +class SubprocessMixin: + def test_stdin_stdout(self): + args = PROGRAM_CAT + + @asyncio.coroutine + def run(data): + proc = yield from asyncio.create_subprocess_exec( + *args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + loop=self.loop) + + # feed data + proc.stdin.write(data) + yield from proc.stdin.drain() + proc.stdin.close() + + # get output and exitcode + data = yield from proc.stdout.read() + exitcode = yield from proc.wait() + return (exitcode, data) + + task = run(b'some data') + task = asyncio.wait_for(task, 10.0, loop=self.loop) + exitcode, stdout = self.loop.run_until_complete(task) + self.assertEqual(exitcode, 0) + self.assertEqual(stdout, b'some data') + + def test_communicate(self): + args = PROGRAM_CAT + + @asyncio.coroutine + def run(data): + proc = yield from asyncio.create_subprocess_exec( + *args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + loop=self.loop) + stdout, stderr = yield from proc.communicate(data) + return proc.returncode, stdout + + task = run(b'some data') + task = asyncio.wait_for(task, 10.0, loop=self.loop) + exitcode, stdout = self.loop.run_until_complete(task) + self.assertEqual(exitcode, 0) + self.assertEqual(stdout, b'some data') + + def test_shell(self): + create = asyncio.create_subprocess_shell('exit 7', + loop=self.loop) + proc = self.loop.run_until_complete(create) + exitcode = self.loop.run_until_complete(proc.wait()) + self.assertEqual(exitcode, 7) + + def test_start_new_session(self): + # start the new process in a new session + create = asyncio.create_subprocess_shell('exit 8', + start_new_session=True, + loop=self.loop) + proc = self.loop.run_until_complete(create) + exitcode = self.loop.run_until_complete(proc.wait()) + self.assertEqual(exitcode, 8) + + def test_kill(self): + args = PROGRAM_BLOCKED + create = asyncio.create_subprocess_exec(*args, loop=self.loop) + proc = self.loop.run_until_complete(create) + proc.kill() + returncode = self.loop.run_until_complete(proc.wait()) + if sys.platform == 'win32': + self.assertIsInstance(returncode, int) + # expect 1 but sometimes get 0 + else: + self.assertEqual(-signal.SIGKILL, returncode) + + def test_terminate(self): + args = PROGRAM_BLOCKED + create = asyncio.create_subprocess_exec(*args, loop=self.loop) + proc = self.loop.run_until_complete(create) + proc.terminate() + returncode = self.loop.run_until_complete(proc.wait()) + if sys.platform == 'win32': + self.assertIsInstance(returncode, int) + # expect 1 but sometimes get 0 + else: + self.assertEqual(-signal.SIGTERM, returncode) + + @unittest.skipIf(sys.platform == 'win32', "Don't have SIGHUP") + def test_send_signal(self): + args = PROGRAM_BLOCKED + create = asyncio.create_subprocess_exec(*args, loop=self.loop) + proc = self.loop.run_until_complete(create) + proc.send_signal(signal.SIGHUP) + returncode = self.loop.run_until_complete(proc.wait()) + self.assertEqual(-signal.SIGHUP, returncode) + + def test_get_subprocess(self): + args = PROGRAM_EXIT_FAST + + @asyncio.coroutine + def run(): + proc = yield from asyncio.create_subprocess_exec(*args, + loop=self.loop) + yield from proc.wait() + + popen = proc.get_subprocess() + popen.wait() + return (proc, popen) + + proc, popen = self.loop.run_until_complete(run()) + self.assertEqual(popen.returncode, proc.returncode) + self.assertEqual(popen.pid, proc.pid) + + def test_broken_pipe(self): + large_data = b'x' * support.PIPE_MAX_SIZE + + create = asyncio.create_subprocess_exec( + *PROGRAM_SLEEP_1SEC, + stdin=subprocess.PIPE, + loop=self.loop) + proc = self.loop.run_until_complete(create) + with self.assertRaises(BrokenPipeError): + self.loop.run_until_complete(proc.communicate(large_data)) + self.loop.run_until_complete(proc.wait()) + + +if sys.platform != 'win32': + # Unix + class SubprocessWatcherMixin(SubprocessMixin): + Watcher = None + + def setUp(self): + policy = asyncio.get_event_loop_policy() + self.loop = policy.new_event_loop() + + # ensure that the event loop is passed explicitly in the code + policy.set_event_loop(None) + + watcher = self.Watcher() + watcher.attach_loop(self.loop) + policy.set_child_watcher(watcher) + + def tearDown(self): + policy = asyncio.get_event_loop_policy() + policy.set_child_watcher(None) + self.loop.close() + policy.set_event_loop(None) + + class SubprocessSafeWatcherTests(SubprocessWatcherMixin, unittest.TestCase): + Watcher = unix_events.SafeChildWatcher + + class SubprocessFastWatcherTests(SubprocessWatcherMixin, unittest.TestCase): + Watcher = unix_events.FastChildWatcher +else: + # Windows + class SubprocessProactorTests(SubprocessMixin, unittest.TestCase): + def setUp(self): + policy = asyncio.get_event_loop_policy() + self.loop = asyncio.ProactorEventLoop() + + # ensure that the event loop is passed explicitly in the code + policy.set_event_loop(None) + + def tearDown(self): + policy = asyncio.get_event_loop_policy() + self.loop.close() + policy.set_event_loop(None) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -105,7 +105,7 @@ self.loop.run_until_complete(f) elapsed = self.loop.time() - start self.assertFalse(f.result()) - self.assertTrue(0.18 < elapsed < 0.9, elapsed) + self.assertTrue(0.18 < elapsed < 0.5, elapsed) _overlapped.SetEvent(event) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 05:15:26 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 2 Feb 2014 05:15:26 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Update_the_pyt?= =?utf-8?q?hon=2Egif_icon_for_the_Idle_classbrowser_and_pathbowser?= Message-ID: <3fGzNZ63qlz7Ljf@mail.python.org> http://hg.python.org/cpython/rev/ae1c70135763 changeset: 88890:ae1c70135763 branch: 2.7 parent: 88886:0d50b5851f38 user: Terry Jan Reedy date: Sat Feb 01 23:08:24 2014 -0500 summary: Update the python.gif icon for the Idle classbrowser and pathbowser from the old green snake to the new new blue and yellow snakes. files: Lib/idlelib/Icons/python.gif | Bin 1 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/Icons/python.gif b/Lib/idlelib/Icons/python.gif index 58271edec49353382f935ada2de138a1f5b35d9a..b189c2c2d22c120800daefebf9b8bb26f60857a0 GIT binary patch [stripped] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 05:15:28 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 2 Feb 2014 05:15:28 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Update_the_pyt?= =?utf-8?q?hon=2Egif_icon_for_the_Idle_classbrowser_and_pathbowser?= Message-ID: <3fGzNc0YDjz7Ljt@mail.python.org> http://hg.python.org/cpython/rev/412a8e2c2af6 changeset: 88891:412a8e2c2af6 branch: 3.3 parent: 88887:32097f193892 user: Terry Jan Reedy date: Sat Feb 01 23:08:33 2014 -0500 summary: Update the python.gif icon for the Idle classbrowser and pathbowser from the old green snake to the new new blue and yellow snakes. files: Lib/idlelib/Icons/python.gif | Bin Misc/NEWS | 3 +++ 2 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/Icons/python.gif b/Lib/idlelib/Icons/python.gif index 58271edec49353382f935ada2de138a1f5b35d9a..b189c2c2d22c120800daefebf9b8bb26f60857a0 GIT binary patch [stripped] diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -286,6 +286,9 @@ IDLE ---- +- Update the python.gif icon for the Idle classbrowser and pathbowser + from the old green snake to the new new blue and yellow snakes. + - Issue #17721: Remove non-functional configuration dialog help button until we make it actually gives some help when clicked. Patch by Guilherme Sim?es. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 05:15:29 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 2 Feb 2014 05:15:29 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Update_the_python=2Egif_icon_for_the_Idle_classbrowser_a?= =?utf-8?q?nd_pathbowser?= Message-ID: <3fGzNd2S8Fz7LkD@mail.python.org> http://hg.python.org/cpython/rev/5e0f9b8e6c2b changeset: 88892:5e0f9b8e6c2b parent: 88889:d7ac90c0463a parent: 88891:412a8e2c2af6 user: Terry Jan Reedy date: Sat Feb 01 23:14:59 2014 -0500 summary: Update the python.gif icon for the Idle classbrowser and pathbowser from the old green snake to the new new blue and yellow snakes. files: Lib/idlelib/Icons/python.gif | Bin Misc/NEWS | 3 +++ 2 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/Icons/python.gif b/Lib/idlelib/Icons/python.gif index 58271edec49353382f935ada2de138a1f5b35d9a..b189c2c2d22c120800daefebf9b8bb26f60857a0 GIT binary patch [stripped] diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -85,6 +85,9 @@ IDLE ---- +- Update the python.gif icon for the Idle classbrowser and pathbowser + from the old green snake to the new new blue and yellow snakes. + - Issue #17721: Remove non-functional configuration dialog help button until we make it actually gives some help when clicked. Patch by Guilherme Sim?es. -- Repository URL: http://hg.python.org/cpython From tjreedy at udel.edu Sun Feb 2 05:25:17 2014 From: tjreedy at udel.edu (Terry Reedy) Date: Sat, 01 Feb 2014 23:25:17 -0500 Subject: [Python-checkins] cpython (2.7): Update the python.gif icon for the Idle classbrowser and pathbowser In-Reply-To: <3fGzNZ63qlz7Ljf@mail.python.org> References: <3fGzNZ63qlz7Ljf@mail.python.org> Message-ID: <52EDC8AD.4070707@udel.edu> On 2/1/2014 11:15 PM, terry.reedy wrote: > http://hg.python.org/cpython/rev/ae1c70135763 > changeset: 88890:ae1c70135763 > branch: 2.7 > parent: 88886:0d50b5851f38 > user: Terry Jan Reedy > date: Sat Feb 01 23:08:24 2014 -0500 > summary: > Update the python.gif icon for the Idle classbrowser and pathbowser > from the old green snake to the new new blue and yellow snakes. > > files: > Lib/idlelib/Icons/python.gif | Bin > 1 files changed, 0 insertions(+), 0 deletions(-) > > > diff --git a/Lib/idlelib/Icons/python.gif b/Lib/idlelib/Icons/python.gif > index 58271edec49353382f935ada2de138a1f5b35d9a..b189c2c2d22c120800daefebf9b8bb26f60857a0 > GIT binary patch > [stripped] I deleted the NEWS item added to 3.3/4 IDLE ---- +- Update the python.gif icon for the Idle classbrowser and pathbowser + from the old green snake to the new new blue and yellow snakes. + - Issue #17721: Remove non-functional configuration dialog help button until we make it actually gives some help when clicked. Patch by Guilherme Sim?es. because the pre-commit diff for 2.7 was to delete all ~10000 lines and add back 3 more. This has happened before and is the 2nd thing I hate about NEWS, other than merge conflicts (which happened for the 3.3 to 3.4 merge). Terry From solipsis at pitrou.net Sun Feb 2 09:46:39 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 02 Feb 2014 09:46:39 +0100 Subject: [Python-checkins] Daily reference leaks (d7ac90c0463a): sum=9 Message-ID: results for d7ac90c0463a on branch "default" -------------------------------------------- test__opcode leaked [3, 3, 3] references, sum=9 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogu451k0', '-x'] From python-checkins at python.org Sun Feb 2 15:04:47 2014 From: python-checkins at python.org (victor.stinner) Date: Sun, 2 Feb 2014 15:04:47 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_doc=3A_add_=22asyn?= =?utf-8?q?cio-=22_prefix_to_references?= Message-ID: <3fHDSb6TF4z7Ljn@mail.python.org> http://hg.python.org/cpython/rev/5141dd05bc44 changeset: 88893:5141dd05bc44 user: Victor Stinner date: Sun Feb 02 15:03:02 2014 +0100 summary: asyncio doc: add "asyncio-" prefix to references files: Doc/library/asyncio-dev.rst | 2 +- Doc/library/asyncio-eventloop.rst | 12 ++++++------ Doc/library/asyncio-protocol.rst | 10 +++++----- Doc/library/asyncio-stream.rst | 2 +- Doc/library/asyncio.rst | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -43,7 +43,7 @@ important impact on reactivity. For networking and subprocesses, the :mod:`asyncio` module provides high-level -APIs like :ref:`protocols `. +APIs like :ref:`protocols `. An executor can be used to run a task in a different thread or even in a different process, to not block the thread of the event loop. See the diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -1,6 +1,6 @@ .. currentmodule:: asyncio -.. _event-loop: +.. _asyncio-event-loop: Event loops =========== @@ -10,10 +10,10 @@ * Registering, executing and cancelling delayed calls (timeouts) -* Creating client and server :ref:`transports ` for various +* Creating client and server :ref:`transports ` for various kinds of communication -* Launching subprocesses and the associated :ref:`transports ` +* Launching subprocesses and the associated :ref:`transports ` for communication with an external program * Delegating costly function calls to a pool of threads @@ -172,7 +172,7 @@ Create a streaming transport connection to a given Internet *host* and *port*. *protocol_factory* must be a callable returning a - :ref:`protocol ` instance. + :ref:`protocol ` instance. This method returns a :ref:`coroutine object ` which will try to establish the connection in the background. When successful, the @@ -180,11 +180,11 @@ The chronological synopsis of the underlying operation is as follows: - #. The connection is established, and a :ref:`transport ` + #. The connection is established, and a :ref:`transport ` is created to represent it. #. *protocol_factory* is called without arguments and must return a - :ref:`protocol ` instance. + :ref:`protocol ` instance. #. The protocol instance is tied to the transport, and its :meth:`connection_made` method is called. diff --git a/Doc/library/asyncio-protocol.rst b/Doc/library/asyncio-protocol.rst --- a/Doc/library/asyncio-protocol.rst +++ b/Doc/library/asyncio-protocol.rst @@ -4,7 +4,7 @@ Transports and protocols (low-level API) +++++++++++++++++++++++++++++++++++++++++ -.. _transport: +.. _asyncio-transport: Transports ========== @@ -16,7 +16,7 @@ communication channel, calling you back when it succeeds. Once the communication channel is established, a transport is always -paired with a :ref:`protocol ` instance. The protocol can +paired with a :ref:`protocol ` instance. The protocol can then call the transport's methods for various purposes. :mod:`asyncio` currently implements transports for TCP, UDP, SSL, and @@ -228,14 +228,14 @@ stop the subprocess. -.. _protocol: +.. _asyncio-protocol: Protocols ========= :mod:`asyncio` provides base classes that you can subclass to implement your network protocols. Those classes are used in conjunction with -:ref:`transports ` (see below): the protocol parses incoming +:ref:`transports ` (see below): the protocol parses incoming data and asks for the writing of outgoing data, while the transport is responsible for the actual I/O and buffering. @@ -410,7 +410,7 @@ is not guarantee on the execution order. Protocols are not aware of coroutines created in protocol methods and so will not wait for them. -To have a reliable execution order, use :ref:`stream objects ` in a +To have a reliable execution order, use :ref:`stream objects ` in a coroutine with ``yield from``. For example, the :meth:`StreamWriter.drain` coroutine can be used to wait until the write buffer is flushed. diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -1,6 +1,6 @@ .. currentmodule:: asyncio -.. _streams: +.. _asyncio-streams: ++++++++++++++++++++++++ Streams (high-level API) diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -15,10 +15,10 @@ resources, running network clients and servers, and other related primitives. Here is a more detailed list of the package contents: -* a pluggable :ref:`event loop ` with various system-specific +* a pluggable :ref:`event loop ` with various system-specific implementations; -* :ref:`transport ` and :ref:`protocol ` abstractions +* :ref:`transport ` and :ref:`protocol ` abstractions (similar to those in `Twisted `_); * concrete support for TCP, UDP, SSL, subprocess pipes, delayed calls, and @@ -32,7 +32,7 @@ * cancellation support for Futures and coroutines; -* :ref:`synchronization primitives ` for use between coroutines in +* :ref:`synchronization primitives ` for use between coroutines in a single thread, mimicking those in the :mod:`threading` module; * an interface for passing work off to a threadpool, for times when -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 17:33:12 2014 From: python-checkins at python.org (r.david.murray) Date: Sun, 2 Feb 2014 17:33:12 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_whatsnew=3A_fix_importlib?= =?utf-8?q?=2Ereload_entry=2E?= Message-ID: <3fHHlr0z3Zz7LjX@mail.python.org> http://hg.python.org/cpython/rev/a0e7e13535a7 changeset: 88894:a0e7e13535a7 user: R David Murray date: Sun Feb 02 10:50:17 2014 -0500 summary: whatsnew: fix importlib.reload entry. Turns out I committed a work-in-progress entry because of a time gap between when I wrote it and when I committed. files: Doc/whatsnew/3.4.rst | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -765,9 +765,9 @@ it will normally be desirable to override the default implementation for performance reasons. (Contributed by Brett Cannon in :issue:`18072`.) -The :func:`~importlib.reload` function has been moved from :mod:`imp` -to :mod:`importlib`. The :func:`mod.reload` name is retained for -backward compatibility, but is deprecated. +The :func:`~importlib.reload` function has been moved from :mod:`imp` to +:mod:`importlib` as part of the :mod:`imp` module deprecation. (Contributed by +Berker Peksag in :issue:`18193`.) inspect -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 17:33:13 2014 From: python-checkins at python.org (r.david.murray) Date: Sun, 2 Feb 2014 17:33:13 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_whatsnew=3A_filecmp=2Eclea?= =?utf-8?q?r=5Fcache=2C_and_reword_description_of_cache_in_docs=2E?= Message-ID: <3fHHls2mjtz7Ljf@mail.python.org> http://hg.python.org/cpython/rev/31b37e72b98d changeset: 88895:31b37e72b98d user: R David Murray date: Sun Feb 02 11:11:01 2014 -0500 summary: whatsnew: filecmp.clear_cache, and reword description of cache in docs. files: Doc/library/filecmp.rst | 8 ++++---- Doc/whatsnew/3.4.rst | 11 +++++++++++ Lib/filecmp.py | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Doc/library/filecmp.rst b/Doc/library/filecmp.rst --- a/Doc/library/filecmp.rst +++ b/Doc/library/filecmp.rst @@ -28,8 +28,8 @@ portability and efficiency. This function uses a cache for past comparisons and the results, - with a cache invalidation mechanism relying on stale signatures - or by explicitly calling :func:`clear_cache`. + with cache entries invalidated if the :func:`os.stat` information for the + file changes. The entire cache may be cleared using :func:`clear_cache`. .. function:: cmpfiles(dir1, dir2, common, shallow=True) @@ -54,12 +54,12 @@ .. function:: clear_cache() - .. versionadded:: 3.4 - Clear the filecmp cache. This may be useful if a file is compared so quickly after it is modified that it is within the mtime resolution of the underlying filesystem. + .. versionadded:: 3.4 + .. _dircmp-objects: diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -675,6 +675,17 @@ (Contributed by R. David Murray in :issue:`18891`.) +filecmp +------- + +A new :func:`~filecmp.clear_cache` function provides the ability to clear the +:mod:`filecmp` comparison cache, which uses :func:`os.stat` information to +determine if the file has changed since the last compare. This can be used, +for example, if the file might have been changed and re-checked in less time +than the resolution of a particular filesystem's file modification time field. +(Contributed by Mark Levitt in :issue:`18149`.) + + functools --------- diff --git a/Lib/filecmp.py b/Lib/filecmp.py --- a/Lib/filecmp.py +++ b/Lib/filecmp.py @@ -43,8 +43,8 @@ True if the files are the same, False otherwise. This function uses a cache for past comparisons and the results, - with a cache invalidation mechanism relying on stale signatures - or by explicitly calling clear_cache(). + with cache entries invalidated if their stat information + changes. The cache may be cleared by calling clear_cache(). """ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 17:33:14 2014 From: python-checkins at python.org (r.david.murray) Date: Sun, 2 Feb 2014 17:33:14 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_whatsnew=3A_some_more_impo?= =?utf-8?q?rtlib_replacements_for_imp_functions=2E?= Message-ID: <3fHHlt4CqGz7Lkm@mail.python.org> http://hg.python.org/cpython/rev/51b78ec2445e changeset: 88896:51b78ec2445e user: R David Murray date: Sun Feb 02 11:32:31 2014 -0500 summary: whatsnew: some more importlib replacements for imp functions. get_magic->util.MAGIC_NUMBER, source_from_cache, and cache_from_source. files: Doc/whatsnew/3.4.rst | 12 ++++++++++-- 1 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -761,7 +761,6 @@ of the error response. (Contributed by Karl Cow in :issue:`12921`.) - importlib --------- @@ -780,11 +779,20 @@ :mod:`importlib` as part of the :mod:`imp` module deprecation. (Contributed by Berker Peksag in :issue:`18193`.) +:mod:`importlib.util` now has a :data:`~importlib.util.MAGIC_NUMBER` attribute +providing access to the bytecode version number. This replaces the +:func:`~imp.get_magic` function in the deprecated :mod:`imp` module. +(Contributed by Brett Cannon in :issue:`18192`.) + +New :mod:`importlib.util` functions :func:`~importlib.util.cache_from_source` +and :func:`~importlib.util.source_from_cache` replace the same-named functions +in the deprecated :mod:`imp` module. (Contributed by Brett Cannon in +:issue:`18194`.) + inspect ------- - The inspect module now offers a basic :ref:`command line interface ` to quickly display source code and other information for modules, classes and functions. (Contributed by Claudiu Popa -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 18:51:35 2014 From: python-checkins at python.org (yury.selivanov) Date: Sun, 2 Feb 2014 18:51:35 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_inspect=2Esignature=3A_Use?= =?utf-8?q?_=27inspect=2Eisbuiltin=27_in_=27Signature=2Efrom=5Fbuiltin=27?= Message-ID: <3fHKVH16xvz7Ljf@mail.python.org> http://hg.python.org/cpython/rev/48c3c42e3e5c changeset: 88897:48c3c42e3e5c user: Yury Selivanov date: Sun Feb 02 12:51:20 2014 -0500 summary: inspect.signature: Use 'inspect.isbuiltin' in 'Signature.from_builtin' files: Lib/inspect.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1594,8 +1594,9 @@ def _signature_is_builtin(obj): # Internal helper to test if `obj` is a callable that might # support Argument Clinic's __text_signature__ protocol. - return (isinstance(obj, _NonUserDefinedCallables) or + return (isbuiltin(obj) or ismethoddescriptor(obj) or + isinstance(obj, _NonUserDefinedCallables) or # Can't test 'isinstance(type)' here, as it would # also be True for regular python classes obj in (type, object)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 18:53:15 2014 From: python-checkins at python.org (r.david.murray) Date: Sun, 2 Feb 2014 18:53:15 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_whatsnew=3A_-m_=3Cnamespac?= =?utf-8?q?e_package=3E=2C_plus_=27using=27_doc_updates=2E?= Message-ID: <3fHKXC4rPFz7LkL@mail.python.org> http://hg.python.org/cpython/rev/28ae009435b0 changeset: 88898:28ae009435b0 user: R David Murray date: Sun Feb 02 12:19:57 2014 -0500 summary: whatsnew: -m , plus 'using' doc updates. files: Doc/using/cmdline.rst | 6 +++++- Doc/whatsnew/3.4.rst | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletions(-) diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -81,7 +81,8 @@ the implementation may not always enforce this (e.g. it may allow you to use a name that includes a hyphen). - Package names are also permitted. When a package name is supplied instead + Package names (including namespace packages) are also permitted. When a + package name is supplied instead of a normal module, the interpreter will execute ``.__main__`` as the main module. This behaviour is deliberately similar to the handling of directories and zipfiles that are passed to the interpreter as the @@ -115,6 +116,9 @@ .. versionchanged:: 3.1 Supply the package name to run a ``__main__`` submodule. + .. versionchanged:: 3.4 + namespace packages are also supported + .. describe:: - diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -789,6 +789,11 @@ in the deprecated :mod:`imp` module. (Contributed by Brett Cannon in :issue:`18194`.) +The :mod:`importlib` bootstrap :class:`.NamespaceLoader` now conforms to +the :class:`.InspectLoader` ABC, which means that ``runpy`` and +``python -m`` can now be used with namespace packages. (Contributed +by Brett Cannon in :issue:`18058`.) + inspect ------- @@ -1380,6 +1385,8 @@ :func:`sys.getallocatedblocks()`. (Contributed by Antoine Pitrou in :issue:`13390`). +* ``python -m`` now works with namespace packages. + Significant Optimizations -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 18:53:16 2014 From: python-checkins at python.org (r.david.murray) Date: Sun, 2 Feb 2014 18:53:16 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_whatsnew=3A_hmac_accepts_m?= =?utf-8?q?ore_bytes_types=2C_importlib_decode=5Fsource=2C_stat_in_C=2E?= Message-ID: <3fHKXD6k1Cz7Ll9@mail.python.org> http://hg.python.org/cpython/rev/558b26b6a342 changeset: 88899:558b26b6a342 user: R David Murray date: Sun Feb 02 12:50:48 2014 -0500 summary: whatsnew: hmac accepts more bytes types, importlib decode_source, stat in C. files: Doc/whatsnew/3.4.rst | 18 ++++++++++++++++++ Misc/NEWS | 2 +- 2 files changed, 19 insertions(+), 1 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -732,6 +732,16 @@ (Contributed by Christian Heimes in :issue:`18582`) +hmac +---- + +:mod:`hmac` now accepts ``bytearray`` as well as ``bytes`` for the *key* +argument to the :func:`~hmac.new` function, and the *msg* parameter to both the +:func:`~hmac.new` function and the :meth:`~hmac.HMAC.update` method now +accepts any type supported by the :mod:`hashlib` module. (Contributed +by Jonas Borgstr?m in :issue:`18240`.) + + html ---- @@ -794,6 +804,10 @@ ``python -m`` can now be used with namespace packages. (Contributed by Brett Cannon in :issue:`18058`.) +:mod:`importlib.util` has a new function :func:`~importlib.util.decode_source` +that decodes source from bytes using universal newline processing. This is +useful for implementing :meth:`.InspectLoader.get_source` methods. + inspect ------- @@ -1387,6 +1401,10 @@ * ``python -m`` now works with namespace packages. +* The :mod:`stat` module is now implemented in C, which means it gets the + values for its constants from the C header files, instead of having the + values hard-coded in the python module as was previously the case. + Significant Optimizations diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -2346,7 +2346,7 @@ - Issue #18339: Negative ints keys in unpickler.memo dict no longer cause a segfault inside the _pickle C extension. -- Issue 18240: The HMAC module is no longer restricted to bytes and accepts +- Issue #18240: The HMAC module is no longer restricted to bytes and accepts any bytes-like object, e.g. memoryview. Original patch by Jonas Borgstr?m. - Issue #18224: Removed pydoc script from created venv, as it causes problems -- Repository URL: http://hg.python.org/cpython From rdmurray at bitdance.com Sun Feb 2 19:08:48 2014 From: rdmurray at bitdance.com (R. David Murray) Date: Sun, 02 Feb 2014 13:08:48 -0500 Subject: [Python-checkins] [Python-Dev] cpython: whatsnew: move of reload, update new windows-only ssl functions entry. In-Reply-To: References: <3fGj0k1zTdz7Llp@mail.python.org> Message-ID: <20140202180848.C8A0C250499@webabinitio.net> On Sat, 01 Feb 2014 13:20:48 -0500, Brett Cannon wrote: > On Sat, Feb 1, 2014 at 12:27 PM, r.david.murray > wrote: > > > http://hg.python.org/cpython/rev/b3f034f5000f > > changeset: 88884:b3f034f5000f > > parent: 88882:19d81cc213d7 > > user: R David Murray > > date: Sat Feb 01 12:27:07 2014 -0500 > > summary: > > whatsnew: move of reload, update new windows-only ssl functions entry. > > > > files: > > Doc/whatsnew/3.4.rst | 10 ++++++++-- > > 1 files changed, 8 insertions(+), 2 deletions(-) > > > > > > diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst > > --- a/Doc/whatsnew/3.4.rst > > +++ b/Doc/whatsnew/3.4.rst > > @@ -765,6 +765,10 @@ > > it will normally be desirable to override the default implementation > > for performance reasons. (Contributed by Brett Cannon in :issue:`18072`.) > > > > +The :func:`~importlib.reload` function has been moved from :mod:`imp` > > +to :mod:`importlib`. The :func:`mod.reload` name is retained for > > +backward compatibility, but is deprecated. > > + > > > > That wording seems confusing to me. It makes it seem like importlib.reload > is deprecated when in fact it's the imp module itself. I committed that thinking I'd finished working on that entry, but I hadn't. The updated text reads: The reload() function has been moved from imp to importlib as part of the imp module deprecation. Is that clearer? --David From brett at python.org Sun Feb 2 21:10:32 2014 From: brett at python.org (Brett Cannon) Date: Sun, 2 Feb 2014 15:10:32 -0500 Subject: [Python-checkins] [Python-Dev] cpython: whatsnew: move of reload, update new windows-only ssl functions entry. In-Reply-To: <20140202180848.C8A0C250499@webabinitio.net> References: <3fGj0k1zTdz7Llp@mail.python.org> <20140202180848.C8A0C250499@webabinitio.net> Message-ID: On Feb 2, 2014 1:08 PM, "R. David Murray" wrote: > > On Sat, 01 Feb 2014 13:20:48 -0500, Brett Cannon wrote: > > On Sat, Feb 1, 2014 at 12:27 PM, r.david.murray > > wrote: > > > > > http://hg.python.org/cpython/rev/b3f034f5000f > > > changeset: 88884:b3f034f5000f > > > parent: 88882:19d81cc213d7 > > > user: R David Murray > > > date: Sat Feb 01 12:27:07 2014 -0500 > > > summary: > > > whatsnew: move of reload, update new windows-only ssl functions entry. > > > > > > files: > > > Doc/whatsnew/3.4.rst | 10 ++++++++-- > > > 1 files changed, 8 insertions(+), 2 deletions(-) > > > > > > > > > diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst > > > --- a/Doc/whatsnew/3.4.rst > > > +++ b/Doc/whatsnew/3.4.rst > > > @@ -765,6 +765,10 @@ > > > it will normally be desirable to override the default implementation > > > for performance reasons. (Contributed by Brett Cannon in :issue:`18072`.) > > > > > > +The :func:`~importlib.reload` function has been moved from :mod:`imp` > > > +to :mod:`importlib`. The :func:`mod.reload` name is retained for > > > +backward compatibility, but is deprecated. > > > + > > > > > > > That wording seems confusing to me. It makes it seem like importlib.reload > > is deprecated when in fact it's the imp module itself. > > I committed that thinking I'd finished working on that entry, but I > hadn't. The updated text reads: > > The reload() function has been moved from imp to importlib as part > of the imp module deprecation. > > Is that clearer? Yep! And thanks for catching the missing entry. -Brett > > --David -------------- next part -------------- An HTML attachment was scrubbed... URL: From python-checkins at python.org Sun Feb 2 21:31:25 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 2 Feb 2014 21:31:25 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_use_with_state?= =?utf-8?q?ment_to_ensure_zipfile_is_always_closed_=28closes_=2320102=29?= Message-ID: <3fHP2j4kWqz7Ljb@mail.python.org> http://hg.python.org/cpython/rev/767d034b3feb changeset: 88900:767d034b3feb branch: 2.7 parent: 88890:ae1c70135763 user: Benjamin Peterson date: Sun Feb 02 15:30:22 2014 -0500 summary: use with statement to ensure zipfile is always closed (closes #20102) files: Lib/shutil.py | 20 +++++++++----------- 1 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Lib/shutil.py b/Lib/shutil.py --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -446,17 +446,15 @@ zip_filename, base_dir) if not dry_run: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) - - for dirpath, dirnames, filenames in os.walk(base_dir): - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - if os.path.isfile(path): - zip.write(path, path) - if logger is not None: - logger.info("adding '%s'", path) - zip.close() + with zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) as zf: + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zf.write(path, path) + if logger is not None: + logger.info("adding '%s'", path) return zip_filename -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 21:31:26 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 2 Feb 2014 21:31:26 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_use_with_state?= =?utf-8?q?ment_to_ensure_zipfile_is_always_closed_=28closes_=2320102=29?= Message-ID: <3fHP2k6hhkz7Lkm@mail.python.org> http://hg.python.org/cpython/rev/838674d15b5b changeset: 88901:838674d15b5b branch: 3.3 parent: 88891:412a8e2c2af6 user: Benjamin Peterson date: Sun Feb 02 15:30:22 2014 -0500 summary: use with statement to ensure zipfile is always closed (closes #20102) files: Lib/shutil.py | 20 +++++++++----------- 1 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Lib/shutil.py b/Lib/shutil.py --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -680,17 +680,15 @@ zip_filename, base_dir) if not dry_run: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) - - for dirpath, dirnames, filenames in os.walk(base_dir): - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - if os.path.isfile(path): - zip.write(path, path) - if logger is not None: - logger.info("adding '%s'", path) - zip.close() + with zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) as zf: + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zf.write(path, path) + if logger is not None: + logger.info("adding '%s'", path) return zip_filename -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 21:31:28 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 2 Feb 2014 21:31:28 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy4zICgjMjAxMDIp?= Message-ID: <3fHP2m1LRpz7Llw@mail.python.org> http://hg.python.org/cpython/rev/20aa07f93ca7 changeset: 88902:20aa07f93ca7 parent: 88899:558b26b6a342 parent: 88901:838674d15b5b user: Benjamin Peterson date: Sun Feb 02 15:31:07 2014 -0500 summary: merge 3.3 (#20102) files: Lib/shutil.py | 20 +++++++++----------- 1 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Lib/shutil.py b/Lib/shutil.py --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -679,17 +679,15 @@ zip_filename, base_dir) if not dry_run: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) - - for dirpath, dirnames, filenames in os.walk(base_dir): - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - if os.path.isfile(path): - zip.write(path, path) - if logger is not None: - logger.info("adding '%s'", path) - zip.close() + with zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) as zf: + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zf.write(path, path) + if logger is not None: + logger.info("adding '%s'", path) return zip_filename -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 22:05:41 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 2 Feb 2014 22:05:41 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5MzIw?= =?utf-8?q?=3A_Fixed_split/splitlist_tests_in_test=5Ftcl_for_Tcl_8=2E5=2E0?= =?utf-8?b?LTguNS41Lg==?= Message-ID: <3fHPpF2ygzz7LkP@mail.python.org> http://hg.python.org/cpython/rev/515e6afd2f0f changeset: 88903:515e6afd2f0f branch: 2.7 parent: 88900:767d034b3feb user: Serhiy Storchaka date: Sun Feb 02 23:04:06 2014 +0200 summary: Issue #19320: Fixed split/splitlist tests in test_tcl for Tcl 8.5.0-8.5.5. files: Lib/test/test_tcl.py | 37 +++++++++++++++++++++++++++---- 1 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -19,6 +19,21 @@ pass tcl_version = tuple(tcl_version) +_tk_patchlevel = None +def get_tk_patchlevel(): + global _tk_patchlevel + if _tk_patchlevel is None: + tcl = Tcl() + patchlevel = [] + for x in tcl.call('info', 'patchlevel').split('.'): + try: + x = int(x, 10) + except ValueError: + x = -1 + patchlevel.append(x) + _tk_patchlevel = tuple(patchlevel) + return _tk_patchlevel + class TkinterTest(unittest.TestCase): @@ -272,10 +287,16 @@ ('1', '2', '3.4')), ] if tcl_version >= (8, 5): + if not self.wantobjects: + expected = ('12', '\xe2\x82\xac', '\xe2\x82\xac', '3.4') + elif get_tk_patchlevel() < (8, 5, 5): + # Before 8.5.5 dicts were converted to lists through string + expected = ('12', u'\u20ac', u'\u20ac', '3.4') + else: + expected = (12, u'\u20ac', u'\u20ac', (3.4,)) testcases += [ - (call('dict', 'create', 1, u'\u20ac', '\xe2\x82\xac', (3.4,)), - (1, u'\u20ac', u'\u20ac', (3.4,)) if self.wantobjects else - ('1', '\xe2\x82\xac', '\xe2\x82\xac', '3.4')), + (call('dict', 'create', 12, u'\u20ac', '\xe2\x82\xac', (3.4,)), + expected), ] for arg, res in testcases: self.assertEqual(splitlist(arg), res) @@ -312,10 +333,16 @@ ('1', '2', '3.4')), ] if tcl_version >= (8, 5): + if not self.wantobjects: + expected = ('12', '\xe2\x82\xac', '\xe2\x82\xac', '3.4') + elif get_tk_patchlevel() < (8, 5, 5): + # Before 8.5.5 dicts were converted to lists through string + expected = ('12', u'\u20ac', u'\u20ac', '3.4') + else: + expected = (12, u'\u20ac', u'\u20ac', (3.4,)) testcases += [ (call('dict', 'create', 12, u'\u20ac', '\xe2\x82\xac', (3.4,)), - (12, u'\u20ac', u'\u20ac', (3.4,)) if self.wantobjects else - ('12', '\xe2\x82\xac', '\xe2\x82\xac', '3.4')), + expected), ] for arg, res in testcases: self.assertEqual(split(arg), res) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 22:05:42 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 2 Feb 2014 22:05:42 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE5MzIw?= =?utf-8?q?=3A_Fixed_split/splitlist_tests_in_test=5Ftcl_for_Tcl_8=2E5=2E0?= =?utf-8?b?LTguNS41Lg==?= Message-ID: <3fHPpG4KWbz7Lly@mail.python.org> http://hg.python.org/cpython/rev/e6363a42a9f0 changeset: 88904:e6363a42a9f0 branch: 3.3 parent: 88901:838674d15b5b user: Serhiy Storchaka date: Sun Feb 02 23:04:24 2014 +0200 summary: Issue #19320: Fixed split/splitlist tests in test_tcl for Tcl 8.5.0-8.5.5. files: Lib/test/test_tcl.py | 33 +++++++++++++++++++++++++++---- 1 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -21,6 +21,21 @@ pass tcl_version = tuple(tcl_version) +_tk_patchlevel = None +def get_tk_patchlevel(): + global _tk_patchlevel + if _tk_patchlevel is None: + tcl = Tcl() + patchlevel = [] + for x in tcl.call('info', 'patchlevel').split('.'): + try: + x = int(x, 10) + except ValueError: + x = -1 + patchlevel.append(x) + _tk_patchlevel = tuple(patchlevel) + return _tk_patchlevel + class TkinterTest(unittest.TestCase): @@ -259,10 +274,14 @@ ('1', '2', '3.4')), ] if tcl_version >= (8, 5): + if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5): + # Before 8.5.5 dicts were converted to lists through string + expected = ('12', '\u20ac', '\u20ac', '3.4') + else: + expected = (12, '\u20ac', '\u20ac', (3.4,)) testcases += [ - (call('dict', 'create', 1, '\u20ac', b'\xe2\x82\xac', (3.4,)), - (1, '\u20ac', '\u20ac', (3.4,)) if self.wantobjects else - ('1', '\u20ac', '\u20ac', '3.4')), + (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), + expected), ] for arg, res in testcases: self.assertEqual(splitlist(arg), res, msg=arg) @@ -299,10 +318,14 @@ ('1', '2', '3.4')), ] if tcl_version >= (8, 5): + if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5): + # Before 8.5.5 dicts were converted to lists through string + expected = ('12', '\u20ac', '\u20ac', '3.4') + else: + expected = (12, '\u20ac', '\u20ac', (3.4,)) testcases += [ (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), - (12, '\u20ac', '\u20ac', (3.4,)) if self.wantobjects else - ('12', '\u20ac', '\u20ac', '3.4')), + expected), ] for arg, res in testcases: self.assertEqual(split(arg), res, msg=arg) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 22:05:43 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 2 Feb 2014 22:05:43 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2319320=3A_Fixed_split/splitlist_tests_in_test=5F?= =?utf-8?b?dGNsIGZvciBUY2wgOC41LjAtOC41LjUu?= Message-ID: <3fHPpH5gjSz7LmD@mail.python.org> http://hg.python.org/cpython/rev/f2fdafe73ab6 changeset: 88905:f2fdafe73ab6 parent: 88902:20aa07f93ca7 parent: 88904:e6363a42a9f0 user: Serhiy Storchaka date: Sun Feb 02 23:05:10 2014 +0200 summary: Issue #19320: Fixed split/splitlist tests in test_tcl for Tcl 8.5.0-8.5.5. files: Lib/test/test_tcl.py | 33 +++++++++++++++++++++++++++---- 1 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -21,6 +21,21 @@ pass tcl_version = tuple(tcl_version) +_tk_patchlevel = None +def get_tk_patchlevel(): + global _tk_patchlevel + if _tk_patchlevel is None: + tcl = Tcl() + patchlevel = [] + for x in tcl.call('info', 'patchlevel').split('.'): + try: + x = int(x, 10) + except ValueError: + x = -1 + patchlevel.append(x) + _tk_patchlevel = tuple(patchlevel) + return _tk_patchlevel + class TkinterTest(unittest.TestCase): @@ -259,10 +274,14 @@ ('1', '2', '3.4')), ] if tcl_version >= (8, 5): + if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5): + # Before 8.5.5 dicts were converted to lists through string + expected = ('12', '\u20ac', '\u20ac', '3.4') + else: + expected = (12, '\u20ac', '\u20ac', (3.4,)) testcases += [ - (call('dict', 'create', 1, '\u20ac', b'\xe2\x82\xac', (3.4,)), - (1, '\u20ac', '\u20ac', (3.4,)) if self.wantobjects else - ('1', '\u20ac', '\u20ac', '3.4')), + (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), + expected), ] for arg, res in testcases: self.assertEqual(splitlist(arg), res, msg=arg) @@ -299,10 +318,14 @@ ('1', '2', '3.4')), ] if tcl_version >= (8, 5): + if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5): + # Before 8.5.5 dicts were converted to lists through string + expected = ('12', '\u20ac', '\u20ac', '3.4') + else: + expected = (12, '\u20ac', '\u20ac', (3.4,)) testcases += [ (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), - (12, '\u20ac', '\u20ac', (3.4,)) if self.wantobjects else - ('12', '\u20ac', '\u20ac', '3.4')), + expected), ] for arg, res in testcases: self.assertEqual(split(arg), res, msg=arg) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 22:44:06 2014 From: python-checkins at python.org (victor.stinner) Date: Sun, 2 Feb 2014 22:44:06 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=3A_document_the_ne?= =?utf-8?q?w_asyncio=2Esubprocess_module?= Message-ID: <3fHQfZ46MCz7LjM@mail.python.org> http://hg.python.org/cpython/rev/0d3831bbc5f0 changeset: 88906:0d3831bbc5f0 user: Victor Stinner date: Sun Feb 02 22:43:39 2014 +0100 summary: asyncio: document the new asyncio.subprocess module files: Doc/library/asyncio-eventloop.rst | 5 + Doc/library/asyncio-stream.rst | 6 +- Doc/library/asyncio-subprocess.rst | 140 +++++++++++++++++ Doc/library/asyncio.rst | 1 + 4 files changed, 149 insertions(+), 3 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -353,6 +353,11 @@ This method returns a :ref:`coroutine object `. +.. seealso:: + + The :func:`create_subprocess_exec` and :func:`create_subprocess_shell` + functions. + UNIX signals ------------ diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -9,7 +9,7 @@ Stream functions ================ -.. function:: open_connection(host=None, port=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds) +.. function:: open_connection(host=None, port=None, \*, loop=None, limit=None, **kwds) A wrapper for :meth:`~BaseEventLoop.create_connection()` returning a (reader, writer) pair. @@ -32,7 +32,7 @@ This function returns a :ref:`coroutine object `. -.. function:: start_server(client_connected_cb, host=None, port=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds) +.. function:: start_server(client_connected_cb, host=None, port=None, \*, loop=None, limit=None, **kwds) Start a socket server, call back for each client connected. @@ -62,7 +62,7 @@ StreamReader ============ -.. class:: StreamReader(limit=_DEFAULT_LIMIT, loop=None) +.. class:: StreamReader(limit=None, loop=None) .. method:: exception() diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst new file mode 100644 --- /dev/null +++ b/Doc/library/asyncio-subprocess.rst @@ -0,0 +1,140 @@ +.. currentmodule:: asyncio + +Subprocess +========== + +Create a subproces +------------------ + +.. function:: create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) + + Run the shell command *cmd* (:class:`str`)`. Return a :class:`Process` + instance. + + This function returns a :ref:`coroutine object `. + +.. function:: create_subprocess_exec(\*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) + + Create a subprocess. Return a :class:`Process` instance. + + This function returns a :ref:`coroutine object `. + +Use the :meth:`BaseEventLoop.connect_read_pipe` and +:meth:`BaseEventLoop.connect_write_pipe` methods to connect pipes. + +.. seealso:: + + The :meth:`BaseEventLoop.subprocess_exec` and + :meth:`BaseEventLoop.subprocess_shell` methods. + + +Constants +--------- + +.. data:: asyncio.subprocess.PIPE + + Special value that can be used as the *stdin*, *stdout* or *stderr* argument + to :func:`create_subprocess_shell` and :func:`create_subprocess_exec` and + indicates that a pipe to the standard stream should be opened. + +.. data:: asyncio.subprocess.STDOUT + + Special value that can be used as the *stderr* argument to + :func:`create_subprocess_shell` and :func:`create_subprocess_exec` and + indicates that standard error should go into the same handle as standard + output. + +.. data:: asyncio.subprocess.DEVNULL + + Special value that can be used as the *stderr* argument to + :func:`create_subprocess_shell` and :func:`create_subprocess_exec` and + indicates that standard error should go into the same handle as standard + output. + + +Process +------- + +.. class:: asyncio.subprocess.Process + + .. attribute:: stdin + + Standard input stream (write), ``None`` if the process was created with + ``stdin=None``. + + .. attribute:: stdout + + Standard output stream (read), ``None`` if the process was created with + ``stdout=None``. + + .. attribute:: stderr + + Standard error stream (read), ``None`` if the process was created with + ``stderr=None``. + + .. attribute:: pid + + The identifier of the process. + + Note that if you set the *shell* argument to ``True``, this is the + process identifier of the spawned shell. + + .. attribute:: returncode + + Return code of the process when it exited. A ``None`` value indicates + that the process has not terminated yet. + + A negative value ``-N`` indicates that the child was terminated by signal + ``N`` (Unix only). + + .. method:: communicate(input=None) + + Interact with process: Send data to stdin. Read data from stdout and + stderr, until end-of-file is reached. Wait for process to terminate. + The optional *input* argument should be data to be sent to the child + process, or ``None``, if no data should be sent to the child. The type + of *input* must be bytes. + + :meth:`communicate` returns a tuple ``(stdoutdata, stderrdata)``. + + Note that if you want to send data to the process's stdin, you need to + create the Popen object with ``stdin=PIPE``. Similarly, to get anything + other than ``None`` in the result tuple, you need to give ``stdout=PIPE`` + and/or ``stderr=PIPE`` too. + + .. note:: + + The data read is buffered in memory, so do not use this method if the + data size is large or unlimited. + + .. method:: get_subprocess() + + Get the underlying :class:`subprocess.Popen` object. + + .. method:: kill() + + Kills the child. On Posix OSs the function sends :py:data:`SIGKILL` to + the child. On Windows :meth:`kill` is an alias for :meth:`terminate`. + + .. method:: send_signal(signale) + + Sends the signal *signal* to the child process. + + .. note:: + + On Windows, :py:data:`SIGTERM` is an alias for :meth:`terminate`. + ``CTRL_C_EVENT`` and ``CTRL_BREAK_EVENT`` can be sent to processes + started with a *creationflags* parameter which includes + ``CREATE_NEW_PROCESS_GROUP``. + + .. method:: terminate() + + Stop the child. On Posix OSs the method sends :py:data:`signal.SIGTERM` + to the child. On Windows the Win32 API function + :c:func:`TerminateProcess` is called to stop the child. + + .. method:: wait(self): + + Wait for child process to terminate. Set and return :attr:`returncode` + attribute. + diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -48,6 +48,7 @@ asyncio-task.rst asyncio-protocol.rst asyncio-stream.rst + asyncio-subprocess.rst asyncio-sync.rst asyncio-dev.rst -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 22:51:53 2014 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 2 Feb 2014 22:51:53 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNDIz?= =?utf-8?q?=3A_fix_documentation_of_io=2EStringIO=27s_newline_parameter?= Message-ID: <3fHQqY3zm2z7LjM@mail.python.org> http://hg.python.org/cpython/rev/82cfab2ad98d changeset: 88907:82cfab2ad98d branch: 3.3 parent: 88904:e6363a42a9f0 user: Antoine Pitrou date: Sun Feb 02 22:48:25 2014 +0100 summary: Issue #20423: fix documentation of io.StringIO's newline parameter files: Doc/library/io.rst | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -844,13 +844,14 @@ Whether line buffering is enabled. -.. class:: StringIO(initial_value='', newline=None) +.. class:: StringIO(initial_value='', newline='\\n') An in-memory stream for text I/O. The initial value of the buffer (an empty string by default) can be set by providing *initial_value*. The *newline* argument works like that of - :class:`TextIOWrapper`. The default is to do no newline translation. + :class:`TextIOWrapper`. The default is to consider only ``\n`` characters + as end of lines and to do no newline translation. :class:`StringIO` provides this method in addition to those from :class:`TextIOBase` and its parents: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 22:51:54 2014 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 2 Feb 2014 22:51:54 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNDIz?= =?utf-8?q?=3A_fix_documentation_of_io=2EStringIO=27s_newline_parameter?= Message-ID: <3fHQqZ5L0yz7LlN@mail.python.org> http://hg.python.org/cpython/rev/69a2cc048c80 changeset: 88908:69a2cc048c80 branch: 2.7 parent: 88903:515e6afd2f0f user: Antoine Pitrou date: Sun Feb 02 22:48:25 2014 +0100 summary: Issue #20423: fix documentation of io.StringIO's newline parameter files: Doc/library/io.rst | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -791,14 +791,14 @@ Whether line buffering is enabled. -.. class:: StringIO(initial_value=u'', newline=None) +.. class:: StringIO(initial_value=u'', newline=u'\\n') An in-memory stream for unicode text. It inherits :class:`TextIOWrapper`. The initial value of the buffer (an empty unicode string by default) can be set by providing *initial_value*. The *newline* argument works like - that of :class:`TextIOWrapper`. The default is to do no newline - translation. + that of :class:`TextIOWrapper`. The default is to consider only ``\n`` + characters as end of lines and to do no newline translation. :class:`StringIO` provides this method in addition to those from :class:`TextIOWrapper` and its parents: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 22:51:55 2014 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 2 Feb 2014 22:51:55 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320423=3A_fix_documentation_of_io=2EStringIO=27s?= =?utf-8?q?_newline_parameter?= Message-ID: <3fHQqb6h6vz7Ll2@mail.python.org> http://hg.python.org/cpython/rev/df2efd48227e changeset: 88909:df2efd48227e parent: 88906:0d3831bbc5f0 parent: 88907:82cfab2ad98d user: Antoine Pitrou date: Sun Feb 02 22:49:03 2014 +0100 summary: Issue #20423: fix documentation of io.StringIO's newline parameter files: Doc/library/io.rst | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -849,13 +849,14 @@ Whether line buffering is enabled. -.. class:: StringIO(initial_value='', newline=None) +.. class:: StringIO(initial_value='', newline='\\n') An in-memory stream for text I/O. The initial value of the buffer (an empty string by default) can be set by providing *initial_value*. The *newline* argument works like that of - :class:`TextIOWrapper`. The default is to do no newline translation. + :class:`TextIOWrapper`. The default is to consider only ``\n`` characters + as end of lines and to do no newline translation. :class:`StringIO` provides this method in addition to those from :class:`TextIOBase` and its parents: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 23:01:29 2014 From: python-checkins at python.org (ned.deily) Date: Sun, 2 Feb 2014 23:01:29 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5OTkw?= =?utf-8?q?=3A_Install_test/imghdrdata=2E?= Message-ID: <3fHR2d15Sgz7LjM@mail.python.org> http://hg.python.org/cpython/rev/e2d013e90e88 changeset: 88910:e2d013e90e88 branch: 2.7 parent: 88908:69a2cc048c80 user: Ned Deily date: Sun Feb 02 13:59:26 2014 -0800 summary: Issue #19990: Install test/imghdrdata. files: Makefile.pre.in | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -931,7 +931,9 @@ PLATMACPATH=:plat-mac:plat-mac/lib-scriptpackages LIBSUBDIRS= lib-tk lib-tk/test lib-tk/test/test_tkinter \ lib-tk/test/test_ttk site-packages test test/audiodata test/data \ - test/cjkencodings test/decimaltestdata test/xmltestdata test/subprocessdata \ + test/cjkencodings test/decimaltestdata test/xmltestdata \ + test/imghdrdata \ + test/subprocessdata \ test/tracedmodules \ encodings compiler hotshot \ email email/mime email/test email/test/data \ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 23:01:30 2014 From: python-checkins at python.org (ned.deily) Date: Sun, 2 Feb 2014 23:01:30 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE5OTkw?= =?utf-8?q?=3A_Install_test/imghdrdata=2E?= Message-ID: <3fHR2f2vTtz7LjM@mail.python.org> http://hg.python.org/cpython/rev/5fbe155e62b0 changeset: 88911:5fbe155e62b0 branch: 3.3 parent: 88907:82cfab2ad98d user: Ned Deily date: Sun Feb 02 13:59:49 2014 -0800 summary: Issue #19990: Install test/imghdrdata. files: Makefile.pre.in | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1019,6 +1019,7 @@ test/audiodata \ test/capath test/data \ test/cjkencodings test/decimaltestdata test/xmltestdata \ + test/imghdrdata \ test/subprocessdata test/sndhdrdata test/support \ test/tracedmodules test/encoded_modules \ test/namespace_pkgs \ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 23:01:31 2014 From: python-checkins at python.org (ned.deily) Date: Sun, 2 Feb 2014 23:01:31 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2319990=3A_Install_test/imghdrdata=2E?= Message-ID: <3fHR2g4dv8z7LjX@mail.python.org> http://hg.python.org/cpython/rev/b41ba99a276c changeset: 88912:b41ba99a276c parent: 88909:df2efd48227e parent: 88911:5fbe155e62b0 user: Ned Deily date: Sun Feb 02 14:00:39 2014 -0800 summary: Issue #19990: Install test/imghdrdata. files: Makefile.pre.in | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1116,6 +1116,7 @@ test/audiodata \ test/capath test/data \ test/cjkencodings test/decimaltestdata test/xmltestdata \ + test/imghdrdata \ test/subprocessdata test/sndhdrdata test/support \ test/tracedmodules test/encoded_modules \ test/namespace_pkgs \ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 23:40:57 2014 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 2 Feb 2014 23:40:57 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNDM1?= =?utf-8?q?=3A_Fix_=5Fpyio=2EStringIO=2Egetvalue=28=29_to_take_into_accoun?= =?utf-8?q?t_newline?= Message-ID: <3fHRw93Nyhz7LkG@mail.python.org> http://hg.python.org/cpython/rev/99168e7d4a3d changeset: 88913:99168e7d4a3d branch: 3.3 parent: 88911:5fbe155e62b0 user: Antoine Pitrou date: Sun Feb 02 23:37:29 2014 +0100 summary: Issue #20435: Fix _pyio.StringIO.getvalue() to take into account newline translation settings. files: Lib/_pyio.py | 8 +++++++- Lib/test/test_memoryio.py | 9 +++++++++ Misc/NEWS | 3 +++ 3 files changed, 19 insertions(+), 1 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -2060,7 +2060,13 @@ def getvalue(self): self.flush() - return self.buffer.getvalue().decode(self._encoding, self._errors) + decoder = self._decoder or self._get_decoder() + old_state = decoder.getstate() + decoder.reset() + try: + return decoder.decode(self.buffer.getvalue(), final=True) + finally: + decoder.setstate(old_state) def __repr__(self): # TextIOWrapper tells the encoding in its repr. In StringIO, diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -551,6 +551,7 @@ self.assertEqual(3, memio.write("c\rd")) memio.seek(0) self.assertEqual(memio.read(), "a\nb\nc\nd") + self.assertEqual(memio.getvalue(), "a\nb\nc\nd") memio = self.ioclass("a\r\nb", newline=None) self.assertEqual(memio.read(3), "a\nb") @@ -562,6 +563,7 @@ self.assertEqual(memio.read(4), "a\nb\r") self.assertEqual(memio.read(2), "\nc") self.assertEqual(memio.read(1), "\r") + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") memio = self.ioclass(newline="") self.assertEqual(2, memio.write("a\n")) self.assertEqual(2, memio.write("b\r")) @@ -581,6 +583,9 @@ self.assertEqual(memio.read(), "a\rb\r\rc\rd") memio.seek(0) self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"]) + memio.seek(0) + self.assertEqual(memio.readlines(), ["a\r", "b\r", "\r", "c\r", "d"]) + self.assertEqual(memio.getvalue(), "a\rb\r\rc\rd") def test_newline_crlf(self): # newline="\r\n" @@ -588,11 +593,15 @@ self.assertEqual(memio.read(), "a\r\nb\r\r\nc\rd") memio.seek(0) self.assertEqual(list(memio), ["a\r\n", "b\r\r\n", "c\rd"]) + memio.seek(0) + self.assertEqual(memio.readlines(), ["a\r\n", "b\r\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\r\nb\r\r\nc\rd") def test_issue5265(self): # StringIO can duplicate newlines in universal newlines mode memio = self.ioclass("a\r\nb\r\n", newline=None) self.assertEqual(memio.read(5), "a\nb\n") + self.assertEqual(memio.getvalue(), "a\nb\n") def test_newline_argument(self): self.assertRaises(TypeError, self.ioclass, newline=b"\n") diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,9 @@ Library ------- +- Issue #20435: Fix _pyio.StringIO.getvalue() to take into account newline + translation settings. + - Issue #20288: fix handling of invalid numeric charrefs in HTMLParser. - Issue #20424: Python implementation of io.StringIO now supports lone surrogates. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 23:40:58 2014 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 2 Feb 2014 23:40:58 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogSXNzdWUgIzIwNDM1OiBGaXggX3B5aW8uU3RyaW5nSU8uZ2V0dmFsdWUo?= =?utf-8?q?=29_to_take_into_account_newline?= Message-ID: <3fHRwB51j8z7LlG@mail.python.org> http://hg.python.org/cpython/rev/aadcc71a4967 changeset: 88914:aadcc71a4967 parent: 88912:b41ba99a276c parent: 88913:99168e7d4a3d user: Antoine Pitrou date: Sun Feb 02 23:38:48 2014 +0100 summary: Issue #20435: Fix _pyio.StringIO.getvalue() to take into account newline translation settings. files: Lib/_pyio.py | 8 +++++++- Lib/test/test_memoryio.py | 9 +++++++++ Misc/NEWS | 3 +++ 3 files changed, 19 insertions(+), 1 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -2067,7 +2067,13 @@ def getvalue(self): self.flush() - return self.buffer.getvalue().decode(self._encoding, self._errors) + decoder = self._decoder or self._get_decoder() + old_state = decoder.getstate() + decoder.reset() + try: + return decoder.decode(self.buffer.getvalue(), final=True) + finally: + decoder.setstate(old_state) def __repr__(self): # TextIOWrapper tells the encoding in its repr. In StringIO, diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -551,6 +551,7 @@ self.assertEqual(3, memio.write("c\rd")) memio.seek(0) self.assertEqual(memio.read(), "a\nb\nc\nd") + self.assertEqual(memio.getvalue(), "a\nb\nc\nd") memio = self.ioclass("a\r\nb", newline=None) self.assertEqual(memio.read(3), "a\nb") @@ -562,6 +563,7 @@ self.assertEqual(memio.read(4), "a\nb\r") self.assertEqual(memio.read(2), "\nc") self.assertEqual(memio.read(1), "\r") + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") memio = self.ioclass(newline="") self.assertEqual(2, memio.write("a\n")) self.assertEqual(2, memio.write("b\r")) @@ -581,6 +583,9 @@ self.assertEqual(memio.read(), "a\rb\r\rc\rd") memio.seek(0) self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"]) + memio.seek(0) + self.assertEqual(memio.readlines(), ["a\r", "b\r", "\r", "c\r", "d"]) + self.assertEqual(memio.getvalue(), "a\rb\r\rc\rd") def test_newline_crlf(self): # newline="\r\n" @@ -588,11 +593,15 @@ self.assertEqual(memio.read(), "a\r\nb\r\r\nc\rd") memio.seek(0) self.assertEqual(list(memio), ["a\r\n", "b\r\r\n", "c\rd"]) + memio.seek(0) + self.assertEqual(memio.readlines(), ["a\r\n", "b\r\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\r\nb\r\r\nc\rd") def test_issue5265(self): # StringIO can duplicate newlines in universal newlines mode memio = self.ioclass("a\r\nb\r\n", newline=None) self.assertEqual(memio.read(5), "a\nb\n") + self.assertEqual(memio.getvalue(), "a\nb\n") def test_newline_argument(self): self.assertRaises(TypeError, self.ioclass, newline=b"\n") diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,9 @@ Library ------- +- Issue #20435: Fix _pyio.StringIO.getvalue() to take into account newline + translation settings. + - tracemalloc: Fix slicing traces and fix slicing a traceback. - Issue #20354: Fix an alignment issue in the tracemalloc module on 64-bit -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 2 23:42:04 2014 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 2 Feb 2014 23:42:04 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNDM1?= =?utf-8?q?=3A_Fix_=5Fpyio=2EStringIO=2Egetvalue=28=29_to_take_into_accoun?= =?utf-8?q?t_newline?= Message-ID: <3fHRxS6ySSz7LkG@mail.python.org> http://hg.python.org/cpython/rev/3e61d8e06ef7 changeset: 88915:3e61d8e06ef7 branch: 2.7 parent: 88910:e2d013e90e88 user: Antoine Pitrou date: Sun Feb 02 23:37:29 2014 +0100 summary: Issue #20435: Fix _pyio.StringIO.getvalue() to take into account newline translation settings. files: Lib/_pyio.py | 8 +++++++- Lib/test/test_memoryio.py | 9 +++++++++ Misc/NEWS | 3 +++ 3 files changed, 19 insertions(+), 1 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1997,7 +1997,13 @@ def getvalue(self): self.flush() - return self.buffer.getvalue().decode(self._encoding, self._errors) + decoder = self._decoder or self._get_decoder() + old_state = decoder.getstate() + decoder.reset() + try: + return decoder.decode(self.buffer.getvalue(), final=True) + finally: + decoder.setstate(old_state) def __repr__(self): # TextIOWrapper tells the encoding in its repr. In StringIO, diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -537,6 +537,7 @@ self.assertEqual(3, memio.write("c\rd")) memio.seek(0) self.assertEqual(memio.read(), "a\nb\nc\nd") + self.assertEqual(memio.getvalue(), "a\nb\nc\nd") memio = self.ioclass("a\r\nb", newline=None) self.assertEqual(memio.read(3), "a\nb") @@ -548,6 +549,7 @@ self.assertEqual(memio.read(4), "a\nb\r") self.assertEqual(memio.read(2), "\nc") self.assertEqual(memio.read(1), "\r") + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") memio = self.ioclass(newline="") self.assertEqual(2, memio.write("a\n")) self.assertEqual(2, memio.write("b\r")) @@ -567,6 +569,9 @@ self.assertEqual(memio.read(), "a\rb\r\rc\rd") memio.seek(0) self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"]) + memio.seek(0) + self.assertEqual(memio.readlines(), ["a\r", "b\r", "\r", "c\r", "d"]) + self.assertEqual(memio.getvalue(), "a\rb\r\rc\rd") def test_newline_crlf(self): # newline="\r\n" @@ -574,11 +579,15 @@ self.assertEqual(memio.read(), "a\r\nb\r\r\nc\rd") memio.seek(0) self.assertEqual(list(memio), ["a\r\n", "b\r\r\n", "c\rd"]) + memio.seek(0) + self.assertEqual(memio.readlines(), ["a\r\n", "b\r\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\r\nb\r\r\nc\rd") def test_issue5265(self): # StringIO can duplicate newlines in universal newlines mode memio = self.ioclass("a\r\nb\r\n", newline=None) self.assertEqual(memio.read(5), "a\nb\n") + self.assertEqual(memio.getvalue(), "a\nb\n") class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin, diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -38,6 +38,9 @@ Library ------- +- Issue #20435: Fix _pyio.StringIO.getvalue() to take into account newline + translation settings. + - Issue #20288: fix handling of invalid numeric charrefs in HTMLParser. - Issue #19456: ntpath.join() now joins relative paths correctly when a drive -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 00:37:21 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 3 Feb 2014 00:37:21 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320472=3A_test=5Fa?= =?utf-8?q?syncio=3A_skip_PTY_tests_on_Mac_OS_X_older_than_10=2E6?= Message-ID: <3fHT9F3C54z7Ljf@mail.python.org> http://hg.python.org/cpython/rev/21a4ebf5a170 changeset: 88916:21a4ebf5a170 parent: 88914:aadcc71a4967 user: Victor Stinner date: Mon Feb 03 00:32:13 2014 +0100 summary: Issue #20472: test_asyncio: skip PTY tests on Mac OS X older than 10.6 files: Lib/test/test_asyncio/test_events.py | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -953,6 +953,9 @@ @unittest.skipUnless(sys.platform != 'win32', "Don't support pipes for Windows") + # select, poll and kqueue don't support character devices (PTY) on Mac OS X + # older than 10.6 (Snow Leopard) + @support.requires_mac_ver(10, 6) def test_read_pty_output(self): proto = None @@ -1075,6 +1078,9 @@ @unittest.skipUnless(sys.platform != 'win32', "Don't support pipes for Windows") + # select, poll and kqueue don't support character devices (PTY) on Mac OS X + # older than 10.6 (Snow Leopard) + @support.requires_mac_ver(10, 6) def test_write_pty(self): proto = None transport = None -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 00:37:22 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 3 Feb 2014 00:37:22 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320472=3A_asyncio?= =?utf-8?q?=3A_Adjust_the_note_about_Mac_OS_X_on_PTY=2C_specify_that_it?= Message-ID: <3fHT9G4jTYz7LkD@mail.python.org> http://hg.python.org/cpython/rev/0a71d29b970a changeset: 88917:0a71d29b970a user: Victor Stinner date: Mon Feb 03 00:35:46 2014 +0100 summary: Issue #20472: asyncio: Adjust the note about Mac OS X on PTY, specify that it requires at least Mac OS X 10.6. files: Doc/library/asyncio-eventloop.rst | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -313,7 +313,8 @@ On Mac OS X older than 10.9 (Mavericks), :class:`selectors.KqueueSelector` does not support character devices like PTY, whereas it is used by the default event loop. The :class:`SelectorEventLoop` can be used with - :class:`SelectSelector` to handle character devices. + :class:`SelectSelector` or :class:`PollSelector` to handle character devices + on Mac OS X 10.6 (Snow Leopard) and later. .. method:: BaseEventLoop.subprocess_exec(protocol_factory, \*args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False, shell=False, bufsize=0, \*\*kwargs) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 07:33:53 2014 From: python-checkins at python.org (r.david.murray) Date: Mon, 3 Feb 2014 07:33:53 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_whatsnew=3A_html=2Eescape_?= =?utf-8?q?10x_faster=2C_=5Fgestalt_module_gone=2E?= Message-ID: <3fHfPs6Gfmz7Lk7@mail.python.org> http://hg.python.org/cpython/rev/c2a21098fe7f changeset: 88918:c2a21098fe7f user: R David Murray date: Mon Feb 03 01:14:03 2014 -0500 summary: whatsnew: html.escape 10x faster, _gestalt module gone. files: Doc/whatsnew/3.4.rst | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -1448,6 +1448,8 @@ significant amounts of data through :mod:`subprocess`. (Contributed by Richard Oudkerk in :issue:`15758`.) +* :func:`html.escape` is now 10x faster. (Contributed by Matt Bryant in + :issue:`18020`.) Deprecated @@ -1559,6 +1561,11 @@ * The unused and undocumented internal ``Scanner`` class has been removed from the :mod:`pydoc` module. +* The private and effectively unused ``_gestalt`` module has been removed, + along with the private :mod:`platform` functions ``_mac_ver_lookup``, + ``_mac_ver_gstalt``, and ``_bcd2str``, which would only have ever been called + on badly broken OSX systems (see :issue:`18393`). + Porting to Python 3.4 @@ -1608,7 +1615,7 @@ exceptions now. * :func:`functools.update_wrapper` and :func:`functools.wraps` now correctly - set the ``__wrapped__`` attribute to the function being wrapper, even if + set the ``__wrapped__`` attribute to the function being wrapped, even if that function also had its ``__wrapped__`` attribute set. This means ``__wrapped__`` attributes now correctly link a stack of decorated functions rather than every ``__wrapped__`` attribute in the chain -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 07:33:55 2014 From: python-checkins at python.org (r.david.murray) Date: Mon, 3 Feb 2014 07:33:55 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_whatsnew=3A_read/write_on_?= =?utf-8?q?closed_SSL_socket_exception_has_changed=2E?= Message-ID: <3fHfPv0sLZz7Lm8@mail.python.org> http://hg.python.org/cpython/rev/33bfe77aca56 changeset: 88919:33bfe77aca56 user: R David Murray date: Mon Feb 03 01:33:39 2014 -0500 summary: whatsnew: read/write on closed SSL socket exception has changed. files: Doc/whatsnew/3.4.rst | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -543,6 +543,7 @@ The number of digits in the coefficients for the RGB --- YIQ conversions have been expanded so that they match the FCC NTSC versions. The change in results should be less than 1% and may better match results found elsewhere. +(Contributed by Brian Landers and Serhiy Storchaka in :issue:`14323`.) contextlib @@ -694,7 +695,6 @@ for normal callables. The new descriptor also makes it easier to get arbitrary callables (including :func:`~functools.partial` instances) to behave like normal instance methods when included in a class definition. - (Contributed by Alon Horev and Nick Coghlan in :issue:`4331`) .. _whatsnew-singledispatch: @@ -1665,6 +1665,10 @@ entire :class:`cgi.FieldStorage` instance or read the contents of the file before the :class:`cgi.FieldStorage` instance is garbage collected. +* Calling ``read`` or ``write`` on a closed SSL socket now raises an + informative :exc:`ValueError` rather than the previous more mysterious + :exc:`AttributeError` (:issue:`9177`). + Changes in the C API -------------------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 08:48:26 2014 From: python-checkins at python.org (yury.selivanov) Date: Mon, 3 Feb 2014 08:48:26 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_inspect=2Esignature=3A_Add?= =?utf-8?q?_=28restore=29_support_for_builtin_classes_=2320473?= Message-ID: <3fHh3t5bj2z7LkN@mail.python.org> http://hg.python.org/cpython/rev/c19f5e4fdbe0 changeset: 88920:c19f5e4fdbe0 user: Yury Selivanov date: Mon Feb 03 02:46:07 2014 -0500 summary: inspect.signature: Add (restore) support for builtin classes #20473 files: Lib/inspect.py | 295 +++++++++++++++----------- Lib/test/test_inspect.py | 26 ++ 2 files changed, 190 insertions(+), 131 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1648,6 +1648,139 @@ return spec[2:pos] +def _signature_fromstr(cls, obj, s): + # Internal helper to parse content of '__text_signature__' + # and return a Signature based on it + Parameter = cls._parameter_cls + + if s.endswith("/)"): + kind = Parameter.POSITIONAL_ONLY + s = s[:-2] + ')' + else: + kind = Parameter.POSITIONAL_OR_KEYWORD + + first_parameter_is_self = s.startswith("($") + if first_parameter_is_self: + s = '(' + s[2:] + + s = "def foo" + s + ": pass" + + try: + module = ast.parse(s) + except SyntaxError: + module = None + + if not isinstance(module, ast.Module): + raise ValueError("{!r} builtin has invalid signature".format(obj)) + + f = module.body[0] + + parameters = [] + empty = Parameter.empty + invalid = object() + + module = None + module_dict = {} + module_name = getattr(obj, '__module__', None) + if module_name: + module = sys.modules.get(module_name, None) + if module: + module_dict = module.__dict__ + sys_module_dict = sys.modules + + def parse_name(node): + assert isinstance(node, ast.arg) + if node.annotation != None: + raise ValueError("Annotations are not currently supported") + return node.arg + + def wrap_value(s): + try: + value = eval(s, module_dict) + except NameError: + try: + value = eval(s, sys_module_dict) + except NameError: + raise RuntimeError() + + if isinstance(value, str): + return ast.Str(value) + if isinstance(value, (int, float)): + return ast.Num(value) + if isinstance(value, bytes): + return ast.Bytes(value) + if value in (True, False, None): + return ast.NameConstant(value) + raise RuntimeError() + + class RewriteSymbolics(ast.NodeTransformer): + def visit_Attribute(self, node): + a = [] + n = node + while isinstance(n, ast.Attribute): + a.append(n.attr) + n = n.value + if not isinstance(n, ast.Name): + raise RuntimeError() + a.append(n.id) + value = ".".join(reversed(a)) + return wrap_value(value) + + def visit_Name(self, node): + if not isinstance(node.ctx, ast.Load): + raise ValueError() + return wrap_value(node.id) + + def p(name_node, default_node, default=empty): + name = parse_name(name_node) + if name is invalid: + return None + if default_node and default_node is not _empty: + try: + default_node = RewriteSymbolics().visit(default_node) + o = ast.literal_eval(default_node) + except ValueError: + o = invalid + if o is invalid: + return None + default = o if o is not invalid else default + parameters.append(Parameter(name, kind, default=default, annotation=empty)) + + # non-keyword-only parameters + args = reversed(f.args.args) + defaults = reversed(f.args.defaults) + iter = itertools.zip_longest(args, defaults, fillvalue=None) + for name, default in reversed(list(iter)): + p(name, default) + + # *args + if f.args.vararg: + kind = Parameter.VAR_POSITIONAL + p(f.args.vararg, empty) + + # keyword-only arguments + kind = Parameter.KEYWORD_ONLY + for name, default in zip(f.args.kwonlyargs, f.args.kw_defaults): + p(name, default) + + # **kwargs + if f.args.kwarg: + kind = Parameter.VAR_KEYWORD + p(f.args.kwarg, empty) + + if first_parameter_is_self: + assert parameters + if getattr(obj, '__self__', None): + # strip off self, it's already been bound + parameters.pop(0) + else: + # for builtins, self parameter is always positional-only! + p = parameters[0].replace(kind=Parameter.POSITIONAL_ONLY) + parameters[0] = p + + return cls(parameters, return_annotation=cls.empty) + + def signature(obj): '''Get a signature object for the passed callable.''' @@ -1725,14 +1858,41 @@ sig = signature(init) if sig is None: + # At this point we know, that `obj` is a class, with no user- + # defined '__init__', '__new__', or class-level '__call__' + + for base in obj.__mro__: + # Since '__text_signature__' is implemented as a + # descriptor that extracts text signature from the + # class docstring, if 'obj' is derived from a builtin + # class, its own '__text_signature__' may be 'None'. + # Therefore, we go through the MRO to find the first + # class with non-empty text signature. + try: + text_sig = base.__text_signature__ + except AttributeError: + pass + else: + if text_sig: + # If 'obj' class has a __text_signature__ attribute: + # return a signature based on it + return _signature_fromstr(Signature, obj, text_sig) + + # No '__text_signature__' was found for the 'obj' class. + # Last option is to check if its '__init__' is + # object.__init__ or type.__init__. if type in obj.__mro__: # 'obj' is a metaclass without user-defined __init__ - # or __new__. Return a signature of 'type' builtin. - return signature(type) + # or __new__. + if obj.__init__ is type.__init__: + # Return a signature of 'type' builtin. + return signature(type) else: # We have a class (not metaclass), but no user-defined # __init__ or __new__ for it - return signature(object) + if obj.__init__ is object.__init__: + # Return a signature of 'object' builtin. + return signature(object) elif not isinstance(obj, _NonUserDefinedCallables): # An object with __call__ @@ -2196,134 +2356,7 @@ if not s: raise ValueError("no signature found for builtin {!r}".format(func)) - Parameter = cls._parameter_cls - - if s.endswith("/)"): - kind = Parameter.POSITIONAL_ONLY - s = s[:-2] + ')' - else: - kind = Parameter.POSITIONAL_OR_KEYWORD - - first_parameter_is_self = s.startswith("($") - if first_parameter_is_self: - s = '(' + s[2:] - - s = "def foo" + s + ": pass" - - try: - module = ast.parse(s) - except SyntaxError: - module = None - - if not isinstance(module, ast.Module): - raise ValueError("{!r} builtin has invalid signature".format(func)) - - f = module.body[0] - - parameters = [] - empty = Parameter.empty - invalid = object() - - module = None - module_dict = {} - module_name = getattr(func, '__module__', None) - if module_name: - module = sys.modules.get(module_name, None) - if module: - module_dict = module.__dict__ - sys_module_dict = sys.modules - - def parse_name(node): - assert isinstance(node, ast.arg) - if node.annotation != None: - raise ValueError("Annotations are not currently supported") - return node.arg - - def wrap_value(s): - try: - value = eval(s, module_dict) - except NameError: - try: - value = eval(s, sys_module_dict) - except NameError: - raise RuntimeError() - - if isinstance(value, str): - return ast.Str(value) - if isinstance(value, (int, float)): - return ast.Num(value) - if isinstance(value, bytes): - return ast.Bytes(value) - if value in (True, False, None): - return ast.NameConstant(value) - raise RuntimeError() - - class RewriteSymbolics(ast.NodeTransformer): - def visit_Attribute(self, node): - a = [] - n = node - while isinstance(n, ast.Attribute): - a.append(n.attr) - n = n.value - if not isinstance(n, ast.Name): - raise RuntimeError() - a.append(n.id) - value = ".".join(reversed(a)) - return wrap_value(value) - - def visit_Name(self, node): - if not isinstance(node.ctx, ast.Load): - raise ValueError() - return wrap_value(node.id) - - def p(name_node, default_node, default=empty): - name = parse_name(name_node) - if name is invalid: - return None - if default_node and default_node is not _empty: - try: - default_node = RewriteSymbolics().visit(default_node) - o = ast.literal_eval(default_node) - except ValueError: - o = invalid - if o is invalid: - return None - default = o if o is not invalid else default - parameters.append(Parameter(name, kind, default=default, annotation=empty)) - - # non-keyword-only parameters - args = reversed(f.args.args) - defaults = reversed(f.args.defaults) - iter = itertools.zip_longest(args, defaults, fillvalue=None) - for name, default in reversed(list(iter)): - p(name, default) - - # *args - if f.args.vararg: - kind = Parameter.VAR_POSITIONAL - p(f.args.vararg, empty) - - # keyword-only arguments - kind = Parameter.KEYWORD_ONLY - for name, default in zip(f.args.kwonlyargs, f.args.kw_defaults): - p(name, default) - - # **kwargs - if f.args.kwarg: - kind = Parameter.VAR_KEYWORD - p(f.args.kwarg, empty) - - if first_parameter_is_self: - assert parameters - if getattr(func, '__self__', None): - # strip off self, it's already been bound - parameters.pop(0) - else: - # for builtins, self parameter is always positional-only! - p = parameters[0].replace(kind=Parameter.POSITIONAL_ONLY) - parameters[0] = p - - return cls(parameters, return_annotation=cls.empty) + return _signature_fromstr(cls, func, s) @property def parameters(self): 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 @@ -2210,6 +2210,32 @@ self.assertEqual(str(inspect.signature(D)), '(object_or_name, bases, dict)') + @unittest.skipIf(MISSING_C_DOCSTRINGS, + "Signature information for builtins requires docstrings") + def test_signature_on_builtin_class(self): + self.assertEqual(str(inspect.signature(_pickle.Pickler)), + '(file, protocol=None, fix_imports=True)') + + class P(_pickle.Pickler): pass + class EmptyTrait: pass + class P2(EmptyTrait, P): pass + self.assertEqual(str(inspect.signature(P)), + '(file, protocol=None, fix_imports=True)') + self.assertEqual(str(inspect.signature(P2)), + '(file, protocol=None, fix_imports=True)') + + class P3(P2): + def __init__(self, spam): + pass + self.assertEqual(str(inspect.signature(P3)), '(spam)') + + class MetaP(type): + def __call__(cls, foo, bar): + pass + class P4(P2, metaclass=MetaP): + pass + self.assertEqual(str(inspect.signature(P4)), '(foo, bar)') + def test_signature_on_callable_objects(self): class Foo: def __call__(self, a): -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Feb 3 09:41:39 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 03 Feb 2014 09:41:39 +0100 Subject: [Python-checkins] Daily reference leaks (0a71d29b970a): sum=4 Message-ID: results for 0a71d29b970a on branch "default" -------------------------------------------- test__opcode leaked [3, 3, 3] references, sum=9 test_site leaked [0, 0, 2] references, sum=2 test_site leaked [0, 0, 2] memory blocks, sum=2 test_urllib2net leaked [1590, -1601, 0] references, sum=-11 test_urllib2net leaked [1409, -1409, 2] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogJggTYV', '-x'] From python-checkins at python.org Mon Feb 3 12:52:39 2014 From: python-checkins at python.org (vinay.sajip) Date: Mon, 3 Feb 2014 12:52:39 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Added_cookbook?= =?utf-8?q?_entry_on_logging_filter_configuration_using_dictConfig=28=29?= =?utf-8?q?=2E?= Message-ID: <3fHnTg3xjHz7LjP@mail.python.org> http://hg.python.org/cpython/rev/ae7facd874ba changeset: 88921:ae7facd874ba branch: 2.7 parent: 88915:3e61d8e06ef7 user: Vinay Sajip date: Mon Feb 03 11:51:22 2014 +0000 summary: Added cookbook entry on logging filter configuration using dictConfig(). files: Doc/howto/logging-cookbook.rst | 220 +++++++++++++++++++++ 1 files changed, 220 insertions(+), 0 deletions(-) diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -834,3 +834,223 @@ Note that the order of items might be different according to the version of Python used. + + +.. _custom-handlers: + +.. currentmodule:: logging.config + +Customizing handlers with :func:`dictConfig` +-------------------------------------------- + +There are times when you want to customize logging handlers in particular ways, +and if you use :func:`dictConfig` you may be able to do this without +subclassing. As an example, consider that you may want to set the ownership of a +log file. On POSIX, this is easily done using :func:`shutil.chown`, but the file +handlers in the stdlib don't offer built-in support. You can customize handler +creation using a plain function such as:: + + def owned_file_handler(filename, mode='a', encoding=None, owner=None): + if owner: + if not os.path.exists(filename): + open(filename, 'a').close() + shutil.chown(filename, *owner) + return logging.FileHandler(filename, mode, encoding) + +You can then specify, in a logging configuration passed to :func:`dictConfig`, +that a logging handler be created by calling this function:: + + LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'default': { + 'format': '%(asctime)s %(levelname)s %(name)s %(message)s' + }, + }, + 'handlers': { + 'file':{ + # The values below are popped from this dictionary and + # used to create the handler, set the handler's level and + # its formatter. + '()': owned_file_handler, + 'level':'DEBUG', + 'formatter': 'default', + # The values below are passed to the handler creator callable + # as keyword arguments. + 'owner': ['pulse', 'pulse'], + 'filename': 'chowntest.log', + 'mode': 'w', + 'encoding': 'utf-8', + }, + }, + 'root': { + 'handlers': ['file'], + 'level': 'DEBUG', + }, + } + +In this example I am setting the ownership using the ``pulse`` user and group, +just for the purposes of illustration. Putting it together into a working +script, ``chowntest.py``:: + + import logging, logging.config, os, shutil + + def owned_file_handler(filename, mode='a', encoding=None, owner=None): + if owner: + if not os.path.exists(filename): + open(filename, 'a').close() + shutil.chown(filename, *owner) + return logging.FileHandler(filename, mode, encoding) + + LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'default': { + 'format': '%(asctime)s %(levelname)s %(name)s %(message)s' + }, + }, + 'handlers': { + 'file':{ + # The values below are popped from this dictionary and + # used to create the handler, set the handler's level and + # its formatter. + '()': owned_file_handler, + 'level':'DEBUG', + 'formatter': 'default', + # The values below are passed to the handler creator callable + # as keyword arguments. + 'owner': ['pulse', 'pulse'], + 'filename': 'chowntest.log', + 'mode': 'w', + 'encoding': 'utf-8', + }, + }, + 'root': { + 'handlers': ['file'], + 'level': 'DEBUG', + }, + } + + logging.config.dictConfig(LOGGING) + logger = logging.getLogger('mylogger') + logger.debug('A debug message') + +To run this, you will probably need to run as ``root``:: + + $ sudo python3.3 chowntest.py + $ cat chowntest.log + 2013-11-05 09:34:51,128 DEBUG mylogger A debug message + $ ls -l chowntest.log + -rw-r--r-- 1 pulse pulse 55 2013-11-05 09:34 chowntest.log + +Note that this example uses Python 3.3 because that's where :func:`shutil.chown` +makes an appearance. This approach should work with any Python version that +supports :func:`dictConfig` - namely, Python 2.7, 3.2 or later. With pre-3.3 +versions, you would need to implement the actual ownership change using e.g. +:func:`os.chown`. + +In practice, the handler-creating function may be in a utility module somewhere +in your project. Instead of the line in the configuration:: + + '()': owned_file_handler, + +you could use e.g.:: + + '()': 'ext://project.util.owned_file_handler', + +where ``project.util`` can be replaced with the actual name of the package +where the function resides. In the above working script, using +``'ext://__main__.owned_file_handler'`` should work. Here, the actual callable +is resolved by :func:`dictConfig` from the ``ext://`` specification. + +This example hopefully also points the way to how you could implement other +types of file change - e.g. setting specific POSIX permission bits - in the +same way, using :func:`os.chmod`. + +Of course, the approach could also be extended to types of handler other than a +:class:`~logging.FileHandler` - for example, one of the rotating file handlers, +or a different type of handler altogether. + + +.. _filters-dictconfig: + +Configuring filters with :func:`dictConfig` +------------------------------------------- + +You *can* configure filters using :func:`~logging.config.dictConfig`, though it +might not be obvious at first glance how to do it (hence this recipe). Since +:class:`~logging.Filter` is the only filter class included in the standard +library, and it is unlikely to cater to many requirements (it's only there as a +base class), you will typically need to define your own :class:`~logging.Filter` +subclass with an overridden :meth:`~logging.Filter.filter` method. To do this, +specify the ``()`` key in the configuration dictionary for the filter, +specifying a callable which will be used to create the filter (a class is the +most obvious, but you can provide any callable which returns a +:class:`~logging.Filter` instance). Here is a complete example:: + + import logging + import logging.config + import sys + + class MyFilter(logging.Filter): + def __init__(self, param=None): + self.param = param + + def filter(self, record): + if self.param is None: + allow = True + else: + allow = self.param not in record.msg + if allow: + record.msg = 'changed: ' + record.msg + return allow + + LOGGING = { + 'version': 1, + 'filters': { + 'myfilter': { + '()': MyFilter, + 'param': 'noshow', + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'filters': ['myfilter'] + } + }, + 'root': { + 'level': 'DEBUG', + 'handlers': ['console'] + }, + } + + if __name__ == '__main__': + logging.config.dictConfig(LOGGING) + logging.debug('hello') + logging.debug('hello - noshow') + +This example shows how you can pass configuration data to the callable which +constructs the instance, in the form of keyword parameters. When run, the above +script will print:: + + changed: hello + +which shows that the filter is working as configured. + +A couple of extra points to note: + +* If you can't refer to the callable directly in the configuration (e.g. if it + lives in a different module, and you can't import it directly where the + configuration dictionary is), you can use the form ``ext://...`` as described + in :ref:`logging-config-dict-externalobj`. For example, you could have used + the text ``'ext://__main__.MyFilter'`` instead of ``MyFilter`` in the above + example. + +* As well as for filters, this technique can also be used to configure custom + handlers and formatters. See :ref:`logging-config-dict-userdef` for more + information on how logging supports using user-defined objects in its + configuration, and see the other cookbook recipe :ref:`custom-handlers` above. + -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 12:52:40 2014 From: python-checkins at python.org (vinay.sajip) Date: Mon, 3 Feb 2014 12:52:40 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Added_cookbook?= =?utf-8?q?_entry_on_logging_filter_configuration_using_dictConfig=28=29?= =?utf-8?q?=2E?= Message-ID: <3fHnTh6nGpz7LjP@mail.python.org> http://hg.python.org/cpython/rev/58726e13dea1 changeset: 88922:58726e13dea1 branch: 3.3 parent: 88913:99168e7d4a3d user: Vinay Sajip date: Mon Feb 03 11:51:45 2014 +0000 summary: Added cookbook entry on logging filter configuration using dictConfig(). files: Doc/howto/logging-cookbook.rst | 87 ++++++++++++++++++++++ 1 files changed, 87 insertions(+), 0 deletions(-) diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1694,6 +1694,9 @@ Note that the order of items might be different according to the version of Python used. + +.. _custom-handlers: + .. currentmodule:: logging.config Customizing handlers with :func:`dictConfig` @@ -1948,3 +1951,87 @@ parentheses go around the format string and the arguments, not just the format string. That?s because the __ notation is just syntax sugar for a constructor call to one of the ``XXXMessage`` classes shown above. + + +.. _filters-dictconfig: + +.. currentmodule:: logging.config + +Configuring filters with :func:`dictConfig` +------------------------------------------- + +You *can* configure filters using :func:`~logging.config.dictConfig`, though it +might not be obvious at first glance how to do it (hence this recipe). Since +:class:`~logging.Filter` is the only filter class included in the standard +library, and it is unlikely to cater to many requirements (it's only there as a +base class), you will typically need to define your own :class:`~logging.Filter` +subclass with an overridden :meth:`~logging.Filter.filter` method. To do this, +specify the ``()`` key in the configuration dictionary for the filter, +specifying a callable which will be used to create the filter (a class is the +most obvious, but you can provide any callable which returns a +:class:`~logging.Filter` instance). Here is a complete example:: + + import logging + import logging.config + import sys + + class MyFilter(logging.Filter): + def __init__(self, param=None): + self.param = param + + def filter(self, record): + if self.param is None: + allow = True + else: + allow = self.param not in record.msg + if allow: + record.msg = 'changed: ' + record.msg + return allow + + LOGGING = { + 'version': 1, + 'filters': { + 'myfilter': { + '()': MyFilter, + 'param': 'noshow', + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'filters': ['myfilter'] + } + }, + 'root': { + 'level': 'DEBUG', + 'handlers': ['console'] + }, + } + + if __name__ == '__main__': + logging.config.dictConfig(LOGGING) + logging.debug('hello') + logging.debug('hello - noshow') + +This example shows how you can pass configuration data to the callable which +constructs the instance, in the form of keyword parameters. When run, the above +script will print:: + + changed: hello + +which shows that the filter is working as configured. + +A couple of extra points to note: + +* If you can't refer to the callable directly in the configuration (e.g. if it + lives in a different module, and you can't import it directly where the + configuration dictionary is), you can use the form ``ext://...`` as described + in :ref:`logging-config-dict-externalobj`. For example, you could have used + the text ``'ext://__main__.MyFilter'`` instead of ``MyFilter`` in the above + example. + +* As well as for filters, this technique can also be used to configure custom + handlers and formatters. See :ref:`logging-config-dict-userdef` for more + information on how logging supports using user-defined objects in its + configuration, and see the other cookbook recipe :ref:`custom-handlers` above. + -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 12:52:42 2014 From: python-checkins at python.org (vinay.sajip) Date: Mon, 3 Feb 2014 12:52:42 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merged_documentation_update_from_3=2E3=2E?= Message-ID: <3fHnTk2dQQz7LmH@mail.python.org> http://hg.python.org/cpython/rev/f5178269ccb1 changeset: 88923:f5178269ccb1 parent: 88920:c19f5e4fdbe0 parent: 88922:58726e13dea1 user: Vinay Sajip date: Mon Feb 03 11:52:24 2014 +0000 summary: Merged documentation update from 3.3. files: Doc/howto/logging-cookbook.rst | 87 ++++++++++++++++++++++ 1 files changed, 87 insertions(+), 0 deletions(-) diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1692,6 +1692,9 @@ Note that the order of items might be different according to the version of Python used. + +.. _custom-handlers: + .. currentmodule:: logging.config Customizing handlers with :func:`dictConfig` @@ -1946,3 +1949,87 @@ parentheses go around the format string and the arguments, not just the format string. That?s because the __ notation is just syntax sugar for a constructor call to one of the ``XXXMessage`` classes shown above. + + +.. _filters-dictconfig: + +.. currentmodule:: logging.config + +Configuring filters with :func:`dictConfig` +------------------------------------------- + +You *can* configure filters using :func:`~logging.config.dictConfig`, though it +might not be obvious at first glance how to do it (hence this recipe). Since +:class:`~logging.Filter` is the only filter class included in the standard +library, and it is unlikely to cater to many requirements (it's only there as a +base class), you will typically need to define your own :class:`~logging.Filter` +subclass with an overridden :meth:`~logging.Filter.filter` method. To do this, +specify the ``()`` key in the configuration dictionary for the filter, +specifying a callable which will be used to create the filter (a class is the +most obvious, but you can provide any callable which returns a +:class:`~logging.Filter` instance). Here is a complete example:: + + import logging + import logging.config + import sys + + class MyFilter(logging.Filter): + def __init__(self, param=None): + self.param = param + + def filter(self, record): + if self.param is None: + allow = True + else: + allow = self.param not in record.msg + if allow: + record.msg = 'changed: ' + record.msg + return allow + + LOGGING = { + 'version': 1, + 'filters': { + 'myfilter': { + '()': MyFilter, + 'param': 'noshow', + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'filters': ['myfilter'] + } + }, + 'root': { + 'level': 'DEBUG', + 'handlers': ['console'] + }, + } + + if __name__ == '__main__': + logging.config.dictConfig(LOGGING) + logging.debug('hello') + logging.debug('hello - noshow') + +This example shows how you can pass configuration data to the callable which +constructs the instance, in the form of keyword parameters. When run, the above +script will print:: + + changed: hello + +which shows that the filter is working as configured. + +A couple of extra points to note: + +* If you can't refer to the callable directly in the configuration (e.g. if it + lives in a different module, and you can't import it directly where the + configuration dictionary is), you can use the form ``ext://...`` as described + in :ref:`logging-config-dict-externalobj`. For example, you could have used + the text ``'ext://__main__.MyFilter'`` instead of ``MyFilter`` in the above + example. + +* As well as for filters, this technique can also be used to configure custom + handlers and formatters. See :ref:`logging-config-dict-userdef` for more + information on how logging supports using user-defined objects in its + configuration, and see the other cookbook recipe :ref:`custom-handlers` above. + -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 15:19:34 2014 From: python-checkins at python.org (andrew.kuchling) Date: Mon, 3 Feb 2014 15:19:34 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Use_different_?= =?utf-8?q?word?= Message-ID: <3fHrlB6cTvz7LjV@mail.python.org> http://hg.python.org/cpython/rev/3afb9d94d18f changeset: 88924:3afb9d94d18f branch: 3.3 parent: 88922:58726e13dea1 user: Andrew Kuchling date: Mon Feb 03 09:04:02 2014 -0500 summary: Use different word files: Doc/tutorial/controlflow.rst | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -370,7 +370,7 @@ return False retries = retries - 1 if retries < 0: - raise IOError('refusenik user') + raise IOError('uncooperative user') print(complaint) This function can be called in several ways: @@ -756,4 +756,3 @@ .. [#] Actually, *call by object reference* would be a better description, since if a mutable object is passed, the caller will see any changes the callee makes to it (items inserted into a list). - -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 15:20:33 2014 From: python-checkins at python.org (andrew.kuchling) Date: Mon, 3 Feb 2014 15:20:33 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge_from_3=2E3?= Message-ID: <3fHrmK3wy2z7LjV@mail.python.org> http://hg.python.org/cpython/rev/79ed8e3e3dc6 changeset: 88925:79ed8e3e3dc6 parent: 88923:f5178269ccb1 parent: 88924:3afb9d94d18f user: Andrew Kuchling date: Mon Feb 03 09:20:22 2014 -0500 summary: Merge from 3.3 files: Doc/tutorial/controlflow.rst | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -370,7 +370,7 @@ return False retries = retries - 1 if retries < 0: - raise IOError('refusenik user') + raise IOError('uncooperative user') print(complaint) This function can be called in several ways: @@ -756,4 +756,3 @@ .. [#] Actually, *call by object reference* would be a better description, since if a mutable object is passed, the caller will see any changes the callee makes to it (items inserted into a list). - -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 15:35:15 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 3 Feb 2014 15:35:15 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_remove_code_which_does_not?= =?utf-8?q?hing_but_cause_refleaks?= Message-ID: <3fHs5H0rtsz7Lk7@mail.python.org> http://hg.python.org/cpython/rev/c45a10b93a56 changeset: 88926:c45a10b93a56 user: Benjamin Peterson date: Mon Feb 03 09:35:08 2014 -0500 summary: remove code which does nothing but cause refleaks files: Modules/_opcode.c | 4 ---- 1 files changed, 0 insertions(+), 4 deletions(-) diff --git a/Modules/_opcode.c b/Modules/_opcode.c --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -55,15 +55,11 @@ int effect; int oparg_int = 0; if (HAS_ARG(opcode)) { - PyObject *i_object; if (oparg == Py_None) { PyErr_SetString(PyExc_ValueError, "stack_effect: opcode requires oparg but oparg was not specified"); return -1; } - i_object = PyNumber_Index(oparg); - if (!i_object) - return -1; oparg_int = (int)PyLong_AsLong(oparg); if ((oparg_int == -1) && PyErr_Occurred()) return -1; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 19:34:02 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 3 Feb 2014 19:34:02 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_use_system_mer?= =?utf-8?q?curial?= Message-ID: <3fHyNp5XDVz7Ln7@mail.python.org> http://hg.python.org/cpython/rev/f086503dcf54 changeset: 88927:f086503dcf54 branch: 2.7 parent: 88921:ae7facd874ba user: Benjamin Peterson date: Mon Feb 03 13:33:56 2014 -0500 summary: use system mercurial files: Doc/tools/dailybuild.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/tools/dailybuild.py b/Doc/tools/dailybuild.py --- a/Doc/tools/dailybuild.py +++ b/Doc/tools/dailybuild.py @@ -43,7 +43,7 @@ print 'Doc autobuild started in %s' % checkout os.chdir(checkout) print 'Running hg pull --update' - os.system('/usr/local/bin/hg pull --update') + os.system('hg pull --update') print 'Running make autobuild' maketarget = 'autobuild-' + ('html' if quick else ('dev' if isdev else 'stable')) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:08:06 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 3 Feb 2014 20:08:06 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_remove_extra_backtick?= Message-ID: <3fHz8654L8z7Lnl@mail.python.org> http://hg.python.org/cpython/rev/825d8e5c8b61 changeset: 88928:825d8e5c8b61 parent: 88926:c45a10b93a56 user: Benjamin Peterson date: Mon Feb 03 14:08:00 2014 -0500 summary: remove extra backtick files: Doc/library/asyncio-subprocess.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -8,7 +8,7 @@ .. function:: create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) - Run the shell command *cmd* (:class:`str`)`. Return a :class:`Process` + Run the shell command *cmd* given as a string. Return a :class:`Process` instance. This function returns a :ref:`coroutine object `. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:32 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:32 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwMzY4?= =?utf-8?q?=3A_Add_tests_for_Tkinter_methods_exprstring=28=29=2C_exprdoubl?= =?utf-8?b?ZSgpLA==?= Message-ID: <3fHzrN0CX0z7LjV@mail.python.org> http://hg.python.org/cpython/rev/a6ba6db9edb4 changeset: 88929:a6ba6db9edb4 branch: 2.7 parent: 88921:ae7facd874ba user: Serhiy Storchaka date: Mon Feb 03 20:41:04 2014 +0200 summary: Issue #20368: Add tests for Tkinter methods exprstring(), exprdouble(), exprlong() and exprboolean(). files: Lib/test/test_tcl.py | 131 +++++++++++++++++++++++++++++++ 1 files changed, 131 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -189,6 +189,137 @@ self.assertEqual(p.wait(), 0, 'Non-zero exit code') + def test_exprstring(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprstring(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, str) + + self.assertRaises(TypeError, tcl.exprstring) + self.assertRaises(TypeError, tcl.exprstring, '8.2', '+6') + self.assertRaises(TclError, tcl.exprstring, 'spam') + check('', '0') + check('8.2 + 6', '14.2') + check('2**64', str(2**64)) + check('3.1 + $a', '6.1') + check('2 + "$a.$b"', '5.6') + check('4*[llength "6 2"]', '8') + check('{word one} < "word $a"', '0') + check('4*2 < 7', '0') + check('hypot($a, 4)', '5.0') + check('5 / 4', '1') + check('5 / 4.0', '1.25') + check('5 / ( [string length "abcd"] + 0.0 )', '1.25') + check('20.0/5.0', '4.0') + check('"0x03" > "2"', '1') + check('[string length "a\xc2\xbd\xe2\x82\xac"]', '3') + check(r'[string length "a\xbd\u20ac"]', '3') + check('"abc"', 'abc') + check('"a\xc2\xbd\xe2\x82\xac"', 'a\xc2\xbd\xe2\x82\xac') + check(r'"a\xbd\u20ac"', 'a\xc2\xbd\xe2\x82\xac') + + def test_exprdouble(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprdouble(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, float) + + self.assertRaises(TypeError, tcl.exprdouble) + self.assertRaises(TypeError, tcl.exprdouble, '8.2', '+6') + self.assertRaises(TclError, tcl.exprdouble, 'spam') + check('', 0.0) + check('8.2 + 6', 14.2) + check('2**64', float(2**64)) + check('3.1 + $a', 6.1) + check('2 + "$a.$b"', 5.6) + check('4*[llength "6 2"]', 8.0) + check('{word one} < "word $a"', 0.0) + check('4*2 < 7', 0.0) + check('hypot($a, 4)', 5.0) + check('5 / 4', 1.0) + check('5 / 4.0', 1.25) + check('5 / ( [string length "abcd"] + 0.0 )', 1.25) + check('20.0/5.0', 4.0) + check('"0x03" > "2"', 1.0) + check('[string length "a\xc2\xbd\xe2\x82\xac"]', 3.0) + check(r'[string length "a\xbd\u20ac"]', 3.0) + self.assertRaises(TclError, tcl.exprdouble, '"abc"') + + def test_exprlong(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprlong(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, int) + + self.assertRaises(TypeError, tcl.exprlong) + self.assertRaises(TypeError, tcl.exprlong, '8.2', '+6') + self.assertRaises(TclError, tcl.exprlong, 'spam') + check('', 0) + check('8.2 + 6', 14) + self.assertRaises(TclError, tcl.exprlong, '2**64') + check('3.1 + $a', 6) + check('2 + "$a.$b"', 5) + check('4*[llength "6 2"]', 8) + check('{word one} < "word $a"', 0) + check('4*2 < 7', 0) + check('hypot($a, 4)', 5) + check('5 / 4', 1) + check('5 / 4.0', 1) + check('5 / ( [string length "abcd"] + 0.0 )', 1) + check('20.0/5.0', 4) + check('"0x03" > "2"', 1) + check('[string length "a\xc2\xbd\xe2\x82\xac"]', 3) + check(r'[string length "a\xbd\u20ac"]', 3) + self.assertRaises(TclError, tcl.exprlong, '"abc"') + + def test_exprboolean(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprboolean(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, int) + self.assertNotIsInstance(result, bool) + + self.assertRaises(TypeError, tcl.exprboolean) + self.assertRaises(TypeError, tcl.exprboolean, '8.2', '+6') + self.assertRaises(TclError, tcl.exprboolean, 'spam') + check('', False) + for value in ('0', 'false', 'no', 'off'): + check(value, False) + check('"%s"' % value, False) + check('{%s}' % value, False) + for value in ('1', 'true', 'yes', 'on'): + check(value, True) + check('"%s"' % value, True) + check('{%s}' % value, True) + check('8.2 + 6', True) + check('2**64', True) + check('3.1 + $a', True) + check('2 + "$a.$b"', True) + check('4*[llength "6 2"]', True) + check('{word one} < "word $a"', False) + check('4*2 < 7', False) + check('hypot($a, 4)', True) + check('5 / 4', True) + check('5 / 4.0', True) + check('5 / ( [string length "abcd"] + 0.0 )', True) + check('20.0/5.0', True) + check('"0x03" > "2"', True) + check('[string length "a\xc2\xbd\xe2\x82\xac"]', True) + check(r'[string length "a\xbd\u20ac"]', True) + self.assertRaises(TclError, tcl.exprboolean, '"abc"') + def test_passing_values(self): def passValue(value): return self.interp.call('set', '_', value) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:33 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:33 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwMzY4?= =?utf-8?q?=3A_Add_tests_for_Tkinter_methods_exprstring=28=29=2C_exprdoubl?= =?utf-8?b?ZSgpLA==?= Message-ID: <3fHzrP44dvz7Lll@mail.python.org> http://hg.python.org/cpython/rev/825c8db8b1e2 changeset: 88930:825c8db8b1e2 branch: 3.3 parent: 88924:3afb9d94d18f user: Serhiy Storchaka date: Mon Feb 03 20:41:34 2014 +0200 summary: Issue #20368: Add tests for Tkinter methods exprstring(), exprdouble(), exprlong() and exprboolean(). files: Lib/test/test_tcl.py | 135 +++++++++++++++++++++++++++++++ 1 files changed, 135 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -177,6 +177,141 @@ # exit code must be zero self.assertEqual(f.close(), None) + def test_exprstring(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprstring(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, str) + + self.assertRaises(TypeError, tcl.exprstring) + self.assertRaises(TypeError, tcl.exprstring, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprstring, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprstring, 'spam') + check('', '0') + check('8.2 + 6', '14.2') + check('2**64', str(2**64)) + check('3.1 + $a', '6.1') + check('2 + "$a.$b"', '5.6') + check('4*[llength "6 2"]', '8') + check('{word one} < "word $a"', '0') + check('4*2 < 7', '0') + check('hypot($a, 4)', '5.0') + check('5 / 4', '1') + check('5 / 4.0', '1.25') + check('5 / ( [string length "abcd"] + 0.0 )', '1.25') + check('20.0/5.0', '4.0') + check('"0x03" > "2"', '1') + check('[string length "a\xbd\u20ac"]', '3') + check(r'[string length "a\xbd\u20ac"]', '3') + check('"abc"', 'abc') + check('"a\xbd\u20ac"', 'a\xbd\u20ac') + check(r'"a\xbd\u20ac"', 'a\xbd\u20ac') + + def test_exprdouble(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprdouble(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, float) + + self.assertRaises(TypeError, tcl.exprdouble) + self.assertRaises(TypeError, tcl.exprdouble, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprdouble, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprdouble, 'spam') + check('', 0.0) + check('8.2 + 6', 14.2) + check('2**64', float(2**64)) + check('3.1 + $a', 6.1) + check('2 + "$a.$b"', 5.6) + check('4*[llength "6 2"]', 8.0) + check('{word one} < "word $a"', 0.0) + check('4*2 < 7', 0.0) + check('hypot($a, 4)', 5.0) + check('5 / 4', 1.0) + check('5 / 4.0', 1.25) + check('5 / ( [string length "abcd"] + 0.0 )', 1.25) + check('20.0/5.0', 4.0) + check('"0x03" > "2"', 1.0) + check('[string length "a\xbd\u20ac"]', 3.0) + check(r'[string length "a\xbd\u20ac"]', 3.0) + self.assertRaises(TclError, tcl.exprdouble, '"abc"') + + def test_exprlong(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprlong(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, int) + + self.assertRaises(TypeError, tcl.exprlong) + self.assertRaises(TypeError, tcl.exprlong, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprlong, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprlong, 'spam') + check('', 0) + check('8.2 + 6', 14) + self.assertRaises(TclError, tcl.exprlong, '2**64') + check('3.1 + $a', 6) + check('2 + "$a.$b"', 5) + check('4*[llength "6 2"]', 8) + check('{word one} < "word $a"', 0) + check('4*2 < 7', 0) + check('hypot($a, 4)', 5) + check('5 / 4', 1) + check('5 / 4.0', 1) + check('5 / ( [string length "abcd"] + 0.0 )', 1) + check('20.0/5.0', 4) + check('"0x03" > "2"', 1) + check('[string length "a\xbd\u20ac"]', 3) + check(r'[string length "a\xbd\u20ac"]', 3) + self.assertRaises(TclError, tcl.exprlong, '"abc"') + + def test_exprboolean(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprboolean(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, int) + self.assertNotIsInstance(result, bool) + + self.assertRaises(TypeError, tcl.exprboolean) + self.assertRaises(TypeError, tcl.exprboolean, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprboolean, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprboolean, 'spam') + check('', False) + for value in ('0', 'false', 'no', 'off'): + check(value, False) + check('"%s"' % value, False) + check('{%s}' % value, False) + for value in ('1', 'true', 'yes', 'on'): + check(value, True) + check('"%s"' % value, True) + check('{%s}' % value, True) + check('8.2 + 6', True) + check('2**64', True) + check('3.1 + $a', True) + check('2 + "$a.$b"', True) + check('4*[llength "6 2"]', True) + check('{word one} < "word $a"', False) + check('4*2 < 7', False) + check('hypot($a, 4)', True) + check('5 / 4', True) + check('5 / 4.0', True) + check('5 / ( [string length "abcd"] + 0.0 )', True) + check('20.0/5.0', True) + check('"0x03" > "2"', True) + check('[string length "a\xbd\u20ac"]', True) + check(r'[string length "a\xbd\u20ac"]', True) + self.assertRaises(TclError, tcl.exprboolean, '"abc"') + def test_passing_values(self): def passValue(value): return self.interp.call('set', '_', value) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:34 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:34 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMi43IC0+IDIuNyk6?= =?utf-8?q?_Merge_heads?= Message-ID: <3fHzrQ5xJWz7LlC@mail.python.org> http://hg.python.org/cpython/rev/920f29a47d49 changeset: 88931:920f29a47d49 branch: 2.7 parent: 88929:a6ba6db9edb4 parent: 88927:f086503dcf54 user: Serhiy Storchaka date: Mon Feb 03 20:42:43 2014 +0200 summary: Merge heads files: Doc/tools/dailybuild.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/tools/dailybuild.py b/Doc/tools/dailybuild.py --- a/Doc/tools/dailybuild.py +++ b/Doc/tools/dailybuild.py @@ -43,7 +43,7 @@ print 'Doc autobuild started in %s' % checkout os.chdir(checkout) print 'Running hg pull --update' - os.system('/usr/local/bin/hg pull --update') + os.system('hg pull --update') print 'Running make autobuild' maketarget = 'autobuild-' + ('html' if quick else ('dev' if isdev else 'stable')) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:36 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:36 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320368=3A_Add_tests_for_Tkinter_methods_exprstri?= =?utf-8?b?bmcoKSwgZXhwcmRvdWJsZSgpLA==?= Message-ID: <3fHzrS1Pfwz7LjN@mail.python.org> http://hg.python.org/cpython/rev/28ec384e7dcc changeset: 88932:28ec384e7dcc parent: 88926:c45a10b93a56 parent: 88930:825c8db8b1e2 user: Serhiy Storchaka date: Mon Feb 03 20:46:14 2014 +0200 summary: Issue #20368: Add tests for Tkinter methods exprstring(), exprdouble(), exprlong() and exprboolean(). files: Lib/test/test_tcl.py | 135 +++++++++++++++++++++++++++++++ 1 files changed, 135 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -177,6 +177,141 @@ # exit code must be zero self.assertEqual(f.close(), None) + def test_exprstring(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprstring(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, str) + + self.assertRaises(TypeError, tcl.exprstring) + self.assertRaises(TypeError, tcl.exprstring, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprstring, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprstring, 'spam') + check('', '0') + check('8.2 + 6', '14.2') + check('2**64', str(2**64)) + check('3.1 + $a', '6.1') + check('2 + "$a.$b"', '5.6') + check('4*[llength "6 2"]', '8') + check('{word one} < "word $a"', '0') + check('4*2 < 7', '0') + check('hypot($a, 4)', '5.0') + check('5 / 4', '1') + check('5 / 4.0', '1.25') + check('5 / ( [string length "abcd"] + 0.0 )', '1.25') + check('20.0/5.0', '4.0') + check('"0x03" > "2"', '1') + check('[string length "a\xbd\u20ac"]', '3') + check(r'[string length "a\xbd\u20ac"]', '3') + check('"abc"', 'abc') + check('"a\xbd\u20ac"', 'a\xbd\u20ac') + check(r'"a\xbd\u20ac"', 'a\xbd\u20ac') + + def test_exprdouble(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprdouble(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, float) + + self.assertRaises(TypeError, tcl.exprdouble) + self.assertRaises(TypeError, tcl.exprdouble, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprdouble, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprdouble, 'spam') + check('', 0.0) + check('8.2 + 6', 14.2) + check('2**64', float(2**64)) + check('3.1 + $a', 6.1) + check('2 + "$a.$b"', 5.6) + check('4*[llength "6 2"]', 8.0) + check('{word one} < "word $a"', 0.0) + check('4*2 < 7', 0.0) + check('hypot($a, 4)', 5.0) + check('5 / 4', 1.0) + check('5 / 4.0', 1.25) + check('5 / ( [string length "abcd"] + 0.0 )', 1.25) + check('20.0/5.0', 4.0) + check('"0x03" > "2"', 1.0) + check('[string length "a\xbd\u20ac"]', 3.0) + check(r'[string length "a\xbd\u20ac"]', 3.0) + self.assertRaises(TclError, tcl.exprdouble, '"abc"') + + def test_exprlong(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprlong(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, int) + + self.assertRaises(TypeError, tcl.exprlong) + self.assertRaises(TypeError, tcl.exprlong, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprlong, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprlong, 'spam') + check('', 0) + check('8.2 + 6', 14) + self.assertRaises(TclError, tcl.exprlong, '2**64') + check('3.1 + $a', 6) + check('2 + "$a.$b"', 5) + check('4*[llength "6 2"]', 8) + check('{word one} < "word $a"', 0) + check('4*2 < 7', 0) + check('hypot($a, 4)', 5) + check('5 / 4', 1) + check('5 / 4.0', 1) + check('5 / ( [string length "abcd"] + 0.0 )', 1) + check('20.0/5.0', 4) + check('"0x03" > "2"', 1) + check('[string length "a\xbd\u20ac"]', 3) + check(r'[string length "a\xbd\u20ac"]', 3) + self.assertRaises(TclError, tcl.exprlong, '"abc"') + + def test_exprboolean(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprboolean(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, int) + self.assertNotIsInstance(result, bool) + + self.assertRaises(TypeError, tcl.exprboolean) + self.assertRaises(TypeError, tcl.exprboolean, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprboolean, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprboolean, 'spam') + check('', False) + for value in ('0', 'false', 'no', 'off'): + check(value, False) + check('"%s"' % value, False) + check('{%s}' % value, False) + for value in ('1', 'true', 'yes', 'on'): + check(value, True) + check('"%s"' % value, True) + check('{%s}' % value, True) + check('8.2 + 6', True) + check('2**64', True) + check('3.1 + $a', True) + check('2 + "$a.$b"', True) + check('4*[llength "6 2"]', True) + check('{word one} < "word $a"', False) + check('4*2 < 7', False) + check('hypot($a, 4)', True) + check('5 / 4', True) + check('5 / 4.0', True) + check('5 / ( [string length "abcd"] + 0.0 )', True) + check('20.0/5.0', True) + check('"0x03" > "2"', True) + check('[string length "a\xbd\u20ac"]', True) + check(r'[string length "a\xbd\u20ac"]', True) + self.assertRaises(TclError, tcl.exprboolean, '"abc"') + def test_passing_values(self): def passValue(value): return self.interp.call('set', '_', value) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:37 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:37 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwMzY4?= =?utf-8?q?=3A_The_null_character_now_correctly_passed_from_Tcl_to_Python_?= =?utf-8?b?KGlu?= Message-ID: <3fHzrT5Cbbz7Lk7@mail.python.org> http://hg.python.org/cpython/rev/65c29c07bb31 changeset: 88933:65c29c07bb31 branch: 2.7 parent: 88931:920f29a47d49 user: Serhiy Storchaka date: Mon Feb 03 21:23:46 2014 +0200 summary: Issue #20368: The null character now correctly passed from Tcl to Python (in unicode strings only). Improved error handling in variables-related commands. files: Lib/lib-tk/test/test_tkinter/test_variables.py | 14 + Lib/test/test_tcl.py | 47 +- Misc/NEWS | 3 + Modules/_tkinter.c | 217 +++++---- 4 files changed, 175 insertions(+), 106 deletions(-) diff --git a/Lib/lib-tk/test/test_tkinter/test_variables.py b/Lib/lib-tk/test/test_tkinter/test_variables.py --- a/Lib/lib-tk/test/test_tkinter/test_variables.py +++ b/Lib/lib-tk/test/test_tkinter/test_variables.py @@ -58,6 +58,14 @@ with self.assertRaises(TypeError): Variable(self.root, name=123) + def test_null_in_name(self): + with self.assertRaises(ValueError): + Variable(self.root, name='var\x00name') + with self.assertRaises(ValueError): + self.root.globalsetvar('var\x00name', "value") + with self.assertRaises(ValueError): + self.root.setvar('var\x00name', "value") + class TestStringVar(TestBase): @@ -71,6 +79,12 @@ self.root.globalsetvar("name", "value") self.assertEqual("value", v.get()) + def test_get_null(self): + v = StringVar(self.root, "abc\x00def", "name") + self.assertEqual("abc\x00def", v.get()) + self.root.globalsetvar("name", "val\x00ue") + self.assertEqual("val\x00ue", v.get()) + class TestIntVar(TestBase): diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -139,6 +139,18 @@ self.assertEqual(tcl.eval('set b'),'2') self.assertEqual(tcl.eval('set c'),'3') + def test_evalfile_null_in_result(self): + tcl = self.interp + with open(test_support.TESTFN, 'wb') as f: + self.addCleanup(test_support.unlink, test_support.TESTFN) + f.write(""" + set a "a\0b" + set b "a\\0b" + """) + tcl.evalfile(test_support.TESTFN) + self.assertEqual(tcl.eval('set a'), 'a\xc0\x80b') + self.assertEqual(tcl.eval('set b'), 'a\xc0\x80b') + def testEvalFileException(self): tcl = self.interp filename = "doesnotexists" @@ -220,6 +232,7 @@ check('"abc"', 'abc') check('"a\xc2\xbd\xe2\x82\xac"', 'a\xc2\xbd\xe2\x82\xac') check(r'"a\xbd\u20ac"', 'a\xc2\xbd\xe2\x82\xac') + check(r'"a\0b"', 'a\xc0\x80b') def test_exprdouble(self): tcl = self.interp @@ -326,8 +339,17 @@ self.assertEqual(passValue(True), True if self.wantobjects else '1') self.assertEqual(passValue(False), False if self.wantobjects else '0') + self.assertEqual(passValue('string'), 'string') + self.assertEqual(passValue('string\xbd'), 'string\xbd') + self.assertEqual(passValue('string\xe2\x82\xac'), u'string\u20ac') self.assertEqual(passValue(u'string'), u'string') + self.assertEqual(passValue(u'string\xbd'), u'string\xbd') self.assertEqual(passValue(u'string\u20ac'), u'string\u20ac') + self.assertEqual(passValue('str\x00ing'), 'str\x00ing') + self.assertEqual(passValue('str\xc0\x80ing'), 'str\x00ing') + self.assertEqual(passValue(u'str\x00ing'), u'str\x00ing') + self.assertEqual(passValue(u'str\x00ing\xbd'), u'str\x00ing\xbd') + self.assertEqual(passValue(u'str\x00ing\u20ac'), u'str\x00ing\u20ac') for i in (0, 1, -1, int(2**31-1), int(-2**31)): self.assertEqual(passValue(i), i if self.wantobjects else str(i)) for f in (0.0, 1.0, -1.0, 1//3, 1/3.0, @@ -356,14 +378,16 @@ result.append(arg) return arg self.interp.createcommand('testfunc', testfunc) - def check(value, expected, eq=self.assertEqual): + def check(value, expected, expected2=None, eq=self.assertEqual): + if expected2 is None: + expected2 = expected del result[:] r = self.interp.call('testfunc', value) self.assertEqual(len(result), 1) - self.assertIsInstance(result[0], str) - eq(result[0], expected) - self.assertIsInstance(r, str) - eq(r, expected) + self.assertIsInstance(result[0], (str, unicode)) + eq(result[0], expected2) + self.assertIsInstance(r, (str, unicode)) + eq(r, expected2) def float_eq(actual, expected): expected = float(expected) self.assertAlmostEqual(float(actual), expected, @@ -376,7 +400,15 @@ check(False, '0') check('string', 'string') check('string\xbd', 'string\xbd') - check('string\u20ac', 'string\u20ac') + check('string\xe2\x82\xac', 'string\xe2\x82\xac', u'string\u20ac') + check(u'string', u'string') + check(u'string\xbd', 'string\xc2\xbd', u'string\xbd') + check(u'string\u20ac', 'string\xe2\x82\xac', u'string\u20ac') + check('str\xc0\x80ing', 'str\xc0\x80ing', u'str\x00ing') + check('str\xc0\x80ing\xe2\x82\xac', 'str\xc0\x80ing\xe2\x82\xac', u'str\x00ing\u20ac') + check(u'str\x00ing', 'str\xc0\x80ing', u'str\x00ing') + check(u'str\x00ing\xbd', 'str\xc0\x80ing\xc2\xbd', u'str\x00ing\xbd') + check(u'str\x00ing\u20ac', 'str\xc0\x80ing\xe2\x82\xac', u'str\x00ing\u20ac') for i in (0, 1, -1, 2**31-1, -2**31): check(i, str(i)) for f in (0.0, 1.0, -1.0): @@ -405,6 +437,7 @@ (u'a\n b\t\r c\n ', ('a', 'b', 'c')), ('a \xe2\x82\xac', ('a', '\xe2\x82\xac')), (u'a \u20ac', ('a', '\xe2\x82\xac')), + ('a\xc0\x80b c\xc0\x80d', ('a\xc0\x80b', 'c\xc0\x80d')), ('a {b c}', ('a', 'b c')), (r'a b\ c', ('a', 'b c')), (('a', 'b c'), ('a', 'b c')), @@ -449,6 +482,8 @@ (u'a\n b\t\r c\n ', ('a', 'b', 'c')), ('a \xe2\x82\xac', ('a', '\xe2\x82\xac')), (u'a \u20ac', ('a', '\xe2\x82\xac')), + ('a\xc0\x80b', 'a\xc0\x80b'), + ('a\xc0\x80b c\xc0\x80d', ('a\xc0\x80b', 'c\xc0\x80d')), ('a {b c}', ('a', ('b', 'c'))), (r'a b\ c', ('a', ('b', 'c'))), (('a', 'b c'), ('a', ('b', 'c'))), diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -38,6 +38,9 @@ Library ------- +- Issue #20368: The null character now correctly passed from Tcl to Python (in + unicode strings only). Improved error handling in variables-related commands. + - Issue #20435: Fix _pyio.StringIO.getvalue() to take into account newline translation settings. diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -456,6 +456,68 @@ +#ifdef Py_USING_UNICODE +static PyObject * +unicode_FromTclStringAndSize(const char *s, Py_ssize_t size) +{ + PyObject *r = PyUnicode_DecodeUTF8(s, size, NULL); + if (!r && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) { + /* Tcl encodes null character as \xc0\x80 */ + if (memchr(s, '\xc0', size)) { + char *buf, *q; + const char *e = s + size; + PyErr_Clear(); + q = buf = (char *)PyMem_Malloc(size); + if (buf == NULL) + return NULL; + while (s != e) { + if (s + 1 != e && s[0] == '\xc0' && s[1] == '\x80') { + *q++ = '\0'; + s += 2; + } + else + *q++ = *s++; + } + s = buf; + size = q - s; + r = PyUnicode_DecodeUTF8(s, size, NULL); + PyMem_Free(buf); + } + } + return r; +} +#endif + +static PyObject * +fromTclStringAndSize(const char *s, Py_ssize_t size) +{ + PyObject *r; +#ifdef Py_USING_UNICODE + Py_ssize_t i; + /* If Tcl string contains any bytes with the top bit set, + it's UTF-8 and we should decode it to Unicode */ + for (i = 0; i < size; i++) + if (s[i] & 0x80) + break; + if (i != size) { + /* It isn't an ASCII string. */ + r = unicode_FromTclStringAndSize(s, size); + if (r) + return r; + PyErr_Clear(); + } +#endif + r = PyString_FromStringAndSize(s, size); + return r; +} + +static PyObject * +fromTclString(const char *s) +{ + return fromTclStringAndSize(s, strlen(s)); +} + + static PyObject * Split(char *list) { @@ -841,27 +903,10 @@ static PyObject * PyTclObject_string(PyTclObject *self, void *ignored) { - char *s; - int i, len; if (!self->string) { - s = Tcl_GetStringFromObj(self->value, &len); - for (i = 0; i < len; i++) - if (s[i] & 0x80) - break; -#ifdef Py_USING_UNICODE - if (i == len) - /* It is an ASCII string. */ - self->string = PyString_FromStringAndSize(s, len); - else { - self->string = PyUnicode_DecodeUTF8(s, len, "strict"); - if (!self->string) { - PyErr_Clear(); - self->string = PyString_FromStringAndSize(s, len); - } - } -#else - self->string = PyString_FromStringAndSize(s, len); -#endif + int len; + char *s = Tcl_GetStringFromObj(self->value, &len); + self->string = fromTclStringAndSize(s, len); if (!self->string) return NULL; } @@ -883,7 +928,7 @@ } /* XXX Could chache result if it is non-ASCII. */ s = Tcl_GetStringFromObj(self->value, &len); - return PyUnicode_DecodeUTF8(s, len, "strict"); + return unicode_FromTclStringAndSize(s, len); } #endif @@ -1022,6 +1067,8 @@ PyErr_SetString(PyExc_OverflowError, "string is too long"); return NULL; } + if (sizeof(Py_UNICODE) == sizeof(Tcl_UniChar)) + return Tcl_NewUnicodeObj(inbuf, size); allocsize = ((size_t)size) * sizeof(Tcl_UniChar); if (allocsize >= size) outbuf = (Tcl_UniChar*)ckalloc(allocsize); @@ -1073,30 +1120,7 @@ TkappObject *app = (TkappObject*)tkapp; if (value->typePtr == NULL) { - /* If the result contains any bytes with the top bit set, - it's UTF-8 and we should decode it to Unicode */ -#ifdef Py_USING_UNICODE - int i; - char *s = value->bytes; - int len = value->length; - for (i = 0; i < len; i++) { - if (value->bytes[i] & 0x80) - break; - } - - if (i == value->length) - result = PyString_FromStringAndSize(s, len); - else { - /* Convert UTF-8 to Unicode string */ - result = PyUnicode_DecodeUTF8(s, len, "strict"); - if (result == NULL) { - PyErr_Clear(); - result = PyString_FromStringAndSize(s, len); - } - } -#else - result = PyString_FromStringAndSize(value->bytes, value->length); -#endif + result = fromTclStringAndSize(value->bytes, value->length); return result; } @@ -1273,8 +1297,8 @@ Tkapp_CallResult(TkappObject *self) { PyObject *res = NULL; + Tcl_Obj *value = Tcl_GetObjResult(self->interp); if(self->wantobjects) { - Tcl_Obj *value = Tcl_GetObjResult(self->interp); /* Not sure whether the IncrRef is necessary, but something may overwrite the interpreter result while we are converting it. */ @@ -1282,33 +1306,9 @@ res = FromObj((PyObject*)self, value); Tcl_DecrRefCount(value); } else { - const char *s = Tcl_GetStringResult(self->interp); - const char *p = s; - - /* If the result contains any bytes with the top bit set, - it's UTF-8 and we should decode it to Unicode */ -#ifdef Py_USING_UNICODE - while (*p != '\0') { - if (*p & 0x80) - break; - p++; - } - - if (*p == '\0') - res = PyString_FromStringAndSize(s, (int)(p-s)); - else { - /* Convert UTF-8 to Unicode string */ - p = strchr(p, '\0'); - res = PyUnicode_DecodeUTF8(s, (int)(p-s), "strict"); - if (res == NULL) { - PyErr_Clear(); - res = PyString_FromStringAndSize(s, (int)(p-s)); - } - } -#else - p = strchr(p, '\0'); - res = PyString_FromStringAndSize(s, (int)(p-s)); -#endif + int len; + const char *s = Tcl_GetStringFromObj(value, &len); + res = fromTclStringAndSize(s, len); } return res; } @@ -1611,16 +1611,28 @@ static int varname_converter(PyObject *in, void *_out) { + char *s; char **out = (char**)_out; if (PyString_Check(in)) { - *out = PyString_AsString(in); + if (PyString_Size(in) > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "string is too long"); + return 0; + } + s = PyString_AsString(in); + if (strlen(s) != PyString_Size(in)) { + PyErr_SetString(PyExc_ValueError, "null character in string"); + return 0; + } + *out = s; return 1; } if (PyTclObject_Check(in)) { *out = PyTclObject_TclString(in); return 1; } - /* XXX: Should give diagnostics. */ + PyErr_Format(PyExc_TypeError, + "must be str or Tcl_Obj, not %.50s", + in->ob_type->tp_name); return 0; } @@ -1706,8 +1718,11 @@ PyObject *res = NULL; Tcl_Obj *newval, *ok; - if (PyArg_ParseTuple(args, "O&O:setvar", - varname_converter, &name1, &newValue)) { + switch (PyTuple_GET_SIZE(args)) { + case 2: + if (!PyArg_ParseTuple(args, "O&O:setvar", + varname_converter, &name1, &newValue)) + return NULL; /* XXX Acquire tcl lock??? */ newval = AsObj(newValue); if (newval == NULL) @@ -1723,27 +1738,27 @@ Py_INCREF(res); } LEAVE_OVERLAP_TCL - } - else { - PyErr_Clear(); - if (PyArg_ParseTuple(args, "ssO:setvar", - &name1, &name2, &newValue)) { - /* XXX must hold tcl lock already??? */ - newval = AsObj(newValue); - ENTER_TCL - ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, name2, newval, flags); - ENTER_OVERLAP - if (!ok) - Tkinter_Error(self); - else { - res = Py_None; - Py_INCREF(res); - } - LEAVE_OVERLAP_TCL + break; + case 3: + if (!PyArg_ParseTuple(args, "ssO:setvar", + &name1, &name2, &newValue)) + return NULL; + /* XXX must hold tcl lock already??? */ + newval = AsObj(newValue); + ENTER_TCL + ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, name2, newval, flags); + ENTER_OVERLAP + if (!ok) + Tkinter_Error(self); + else { + res = Py_None; + Py_INCREF(res); } - else { - return NULL; - } + LEAVE_OVERLAP_TCL + break; + default: + PyErr_SetString(PyExc_TypeError, "setvar requires 2 to 3 arguments"); + return NULL; } return res; } @@ -1783,7 +1798,9 @@ res = FromObj(self, tres); } else { - res = PyString_FromString(Tcl_GetString(tres)); + int len; + char *s = Tcl_GetStringFromObj(tres, &len); + res = PyString_FromStringAndSize(s, len); } } LEAVE_OVERLAP_TCL @@ -1921,7 +1938,7 @@ if (retval == TCL_ERROR) res = Tkinter_Error(self); else - res = Py_BuildValue("s", Tkapp_Result(self)); + res = PyString_FromString(Tkapp_Result(self)); LEAVE_OVERLAP_TCL return res; } @@ -2158,7 +2175,7 @@ return PythonCmd_Error(interp); for (i = 0; i < (argc - 1); i++) { - PyObject *s = PyString_FromString(argv[i + 1]); + PyObject *s = fromTclString(argv[i + 1]); if (!s || PyTuple_SetItem(arg, i, s)) { Py_DECREF(arg); return PythonCmd_Error(interp); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:39 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:39 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwMzY4?= =?utf-8?q?=3A_The_null_character_now_correctly_passed_from_Tcl_to_Python?= =?utf-8?q?=2E?= Message-ID: <3fHzrW32qnz7Lmy@mail.python.org> http://hg.python.org/cpython/rev/08e3343f01a5 changeset: 88934:08e3343f01a5 branch: 3.3 parent: 88930:825c8db8b1e2 user: Serhiy Storchaka date: Mon Feb 03 21:24:07 2014 +0200 summary: Issue #20368: The null character now correctly passed from Tcl to Python. Improved error handling in variables-related commands. files: Lib/test/test_tcl.py | 50 ++- Lib/tkinter/test/test_tkinter/test_variables.py | 18 + Misc/NEWS | 3 + Modules/_tkinter.c | 184 ++++++--- 4 files changed, 176 insertions(+), 79 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -55,6 +55,10 @@ tcl.eval('set a 1') self.assertEqual(tcl.eval('set a'),'1') + def test_eval_null_in_result(self): + tcl = self.interp + self.assertEqual(tcl.eval('set a "a\\0b"'), 'a\x00b') + def testEvalException(self): tcl = self.interp self.assertRaises(TclError,tcl.eval,'set a') @@ -127,20 +131,29 @@ def testEvalFile(self): tcl = self.interp - filename = "testEvalFile.tcl" - fd = open(filename,'w') - script = """set a 1 - set b 2 - set c [ expr $a + $b ] - """ - fd.write(script) - fd.close() - tcl.evalfile(filename) - os.remove(filename) + with open(support.TESTFN, 'w') as f: + self.addCleanup(support.unlink, support.TESTFN) + f.write("""set a 1 + set b 2 + set c [ expr $a + $b ] + """) + tcl.evalfile(support.TESTFN) self.assertEqual(tcl.eval('set a'),'1') self.assertEqual(tcl.eval('set b'),'2') self.assertEqual(tcl.eval('set c'),'3') + def test_evalfile_null_in_result(self): + tcl = self.interp + with open(support.TESTFN, 'w') as f: + self.addCleanup(support.unlink, support.TESTFN) + f.write(""" + set a "a\0b" + set b "a\\0b" + """) + tcl.evalfile(support.TESTFN) + self.assertEqual(tcl.eval('set a'), 'a\x00b') + self.assertEqual(tcl.eval('set b'), 'a\x00b') + def testEvalFileException(self): tcl = self.interp filename = "doesnotexists" @@ -209,6 +222,7 @@ check('"abc"', 'abc') check('"a\xbd\u20ac"', 'a\xbd\u20ac') check(r'"a\xbd\u20ac"', 'a\xbd\u20ac') + check(r'"a\0b"', 'a\x00b') def test_exprdouble(self): tcl = self.interp @@ -320,6 +334,11 @@ self.assertEqual(passValue(False), False if self.wantobjects else '0') self.assertEqual(passValue('string'), 'string') self.assertEqual(passValue('string\u20ac'), 'string\u20ac') + self.assertEqual(passValue('str\x00ing'), 'str\x00ing') + self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd') + self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac') + self.assertEqual(passValue(b'str\x00ing'), 'str\x00ing') + self.assertEqual(passValue(b'str\xc0\x80ing'), 'str\x00ing') for i in (0, 1, -1, 2**31-1, -2**31): self.assertEqual(passValue(i), i if self.wantobjects else str(i)) for f in (0.0, 1.0, -1.0, 1/3, @@ -368,6 +387,13 @@ check('string', 'string') check('string\xbd', 'string\xbd') check('string\u20ac', 'string\u20ac') + check(b'string', 'string') + check(b'string\xe2\x82\xac', 'string\u20ac') + check('str\x00ing', 'str\x00ing') + check('str\x00ing\xbd', 'str\x00ing\xbd') + check('str\x00ing\u20ac', 'str\x00ing\u20ac') + check(b'str\xc0\x80ing', 'str\x00ing') + check(b'str\xc0\x80ing\xe2\x82\xac', 'str\x00ing\u20ac') for i in (0, 1, -1, 2**31-1, -2**31): check(i, str(i)) for f in (0.0, 1.0, -1.0): @@ -396,6 +422,7 @@ (b'a\n b\t\r c\n ', ('a', 'b', 'c')), ('a \u20ac', ('a', '\u20ac')), (b'a \xe2\x82\xac', ('a', '\u20ac')), + (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')), ('a {b c}', ('a', 'b c')), (r'a b\ c', ('a', 'b c')), (('a', 'b c'), ('a', 'b c')), @@ -438,6 +465,9 @@ (b'a\n b\t\r c\n ', ('a', 'b', 'c')), ('a \u20ac', ('a', '\u20ac')), (b'a \xe2\x82\xac', ('a', '\u20ac')), + (b'a\xc0\x80b', 'a\x00b'), + (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')), + (b'{a\xc0\x80b c\xc0\x80d', '{a\x00b c\x00d'), ('a {b c}', ('a', ('b', 'c'))), (r'a b\ c', ('a', ('b', 'c'))), (('a', b'b c'), ('a', ('b', 'c'))), diff --git a/Lib/tkinter/test/test_tkinter/test_variables.py b/Lib/tkinter/test/test_tkinter/test_variables.py --- a/Lib/tkinter/test/test_tkinter/test_variables.py +++ b/Lib/tkinter/test/test_tkinter/test_variables.py @@ -68,6 +68,18 @@ with self.assertRaises(TypeError): Variable(self.root, name=123) + def test_null_in_name(self): + with self.assertRaises(ValueError): + Variable(self.root, name='var\x00name') + with self.assertRaises(ValueError): + self.root.globalsetvar('var\x00name', "value") + with self.assertRaises(ValueError): + self.root.globalsetvar(b'var\x00name', "value") + with self.assertRaises(ValueError): + self.root.setvar('var\x00name', "value") + with self.assertRaises(ValueError): + self.root.setvar(b'var\x00name', "value") + def test_initialize(self): v = Var() self.assertFalse(v.side_effect) @@ -87,6 +99,12 @@ self.root.globalsetvar("name", "value") self.assertEqual("value", v.get()) + def test_get_null(self): + v = StringVar(self.root, "abc\x00def", "name") + self.assertEqual("abc\x00def", v.get()) + self.root.globalsetvar("name", "val\x00ue") + self.assertEqual("val\x00ue", v.get()) + class TestIntVar(TestBase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,9 @@ Library ------- +- Issue #20368: The null character now correctly passed from Tcl to Python. + Improved error handling in variables-related commands. + - Issue #20435: Fix _pyio.StringIO.getvalue() to take into account newline translation settings. diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -419,6 +419,51 @@ static PyObject * +unicodeFromTclStringAndSize(const char *s, Py_ssize_t size) +{ + PyObject *r = PyUnicode_DecodeUTF8(s, size, NULL); + if (!r && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) { + /* Tcl encodes null character as \xc0\x80 */ + if (memchr(s, '\xc0', size)) { + char *buf, *q; + const char *e = s + size; + PyErr_Clear(); + q = buf = (char *)PyMem_Malloc(size); + if (buf == NULL) + return NULL; + while (s != e) { + if (s + 1 != e && s[0] == '\xc0' && s[1] == '\x80') { + *q++ = '\0'; + s += 2; + } + else + *q++ = *s++; + } + s = buf; + size = q - s; + r = PyUnicode_DecodeUTF8(s, size, NULL); + PyMem_Free(buf); + } + } + return r; +} + +static PyObject * +unicodeFromTclString(const char *s) +{ + return unicodeFromTclStringAndSize(s, strlen(s)); +} + +static PyObject * +unicodeFromTclObj(Tcl_Obj *value) +{ + int len; + char *s = Tcl_GetStringFromObj(value, &len); + return unicodeFromTclStringAndSize(s, len); +} + + +static PyObject * Split(char *list) { int argc; @@ -435,13 +480,13 @@ * Could be a quoted string containing funnies, e.g. {"}. * Return the string itself. */ - return PyUnicode_FromString(list); + return unicodeFromTclString(list); } if (argc == 0) v = PyUnicode_FromString(""); else if (argc == 1) - v = PyUnicode_FromString(argv[0]); + v = unicodeFromTclString(argv[0]); else if ((v = PyTuple_New(argc)) != NULL) { int i; PyObject *w; @@ -780,11 +825,8 @@ static PyObject * PyTclObject_string(PyTclObject *self, void *ignored) { - char *s; - int len; if (!self->string) { - s = Tcl_GetStringFromObj(self->value, &len); - self->string = PyUnicode_FromStringAndSize(s, len); + self->string = unicodeFromTclObj(self->value); if (!self->string) return NULL; } @@ -795,15 +837,12 @@ static PyObject * PyTclObject_str(PyTclObject *self, void *ignored) { - char *s; - int len; - if (self->string && PyUnicode_Check(self->string)) { + if (self->string) { Py_INCREF(self->string); return self->string; } /* XXX Could chache result if it is non-ASCII. */ - s = Tcl_GetStringFromObj(self->value, &len); - return PyUnicode_DecodeUTF8(s, len, "strict"); + return unicodeFromTclObj(self->value); } static PyObject * @@ -873,7 +912,7 @@ static PyObject* get_typename(PyTclObject* obj, void* ignored) { - return PyUnicode_FromString(obj->value->typePtr->name); + return unicodeFromTclString(obj->value->typePtr->name); } @@ -985,6 +1024,8 @@ return NULL; } kind = PyUnicode_KIND(value); + if (kind == sizeof(Tcl_UniChar)) + return Tcl_NewUnicodeObj(inbuf, size); allocsize = ((size_t)size) * sizeof(Tcl_UniChar); outbuf = (Tcl_UniChar*)ckalloc(allocsize); /* Else overflow occurred, and we take the next exit */ @@ -1035,8 +1076,7 @@ TkappObject *app = (TkappObject*)tkapp; if (value->typePtr == NULL) { - return PyUnicode_FromStringAndSize(value->bytes, - value->length); + return unicodeFromTclStringAndSize(value->bytes, value->length); } if (value->typePtr == app->BooleanType) { @@ -1093,15 +1133,9 @@ } if (value->typePtr == app->StringType) { -#if TCL_UTF_MAX==3 return PyUnicode_FromKindAndData( - PyUnicode_2BYTE_KIND, Tcl_GetUnicode(value), + sizeof(Tcl_UniChar), Tcl_GetUnicode(value), Tcl_GetCharLength(value)); -#else - return PyUnicode_FromKindAndData( - PyUnicode_4BYTE_KIND, Tcl_GetUnicode(value), - Tcl_GetCharLength(value)); -#endif } return newPyTclObject(value); @@ -1195,8 +1229,8 @@ Tkapp_CallResult(TkappObject *self) { PyObject *res = NULL; + Tcl_Obj *value = Tcl_GetObjResult(self->interp); if(self->wantobjects) { - Tcl_Obj *value = Tcl_GetObjResult(self->interp); /* Not sure whether the IncrRef is necessary, but something may overwrite the interpreter result while we are converting it. */ @@ -1204,7 +1238,7 @@ res = FromObj((PyObject*)self, value); Tcl_DecrRefCount(value); } else { - res = PyUnicode_FromString(Tcl_GetStringResult(self->interp)); + res = unicodeFromTclObj(value); } return res; } @@ -1395,7 +1429,7 @@ if (err == TCL_ERROR) res = Tkinter_Error(self); else - res = PyUnicode_FromString(Tkapp_Result(self)); + res = unicodeFromTclString(Tkapp_Result(self)); LEAVE_OVERLAP_TCL return res; } @@ -1445,9 +1479,8 @@ ENTER_OVERLAP if (err == TCL_ERROR) res = Tkinter_Error(self); - else - res = PyUnicode_FromString(Tkapp_Result(self)); + res = unicodeFromTclString(Tkapp_Result(self)); LEAVE_OVERLAP_TCL return res; } @@ -1470,7 +1503,7 @@ if (err == TCL_ERROR) res = Tkinter_Error(self); else - res = PyUnicode_FromString(Tkapp_Result(self)); + res = unicodeFromTclString(Tkapp_Result(self)); LEAVE_OVERLAP_TCL return res; } @@ -1517,20 +1550,42 @@ static int varname_converter(PyObject *in, void *_out) { + char *s; char **out = (char**)_out; if (PyBytes_Check(in)) { - *out = PyBytes_AsString(in); + if (PyBytes_Size(in) > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "bytes object is too long"); + return 0; + } + s = PyBytes_AsString(in); + if (strlen(s) != PyBytes_Size(in)) { + PyErr_SetString(PyExc_ValueError, "null byte in bytes object"); + return 0; + } + *out = s; return 1; } if (PyUnicode_Check(in)) { - *out = _PyUnicode_AsString(in); + Py_ssize_t size; + s = PyUnicode_AsUTF8AndSize(in, &size); + if (size > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "string is too long"); + return 0; + } + if (strlen(s) != size) { + PyErr_SetString(PyExc_ValueError, "null character in string"); + return 0; + } + *out = s; return 1; } if (PyTclObject_Check(in)) { *out = PyTclObject_TclString(in); return 1; } - /* XXX: Should give diagnostics. */ + PyErr_Format(PyExc_TypeError, + "must be str, bytes or Tcl_Obj, not %.50s", + in->ob_type->tp_name); return 0; } @@ -1616,8 +1671,11 @@ PyObject *res = NULL; Tcl_Obj *newval, *ok; - if (PyArg_ParseTuple(args, "O&O:setvar", - varname_converter, &name1, &newValue)) { + switch (PyTuple_GET_SIZE(args)) { + case 2: + if (!PyArg_ParseTuple(args, "O&O:setvar", + varname_converter, &name1, &newValue)) + return NULL; /* XXX Acquire tcl lock??? */ newval = AsObj(newValue); if (newval == NULL) @@ -1633,27 +1691,27 @@ Py_INCREF(res); } LEAVE_OVERLAP_TCL - } - else { - PyErr_Clear(); - if (PyArg_ParseTuple(args, "ssO:setvar", - &name1, &name2, &newValue)) { - /* XXX must hold tcl lock already??? */ - newval = AsObj(newValue); - ENTER_TCL - ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, name2, newval, flags); - ENTER_OVERLAP - if (!ok) - Tkinter_Error(self); - else { - res = Py_None; - Py_INCREF(res); - } - LEAVE_OVERLAP_TCL + break; + case 3: + if (!PyArg_ParseTuple(args, "ssO:setvar", + &name1, &name2, &newValue)) + return NULL; + /* XXX must hold tcl lock already??? */ + newval = AsObj(newValue); + ENTER_TCL + ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, name2, newval, flags); + ENTER_OVERLAP + if (!ok) + Tkinter_Error(self); + else { + res = Py_None; + Py_INCREF(res); } - else { - return NULL; - } + LEAVE_OVERLAP_TCL + break; + default: + PyErr_SetString(PyExc_TypeError, "setvar requires 2 to 3 arguments"); + return NULL; } return res; } @@ -1693,7 +1751,7 @@ res = FromObj(self, tres); } else { - res = PyUnicode_FromString(Tcl_GetString(tres)); + res = unicodeFromTclObj(tres); } } LEAVE_OVERLAP_TCL @@ -1831,7 +1889,7 @@ if (retval == TCL_ERROR) res = Tkinter_Error(self); else - res = Py_BuildValue("s", Tkapp_Result(self)); + res = unicodeFromTclString(Tkapp_Result(self)); LEAVE_OVERLAP_TCL return res; } @@ -1956,7 +2014,7 @@ goto finally; for (i = 0; i < argc; i++) { - PyObject *s = PyUnicode_FromString(argv[i]); + PyObject *s = unicodeFromTclString(argv[i]); if (!s || PyTuple_SetItem(v, i, s)) { Py_DECREF(v); v = NULL; @@ -2075,20 +2133,8 @@ return PythonCmd_Error(interp); for (i = 0; i < (argc - 1); i++) { - PyObject *s = PyUnicode_FromString(argv[i + 1]); - if (!s) { - /* Is Tk leaking 0xC080 in %A - a "modified" utf-8 null? */ - if (PyErr_ExceptionMatches(PyExc_UnicodeDecodeError) && - !strcmp(argv[i + 1], "\xC0\x80")) { - PyErr_Clear(); - /* Convert to "strict" utf-8 null */ - s = PyUnicode_FromString("\0"); - } else { - Py_DECREF(arg); - return PythonCmd_Error(interp); - } - } - if (PyTuple_SetItem(arg, i, s)) { + PyObject *s = unicodeFromTclString(argv[i + 1]); + if (!s || PyTuple_SetItem(arg, i, s)) { Py_DECREF(arg); return PythonCmd_Error(interp); } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:41 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:41 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320368=3A_The_null_character_now_correctly_passe?= =?utf-8?q?d_from_Tcl_to_Python=2E?= Message-ID: <3fHzrY0CWVz7LmD@mail.python.org> http://hg.python.org/cpython/rev/321b714653e3 changeset: 88935:321b714653e3 parent: 88932:28ec384e7dcc parent: 88934:08e3343f01a5 user: Serhiy Storchaka date: Mon Feb 03 21:25:56 2014 +0200 summary: Issue #20368: The null character now correctly passed from Tcl to Python. Improved error handling in variables-related commands. files: Lib/test/test_tcl.py | 50 ++- Lib/tkinter/test/test_tkinter/test_variables.py | 18 + Misc/NEWS | 3 + Modules/_tkinter.c | 184 ++++++--- 4 files changed, 176 insertions(+), 79 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -55,6 +55,10 @@ tcl.eval('set a 1') self.assertEqual(tcl.eval('set a'),'1') + def test_eval_null_in_result(self): + tcl = self.interp + self.assertEqual(tcl.eval('set a "a\\0b"'), 'a\x00b') + def testEvalException(self): tcl = self.interp self.assertRaises(TclError,tcl.eval,'set a') @@ -127,20 +131,29 @@ def testEvalFile(self): tcl = self.interp - filename = "testEvalFile.tcl" - fd = open(filename,'w') - script = """set a 1 - set b 2 - set c [ expr $a + $b ] - """ - fd.write(script) - fd.close() - tcl.evalfile(filename) - os.remove(filename) + with open(support.TESTFN, 'w') as f: + self.addCleanup(support.unlink, support.TESTFN) + f.write("""set a 1 + set b 2 + set c [ expr $a + $b ] + """) + tcl.evalfile(support.TESTFN) self.assertEqual(tcl.eval('set a'),'1') self.assertEqual(tcl.eval('set b'),'2') self.assertEqual(tcl.eval('set c'),'3') + def test_evalfile_null_in_result(self): + tcl = self.interp + with open(support.TESTFN, 'w') as f: + self.addCleanup(support.unlink, support.TESTFN) + f.write(""" + set a "a\0b" + set b "a\\0b" + """) + tcl.evalfile(support.TESTFN) + self.assertEqual(tcl.eval('set a'), 'a\x00b') + self.assertEqual(tcl.eval('set b'), 'a\x00b') + def testEvalFileException(self): tcl = self.interp filename = "doesnotexists" @@ -209,6 +222,7 @@ check('"abc"', 'abc') check('"a\xbd\u20ac"', 'a\xbd\u20ac') check(r'"a\xbd\u20ac"', 'a\xbd\u20ac') + check(r'"a\0b"', 'a\x00b') def test_exprdouble(self): tcl = self.interp @@ -320,6 +334,11 @@ self.assertEqual(passValue(False), False if self.wantobjects else '0') self.assertEqual(passValue('string'), 'string') self.assertEqual(passValue('string\u20ac'), 'string\u20ac') + self.assertEqual(passValue('str\x00ing'), 'str\x00ing') + self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd') + self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac') + self.assertEqual(passValue(b'str\x00ing'), 'str\x00ing') + self.assertEqual(passValue(b'str\xc0\x80ing'), 'str\x00ing') for i in (0, 1, -1, 2**31-1, -2**31): self.assertEqual(passValue(i), i if self.wantobjects else str(i)) for f in (0.0, 1.0, -1.0, 1/3, @@ -368,6 +387,13 @@ check('string', 'string') check('string\xbd', 'string\xbd') check('string\u20ac', 'string\u20ac') + check(b'string', 'string') + check(b'string\xe2\x82\xac', 'string\u20ac') + check('str\x00ing', 'str\x00ing') + check('str\x00ing\xbd', 'str\x00ing\xbd') + check('str\x00ing\u20ac', 'str\x00ing\u20ac') + check(b'str\xc0\x80ing', 'str\x00ing') + check(b'str\xc0\x80ing\xe2\x82\xac', 'str\x00ing\u20ac') for i in (0, 1, -1, 2**31-1, -2**31): check(i, str(i)) for f in (0.0, 1.0, -1.0): @@ -396,6 +422,7 @@ (b'a\n b\t\r c\n ', ('a', 'b', 'c')), ('a \u20ac', ('a', '\u20ac')), (b'a \xe2\x82\xac', ('a', '\u20ac')), + (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')), ('a {b c}', ('a', 'b c')), (r'a b\ c', ('a', 'b c')), (('a', 'b c'), ('a', 'b c')), @@ -438,6 +465,9 @@ (b'a\n b\t\r c\n ', ('a', 'b', 'c')), ('a \u20ac', ('a', '\u20ac')), (b'a \xe2\x82\xac', ('a', '\u20ac')), + (b'a\xc0\x80b', 'a\x00b'), + (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')), + (b'{a\xc0\x80b c\xc0\x80d', '{a\x00b c\x00d'), ('a {b c}', ('a', ('b', 'c'))), (r'a b\ c', ('a', ('b', 'c'))), (('a', b'b c'), ('a', ('b', 'c'))), diff --git a/Lib/tkinter/test/test_tkinter/test_variables.py b/Lib/tkinter/test/test_tkinter/test_variables.py --- a/Lib/tkinter/test/test_tkinter/test_variables.py +++ b/Lib/tkinter/test/test_tkinter/test_variables.py @@ -68,6 +68,18 @@ with self.assertRaises(TypeError): Variable(self.root, name=123) + def test_null_in_name(self): + with self.assertRaises(ValueError): + Variable(self.root, name='var\x00name') + with self.assertRaises(ValueError): + self.root.globalsetvar('var\x00name', "value") + with self.assertRaises(ValueError): + self.root.globalsetvar(b'var\x00name', "value") + with self.assertRaises(ValueError): + self.root.setvar('var\x00name', "value") + with self.assertRaises(ValueError): + self.root.setvar(b'var\x00name', "value") + def test_initialize(self): v = Var() self.assertFalse(v.side_effect) @@ -87,6 +99,12 @@ self.root.globalsetvar("name", "value") self.assertEqual("value", v.get()) + def test_get_null(self): + v = StringVar(self.root, "abc\x00def", "name") + self.assertEqual("abc\x00def", v.get()) + self.root.globalsetvar("name", "val\x00ue") + self.assertEqual("val\x00ue", v.get()) + class TestIntVar(TestBase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,9 @@ Library ------- +- Issue #20368: The null character now correctly passed from Tcl to Python. + Improved error handling in variables-related commands. + - Issue #20435: Fix _pyio.StringIO.getvalue() to take into account newline translation settings. diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -329,6 +329,51 @@ static PyObject * +unicodeFromTclStringAndSize(const char *s, Py_ssize_t size) +{ + PyObject *r = PyUnicode_DecodeUTF8(s, size, NULL); + if (!r && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) { + /* Tcl encodes null character as \xc0\x80 */ + if (memchr(s, '\xc0', size)) { + char *buf, *q; + const char *e = s + size; + PyErr_Clear(); + q = buf = (char *)PyMem_Malloc(size); + if (buf == NULL) + return NULL; + while (s != e) { + if (s + 1 != e && s[0] == '\xc0' && s[1] == '\x80') { + *q++ = '\0'; + s += 2; + } + else + *q++ = *s++; + } + s = buf; + size = q - s; + r = PyUnicode_DecodeUTF8(s, size, NULL); + PyMem_Free(buf); + } + } + return r; +} + +static PyObject * +unicodeFromTclString(const char *s) +{ + return unicodeFromTclStringAndSize(s, strlen(s)); +} + +static PyObject * +unicodeFromTclObj(Tcl_Obj *value) +{ + int len; + char *s = Tcl_GetStringFromObj(value, &len); + return unicodeFromTclStringAndSize(s, len); +} + + +static PyObject * Split(char *list) { int argc; @@ -344,13 +389,13 @@ * Could be a quoted string containing funnies, e.g. {"}. * Return the string itself. */ - return PyUnicode_FromString(list); + return unicodeFromTclString(list); } if (argc == 0) v = PyUnicode_FromString(""); else if (argc == 1) - v = PyUnicode_FromString(argv[0]); + v = unicodeFromTclString(argv[0]); else if ((v = PyTuple_New(argc)) != NULL) { int i; PyObject *w; @@ -694,11 +739,8 @@ static PyObject * PyTclObject_string(PyTclObject *self, void *ignored) { - char *s; - int len; if (!self->string) { - s = Tcl_GetStringFromObj(self->value, &len); - self->string = PyUnicode_FromStringAndSize(s, len); + self->string = unicodeFromTclObj(self->value); if (!self->string) return NULL; } @@ -709,15 +751,12 @@ static PyObject * PyTclObject_str(PyTclObject *self, void *ignored) { - char *s; - int len; - if (self->string && PyUnicode_Check(self->string)) { + if (self->string) { Py_INCREF(self->string); return self->string; } /* XXX Could chache result if it is non-ASCII. */ - s = Tcl_GetStringFromObj(self->value, &len); - return PyUnicode_DecodeUTF8(s, len, "strict"); + return unicodeFromTclObj(self->value); } static PyObject * @@ -792,7 +831,7 @@ static PyObject* get_typename(PyTclObject* obj, void* ignored) { - return PyUnicode_FromString(obj->value->typePtr->name); + return unicodeFromTclString(obj->value->typePtr->name); } @@ -879,6 +918,8 @@ return NULL; } kind = PyUnicode_KIND(value); + if (kind == sizeof(Tcl_UniChar)) + return Tcl_NewUnicodeObj(inbuf, size); allocsize = ((size_t)size) * sizeof(Tcl_UniChar); outbuf = (Tcl_UniChar*)ckalloc(allocsize); /* Else overflow occurred, and we take the next exit */ @@ -929,8 +970,7 @@ TkappObject *app = (TkappObject*)tkapp; if (value->typePtr == NULL) { - return PyUnicode_FromStringAndSize(value->bytes, - value->length); + return unicodeFromTclStringAndSize(value->bytes, value->length); } if (value->typePtr == app->BooleanType) { @@ -987,15 +1027,9 @@ } if (value->typePtr == app->StringType) { -#if TCL_UTF_MAX==3 return PyUnicode_FromKindAndData( - PyUnicode_2BYTE_KIND, Tcl_GetUnicode(value), + sizeof(Tcl_UniChar), Tcl_GetUnicode(value), Tcl_GetCharLength(value)); -#else - return PyUnicode_FromKindAndData( - PyUnicode_4BYTE_KIND, Tcl_GetUnicode(value), - Tcl_GetCharLength(value)); -#endif } return newPyTclObject(value); @@ -1089,8 +1123,8 @@ Tkapp_CallResult(TkappObject *self) { PyObject *res = NULL; + Tcl_Obj *value = Tcl_GetObjResult(self->interp); if(self->wantobjects) { - Tcl_Obj *value = Tcl_GetObjResult(self->interp); /* Not sure whether the IncrRef is necessary, but something may overwrite the interpreter result while we are converting it. */ @@ -1098,7 +1132,7 @@ res = FromObj((PyObject*)self, value); Tcl_DecrRefCount(value); } else { - res = PyUnicode_FromString(Tcl_GetStringResult(self->interp)); + res = unicodeFromTclObj(value); } return res; } @@ -1253,7 +1287,7 @@ if (err == TCL_ERROR) res = Tkinter_Error(self); else - res = PyUnicode_FromString(Tkapp_Result(self)); + res = unicodeFromTclString(Tkapp_Result(self)); LEAVE_OVERLAP_TCL return res; } @@ -1275,9 +1309,8 @@ ENTER_OVERLAP if (err == TCL_ERROR) res = Tkinter_Error(self); - else - res = PyUnicode_FromString(Tkapp_Result(self)); + res = unicodeFromTclString(Tkapp_Result(self)); LEAVE_OVERLAP_TCL return res; } @@ -1300,7 +1333,7 @@ if (err == TCL_ERROR) res = Tkinter_Error(self); else - res = PyUnicode_FromString(Tkapp_Result(self)); + res = unicodeFromTclString(Tkapp_Result(self)); LEAVE_OVERLAP_TCL return res; } @@ -1346,20 +1379,42 @@ static int varname_converter(PyObject *in, void *_out) { + char *s; char **out = (char**)_out; if (PyBytes_Check(in)) { - *out = PyBytes_AsString(in); + if (PyBytes_Size(in) > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "bytes object is too long"); + return 0; + } + s = PyBytes_AsString(in); + if (strlen(s) != PyBytes_Size(in)) { + PyErr_SetString(PyExc_ValueError, "null byte in bytes object"); + return 0; + } + *out = s; return 1; } if (PyUnicode_Check(in)) { - *out = _PyUnicode_AsString(in); + Py_ssize_t size; + s = PyUnicode_AsUTF8AndSize(in, &size); + if (size > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "string is too long"); + return 0; + } + if (strlen(s) != size) { + PyErr_SetString(PyExc_ValueError, "null character in string"); + return 0; + } + *out = s; return 1; } if (PyTclObject_Check(in)) { *out = PyTclObject_TclString(in); return 1; } - /* XXX: Should give diagnostics. */ + PyErr_Format(PyExc_TypeError, + "must be str, bytes or Tcl_Obj, not %.50s", + in->ob_type->tp_name); return 0; } @@ -1445,8 +1500,11 @@ PyObject *res = NULL; Tcl_Obj *newval, *ok; - if (PyArg_ParseTuple(args, "O&O:setvar", - varname_converter, &name1, &newValue)) { + switch (PyTuple_GET_SIZE(args)) { + case 2: + if (!PyArg_ParseTuple(args, "O&O:setvar", + varname_converter, &name1, &newValue)) + return NULL; /* XXX Acquire tcl lock??? */ newval = AsObj(newValue); if (newval == NULL) @@ -1462,27 +1520,27 @@ Py_INCREF(res); } LEAVE_OVERLAP_TCL - } - else { - PyErr_Clear(); - if (PyArg_ParseTuple(args, "ssO:setvar", - &name1, &name2, &newValue)) { - /* XXX must hold tcl lock already??? */ - newval = AsObj(newValue); - ENTER_TCL - ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, name2, newval, flags); - ENTER_OVERLAP - if (!ok) - Tkinter_Error(self); - else { - res = Py_None; - Py_INCREF(res); - } - LEAVE_OVERLAP_TCL + break; + case 3: + if (!PyArg_ParseTuple(args, "ssO:setvar", + &name1, &name2, &newValue)) + return NULL; + /* XXX must hold tcl lock already??? */ + newval = AsObj(newValue); + ENTER_TCL + ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, name2, newval, flags); + ENTER_OVERLAP + if (!ok) + Tkinter_Error(self); + else { + res = Py_None; + Py_INCREF(res); } - else { - return NULL; - } + LEAVE_OVERLAP_TCL + break; + default: + PyErr_SetString(PyExc_TypeError, "setvar requires 2 to 3 arguments"); + return NULL; } return res; } @@ -1523,7 +1581,7 @@ res = FromObj(self, tres); } else { - res = PyUnicode_FromString(Tcl_GetString(tres)); + res = unicodeFromTclObj(tres); } } LEAVE_OVERLAP_TCL @@ -1662,7 +1720,7 @@ if (retval == TCL_ERROR) res = Tkinter_Error(self); else - res = Py_BuildValue("s", Tkapp_Result(self)); + res = unicodeFromTclString(Tkapp_Result(self)); LEAVE_OVERLAP_TCL return res; } @@ -1787,7 +1845,7 @@ goto finally; for (i = 0; i < argc; i++) { - PyObject *s = PyUnicode_FromString(argv[i]); + PyObject *s = unicodeFromTclString(argv[i]); if (!s || PyTuple_SetItem(v, i, s)) { Py_DECREF(v); v = NULL; @@ -1885,20 +1943,8 @@ return PythonCmd_Error(interp); for (i = 0; i < (argc - 1); i++) { - PyObject *s = PyUnicode_FromString(argv[i + 1]); - if (!s) { - /* Is Tk leaking 0xC080 in %A - a "modified" utf-8 null? */ - if (PyErr_ExceptionMatches(PyExc_UnicodeDecodeError) && - !strcmp(argv[i + 1], "\xC0\x80")) { - PyErr_Clear(); - /* Convert to "strict" utf-8 null */ - s = PyUnicode_FromString("\0"); - } else { - Py_DECREF(arg); - return PythonCmd_Error(interp); - } - } - if (PyTuple_SetItem(arg, i, s)) { + PyObject *s = unicodeFromTclString(argv[i + 1]); + if (!s || PyTuple_SetItem(arg, i, s)) { Py_DECREF(arg); return PythonCmd_Error(interp); } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:42 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:42 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5NzYx?= =?utf-8?q?=3A_Fixed_Tkinter_tests_on_OS_X=2E?= Message-ID: <3fHzrZ2104z7Lmy@mail.python.org> http://hg.python.org/cpython/rev/129eb818d9b2 changeset: 88936:129eb818d9b2 branch: 2.7 parent: 88933:65c29c07bb31 user: Serhiy Storchaka date: Mon Feb 03 21:33:21 2014 +0200 summary: Issue #19761: Fixed Tkinter tests on OS X. files: Lib/lib-tk/test/test_tkinter/test_widgets.py | 9 ++++--- Lib/lib-tk/test/widget_tests.py | 12 +++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Lib/lib-tk/test/test_tkinter/test_widgets.py b/Lib/lib-tk/test/test_tkinter/test_widgets.py --- a/Lib/lib-tk/test/test_tkinter/test_widgets.py +++ b/Lib/lib-tk/test/test_tkinter/test_widgets.py @@ -326,10 +326,11 @@ self.checkColorParam(widget, 'disabledbackground') def test_insertborderwidth(self): - widget = self.create() - self.checkPixelsParam(widget, 'insertborderwidth', 0, 1.3, -2) - self.checkParam(widget, 'insertborderwidth', 2, expected=1) - self.checkParam(widget, 'insertborderwidth', '10p', expected=1) + widget = self.create(insertwidth=100) + self.checkPixelsParam(widget, 'insertborderwidth', + 0, 1.3, 2.6, 6, -2, '10p') + # insertborderwidth is bounded above by a half of insertwidth. + self.checkParam(widget, 'insertborderwidth', 60, expected=100//2) def test_insertwidth(self): widget = self.create() diff --git a/Lib/lib-tk/test/widget_tests.py b/Lib/lib-tk/test/widget_tests.py --- a/Lib/lib-tk/test/widget_tests.py +++ b/Lib/lib-tk/test/widget_tests.py @@ -6,6 +6,7 @@ from ttk import setup_master, Scale from test_ttk.support import (tcl_version, requires_tcl, get_tk_patchlevel, pixels_conv, tcl_obj_eq) +import test.test_support noconv = noconv_meth = False @@ -253,8 +254,14 @@ widget = self.create() self.checkParam(widget, 'bitmap', 'questhead') self.checkParam(widget, 'bitmap', 'gray50') - self.checkInvalidParam(widget, 'bitmap', 'spam', - errmsg='bitmap "spam" not defined') + filename = test.test_support.findfile('python.xbm', subdir='imghdrdata') + self.checkParam(widget, 'bitmap', '@' + filename) + # Cocoa Tk widgets don't detect invalid -bitmap values + # See https://core.tcl.tk/tk/info/31cd33dbf0 + if not ('aqua' in self.root.tk.call('tk', 'windowingsystem') and + 'AppKit' in self.root.winfo_server()): + self.checkInvalidParam(widget, 'bitmap', 'spam', + errmsg='bitmap "spam" not defined') def test_borderwidth(self): widget = self.create() @@ -514,7 +521,6 @@ return decorator def setUpModule(): - import test.test_support if test.test_support.verbose: tcl = Tkinter.Tcl() print 'patchlevel =', tcl.call('info', 'patchlevel') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:43 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:43 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE5NzYx?= =?utf-8?q?=3A_Fixed_Tkinter_tests_on_OS_X=2E?= Message-ID: <3fHzrb3pBfz7Lll@mail.python.org> http://hg.python.org/cpython/rev/76cd2e59b13d changeset: 88937:76cd2e59b13d branch: 3.3 parent: 88934:08e3343f01a5 user: Serhiy Storchaka date: Mon Feb 03 21:33:33 2014 +0200 summary: Issue #19761: Fixed Tkinter tests on OS X. files: Lib/tkinter/test/test_tkinter/test_widgets.py | 9 ++++--- Lib/tkinter/test/widget_tests.py | 12 +++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py --- a/Lib/tkinter/test/test_tkinter/test_widgets.py +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -329,10 +329,11 @@ self.checkColorParam(widget, 'disabledbackground') def test_insertborderwidth(self): - widget = self.create() - self.checkPixelsParam(widget, 'insertborderwidth', 0, 1.3, -2) - self.checkParam(widget, 'insertborderwidth', 2, expected=1) - self.checkParam(widget, 'insertborderwidth', '10p', expected=1) + widget = self.create(insertwidth=100) + self.checkPixelsParam(widget, 'insertborderwidth', + 0, 1.3, 2.6, 6, -2, '10p') + # insertborderwidth is bounded above by a half of insertwidth. + self.checkParam(widget, 'insertborderwidth', 60, expected=100//2) def test_insertwidth(self): widget = self.create() diff --git a/Lib/tkinter/test/widget_tests.py b/Lib/tkinter/test/widget_tests.py --- a/Lib/tkinter/test/widget_tests.py +++ b/Lib/tkinter/test/widget_tests.py @@ -6,6 +6,7 @@ from tkinter.ttk import setup_master, Scale from tkinter.test.support import (tcl_version, requires_tcl, get_tk_patchlevel, pixels_conv, tcl_obj_eq) +import test.support noconv = False @@ -234,8 +235,14 @@ widget = self.create() self.checkParam(widget, 'bitmap', 'questhead') self.checkParam(widget, 'bitmap', 'gray50') - self.checkInvalidParam(widget, 'bitmap', 'spam', - errmsg='bitmap "spam" not defined') + filename = test.support.findfile('python.xbm', subdir='imghdrdata') + self.checkParam(widget, 'bitmap', '@' + filename) + # Cocoa Tk widgets don't detect invalid -bitmap values + # See https://core.tcl.tk/tk/info/31cd33dbf0 + if not ('aqua' in self.root.tk.call('tk', 'windowingsystem') and + 'AppKit' in self.root.winfo_server()): + self.checkInvalidParam(widget, 'bitmap', 'spam', + errmsg='bitmap "spam" not defined') def test_borderwidth(self): widget = self.create() @@ -495,7 +502,6 @@ return decorator def setUpModule(): - import test.support if test.support.verbose: tcl = tkinter.Tcl() print('patchlevel =', tcl.call('info', 'patchlevel')) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:44 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:44 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2319761=3A_Fixed_Tkinter_tests_on_OS_X=2E?= Message-ID: <3fHzrc5F5pz7LnP@mail.python.org> http://hg.python.org/cpython/rev/2efe0778a4cf changeset: 88938:2efe0778a4cf parent: 88935:321b714653e3 parent: 88937:76cd2e59b13d user: Serhiy Storchaka date: Mon Feb 03 21:34:14 2014 +0200 summary: Issue #19761: Fixed Tkinter tests on OS X. files: Lib/tkinter/test/test_tkinter/test_widgets.py | 9 ++++--- Lib/tkinter/test/widget_tests.py | 12 +++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py --- a/Lib/tkinter/test/test_tkinter/test_widgets.py +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -329,10 +329,11 @@ self.checkColorParam(widget, 'disabledbackground') def test_insertborderwidth(self): - widget = self.create() - self.checkPixelsParam(widget, 'insertborderwidth', 0, 1.3, -2) - self.checkParam(widget, 'insertborderwidth', 2, expected=1) - self.checkParam(widget, 'insertborderwidth', '10p', expected=1) + widget = self.create(insertwidth=100) + self.checkPixelsParam(widget, 'insertborderwidth', + 0, 1.3, 2.6, 6, -2, '10p') + # insertborderwidth is bounded above by a half of insertwidth. + self.checkParam(widget, 'insertborderwidth', 60, expected=100//2) def test_insertwidth(self): widget = self.create() diff --git a/Lib/tkinter/test/widget_tests.py b/Lib/tkinter/test/widget_tests.py --- a/Lib/tkinter/test/widget_tests.py +++ b/Lib/tkinter/test/widget_tests.py @@ -6,6 +6,7 @@ from tkinter.ttk import setup_master, Scale from tkinter.test.support import (tcl_version, requires_tcl, get_tk_patchlevel, pixels_conv, tcl_obj_eq) +import test.support noconv = False @@ -234,8 +235,14 @@ widget = self.create() self.checkParam(widget, 'bitmap', 'questhead') self.checkParam(widget, 'bitmap', 'gray50') - self.checkInvalidParam(widget, 'bitmap', 'spam', - errmsg='bitmap "spam" not defined') + filename = test.support.findfile('python.xbm', subdir='imghdrdata') + self.checkParam(widget, 'bitmap', '@' + filename) + # Cocoa Tk widgets don't detect invalid -bitmap values + # See https://core.tcl.tk/tk/info/31cd33dbf0 + if not ('aqua' in self.root.tk.call('tk', 'windowingsystem') and + 'AppKit' in self.root.winfo_server()): + self.checkInvalidParam(widget, 'bitmap', 'spam', + errmsg='bitmap "spam" not defined') def test_borderwidth(self): widget = self.create() @@ -495,7 +502,6 @@ return decorator def setUpModule(): - import test.support if test.support.verbose: tcl = tkinter.Tcl() print('patchlevel =', tcl.call('info', 'patchlevel')) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 20:39:45 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 20:39:45 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_Merge_heads?= Message-ID: <3fHzrd75Vjz7LjN@mail.python.org> http://hg.python.org/cpython/rev/6007e81f5867 changeset: 88939:6007e81f5867 parent: 88938:2efe0778a4cf parent: 88928:825d8e5c8b61 user: Serhiy Storchaka date: Mon Feb 03 21:36:17 2014 +0200 summary: Merge heads files: Doc/library/asyncio-subprocess.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -8,7 +8,7 @@ .. function:: create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) - Run the shell command *cmd* (:class:`str`)`. Return a :class:`Process` + Run the shell command *cmd* given as a string. Return a :class:`Process` instance. This function returns a :ref:`coroutine object `. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 21:01:53 2014 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 3 Feb 2014 21:01:53 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNDI2?= =?utf-8?q?=3A_When_passing_the_re=2EDEBUG_flag=2C_re=2Ecompile=28=29_disp?= =?utf-8?q?lays_the_debug?= Message-ID: <3fJ0L915h9z7LnC@mail.python.org> http://hg.python.org/cpython/rev/a7b180d5df5f changeset: 88940:a7b180d5df5f branch: 3.3 parent: 88937:76cd2e59b13d user: Antoine Pitrou date: Mon Feb 03 20:59:59 2014 +0100 summary: Issue #20426: When passing the re.DEBUG flag, re.compile() displays the debug output every time it is called, regardless of the compilation cache. files: Lib/re.py | 17 ++++++++++------- Lib/test/test_re.py | 15 ++++++++++++++- Misc/NEWS | 3 +++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Lib/re.py b/Lib/re.py --- a/Lib/re.py +++ b/Lib/re.py @@ -267,10 +267,12 @@ _MAXCACHE = 512 def _compile(pattern, flags): # internal: compile pattern - try: - return _cache[type(pattern), pattern, flags] - except KeyError: - pass + bypass_cache = flags & DEBUG + if not bypass_cache: + try: + return _cache[type(pattern), pattern, flags] + except KeyError: + pass if isinstance(pattern, _pattern_type): if flags: raise ValueError( @@ -279,9 +281,10 @@ if not sre_compile.isstring(pattern): raise TypeError("first argument must be string or compiled pattern") p = sre_compile.compile(pattern, flags) - if len(_cache) >= _MAXCACHE: - _cache.clear() - _cache[type(pattern), pattern, flags] = p + if not bypass_cache: + if len(_cache) >= _MAXCACHE: + _cache.clear() + _cache[type(pattern), pattern, flags] = p return p def _compile_repl(repl, pattern): diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1,5 +1,5 @@ from test.support import verbose, run_unittest, gc_collect, bigmemtest, _2G, \ - cpython_only + cpython_only, captured_stdout import io import re from re import Scanner @@ -1064,6 +1064,19 @@ self.assertEqual(m.group(1), "") self.assertEqual(m.group(2), "y") + def test_debug_flag(self): + with captured_stdout() as out: + re.compile('foo', re.DEBUG) + self.assertEqual(out.getvalue().splitlines(), + ['literal 102 ', 'literal 111 ', 'literal 111 ']) + # Debug output is output again even a second time (bypassing + # the cache -- issue #20426). + with captured_stdout() as out: + re.compile('foo', re.DEBUG) + self.assertEqual(out.getvalue().splitlines(), + ['literal 102 ', 'literal 111 ', 'literal 111 ']) + + def run_re_tests(): from test.re_tests import tests, SUCCEED, FAIL, SYNTAX_ERROR if verbose: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,9 @@ Library ------- +- Issue #20426: When passing the re.DEBUG flag, re.compile() displays the + debug output every time it is called, regardless of the compilation cache. + - Issue #20368: The null character now correctly passed from Tcl to Python. Improved error handling in variables-related commands. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 21:01:54 2014 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 3 Feb 2014 21:01:54 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320426=3A_When_passing_the_re=2EDEBUG_flag=2C_re?= =?utf-8?q?=2Ecompile=28=29_displays_the_debug?= Message-ID: <3fJ0LB2pDMz7Lnh@mail.python.org> http://hg.python.org/cpython/rev/a8bcfa290e68 changeset: 88941:a8bcfa290e68 parent: 88939:6007e81f5867 parent: 88940:a7b180d5df5f user: Antoine Pitrou date: Mon Feb 03 21:01:35 2014 +0100 summary: Issue #20426: When passing the re.DEBUG flag, re.compile() displays the debug output every time it is called, regardless of the compilation cache. files: Lib/re.py | 17 ++++++++++------- Lib/test/test_re.py | 14 +++++++++++++- Misc/NEWS | 3 +++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Lib/re.py b/Lib/re.py --- a/Lib/re.py +++ b/Lib/re.py @@ -272,10 +272,12 @@ _MAXCACHE = 512 def _compile(pattern, flags): # internal: compile pattern - try: - return _cache[type(pattern), pattern, flags] - except KeyError: - pass + bypass_cache = flags & DEBUG + if not bypass_cache: + try: + return _cache[type(pattern), pattern, flags] + except KeyError: + pass if isinstance(pattern, _pattern_type): if flags: raise ValueError( @@ -284,9 +286,10 @@ if not sre_compile.isstring(pattern): raise TypeError("first argument must be string or compiled pattern") p = sre_compile.compile(pattern, flags) - if len(_cache) >= _MAXCACHE: - _cache.clear() - _cache[type(pattern), pattern, flags] = p + if not bypass_cache: + if len(_cache) >= _MAXCACHE: + _cache.clear() + _cache[type(pattern), pattern, flags] = p return p def _compile_repl(repl, pattern): diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1,5 +1,5 @@ from test.support import verbose, run_unittest, gc_collect, bigmemtest, _2G, \ - cpython_only + cpython_only, captured_stdout import io import re from re import Scanner @@ -1193,6 +1193,18 @@ self.assertEqual(m.group(1), "") self.assertEqual(m.group(2), "y") + def test_debug_flag(self): + with captured_stdout() as out: + re.compile('foo', re.DEBUG) + self.assertEqual(out.getvalue().splitlines(), + ['literal 102 ', 'literal 111 ', 'literal 111 ']) + # Debug output is output again even a second time (bypassing + # the cache -- issue #20426). + with captured_stdout() as out: + re.compile('foo', re.DEBUG) + self.assertEqual(out.getvalue().splitlines(), + ['literal 102 ', 'literal 111 ', 'literal 111 ']) + class PatternReprTests(unittest.TestCase): def check(self, pattern, expected): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,9 @@ Library ------- +- Issue #20426: When passing the re.DEBUG flag, re.compile() displays the + debug output every time it is called, regardless of the compilation cache. + - Issue #20368: The null character now correctly passed from Tcl to Python. Improved error handling in variables-related commands. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 21:12:54 2014 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 3 Feb 2014 21:12:54 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNDI2?= =?utf-8?q?=3A_When_passing_the_re=2EDEBUG_flag=2C_re=2Ecompile=28=29_disp?= =?utf-8?q?lays_the_debug?= Message-ID: <3fJ0Zt4TcTz7LjN@mail.python.org> http://hg.python.org/cpython/rev/e47f6883dedf changeset: 88942:e47f6883dedf branch: 2.7 parent: 88936:129eb818d9b2 user: Antoine Pitrou date: Mon Feb 03 20:59:59 2014 +0100 summary: Issue #20426: When passing the re.DEBUG flag, re.compile() displays the debug output every time it is called, regardless of the compilation cache. files: Lib/re.py | 17 ++++++++++------- Lib/test/test_re.py | 14 ++++++++++++++ Misc/NEWS | 3 +++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Lib/re.py b/Lib/re.py --- a/Lib/re.py +++ b/Lib/re.py @@ -225,11 +225,13 @@ def _compile(*key): # internal: compile pattern - cachekey = (type(key[0]),) + key - p = _cache.get(cachekey) - if p is not None: - return p pattern, flags = key + bypass_cache = flags & DEBUG + if not bypass_cache: + cachekey = (type(key[0]),) + key + p = _cache.get(cachekey) + if p is not None: + return p if isinstance(pattern, _pattern_type): if flags: raise ValueError('Cannot process flags argument with a compiled pattern') @@ -240,9 +242,10 @@ p = sre_compile.compile(pattern, flags) except error, v: raise error, v # invalid expression - if len(_cache) >= _MAXCACHE: - _cache.clear() - _cache[cachekey] = p + if not bypass_cache: + if len(_cache) >= _MAXCACHE: + _cache.clear() + _cache[cachekey] = p return p def _compile_repl(*key): diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1,5 +1,6 @@ from test.test_support import verbose, run_unittest, import_module from test.test_support import precisionbigmemtest, _2G, cpython_only +from test.test_support import captured_stdout import re from re import Scanner import sre_constants @@ -920,6 +921,19 @@ self.assertEqual(m.group(1), "") self.assertEqual(m.group(2), "y") + def test_debug_flag(self): + with captured_stdout() as out: + re.compile('foo', re.DEBUG) + self.assertEqual(out.getvalue().splitlines(), + ['literal 102', 'literal 111', 'literal 111']) + # Debug output is output again even a second time (bypassing + # the cache -- issue #20426). + with captured_stdout() as out: + re.compile('foo', re.DEBUG) + self.assertEqual(out.getvalue().splitlines(), + ['literal 102', 'literal 111', 'literal 111']) + + def run_re_tests(): from test.re_tests import tests, SUCCEED, FAIL, SYNTAX_ERROR if verbose: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -38,6 +38,9 @@ Library ------- +- Issue #20426: When passing the re.DEBUG flag, re.compile() displays the + debug output every time it is called, regardless of the compilation cache. + - Issue #20368: The null character now correctly passed from Tcl to Python (in unicode strings only). Improved error handling in variables-related commands. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 21:32:51 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 21:32:51 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Skip_expr*_tes?= =?utf-8?q?ts_for_large_integers_for_Tcl_=3C8=2E5=2E?= Message-ID: <3fJ11v5sfLz7Ljd@mail.python.org> http://hg.python.org/cpython/rev/aed29f86bfdc changeset: 88943:aed29f86bfdc branch: 2.7 user: Serhiy Storchaka date: Mon Feb 03 22:30:22 2014 +0200 summary: Skip expr* tests for large integers for Tcl <8.5. The '**' operator is available only since 8.5 and in any case such large integers are not supported on Tcl <8.5. files: Lib/test/test_tcl.py | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -215,7 +215,6 @@ self.assertRaises(TclError, tcl.exprstring, 'spam') check('', '0') check('8.2 + 6', '14.2') - check('2**64', str(2**64)) check('3.1 + $a', '6.1') check('2 + "$a.$b"', '5.6') check('4*[llength "6 2"]', '8') @@ -233,6 +232,8 @@ check('"a\xc2\xbd\xe2\x82\xac"', 'a\xc2\xbd\xe2\x82\xac') check(r'"a\xbd\u20ac"', 'a\xc2\xbd\xe2\x82\xac') check(r'"a\0b"', 'a\xc0\x80b') + if tcl_version >= (8, 5): + check('2**64', str(2**64)) def test_exprdouble(self): tcl = self.interp @@ -248,7 +249,6 @@ self.assertRaises(TclError, tcl.exprdouble, 'spam') check('', 0.0) check('8.2 + 6', 14.2) - check('2**64', float(2**64)) check('3.1 + $a', 6.1) check('2 + "$a.$b"', 5.6) check('4*[llength "6 2"]', 8.0) @@ -263,6 +263,8 @@ check('[string length "a\xc2\xbd\xe2\x82\xac"]', 3.0) check(r'[string length "a\xbd\u20ac"]', 3.0) self.assertRaises(TclError, tcl.exprdouble, '"abc"') + if tcl_version >= (8, 5): + check('2**64', float(2**64)) def test_exprlong(self): tcl = self.interp @@ -278,7 +280,6 @@ self.assertRaises(TclError, tcl.exprlong, 'spam') check('', 0) check('8.2 + 6', 14) - self.assertRaises(TclError, tcl.exprlong, '2**64') check('3.1 + $a', 6) check('2 + "$a.$b"', 5) check('4*[llength "6 2"]', 8) @@ -293,6 +294,8 @@ check('[string length "a\xc2\xbd\xe2\x82\xac"]', 3) check(r'[string length "a\xbd\u20ac"]', 3) self.assertRaises(TclError, tcl.exprlong, '"abc"') + if tcl_version >= (8, 5): + self.assertRaises(TclError, tcl.exprlong, '2**64') def test_exprboolean(self): tcl = self.interp @@ -317,7 +320,6 @@ check('"%s"' % value, True) check('{%s}' % value, True) check('8.2 + 6', True) - check('2**64', True) check('3.1 + $a', True) check('2 + "$a.$b"', True) check('4*[llength "6 2"]', True) @@ -332,6 +334,8 @@ check('[string length "a\xc2\xbd\xe2\x82\xac"]', True) check(r'[string length "a\xbd\u20ac"]', True) self.assertRaises(TclError, tcl.exprboolean, '"abc"') + if tcl_version >= (8, 5): + check('2**64', True) def test_passing_values(self): def passValue(value): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 21:32:53 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 21:32:53 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Skip_expr*_tes?= =?utf-8?q?ts_for_large_integers_for_Tcl_=3C8=2E5=2E?= Message-ID: <3fJ11x0N5qz7LlC@mail.python.org> http://hg.python.org/cpython/rev/4f0e4a68dcdb changeset: 88944:4f0e4a68dcdb branch: 3.3 parent: 88940:a7b180d5df5f user: Serhiy Storchaka date: Mon Feb 03 22:31:09 2014 +0200 summary: Skip expr* tests for large integers for Tcl <8.5. The '**' operator is available only since 8.5 and in any case such large integers are not supported on Tcl <8.5. files: Lib/test/test_tcl.py | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -205,7 +205,6 @@ self.assertRaises(TclError, tcl.exprstring, 'spam') check('', '0') check('8.2 + 6', '14.2') - check('2**64', str(2**64)) check('3.1 + $a', '6.1') check('2 + "$a.$b"', '5.6') check('4*[llength "6 2"]', '8') @@ -223,6 +222,8 @@ check('"a\xbd\u20ac"', 'a\xbd\u20ac') check(r'"a\xbd\u20ac"', 'a\xbd\u20ac') check(r'"a\0b"', 'a\x00b') + if tcl_version >= (8, 5): + check('2**64', str(2**64)) def test_exprdouble(self): tcl = self.interp @@ -239,7 +240,6 @@ self.assertRaises(TclError, tcl.exprdouble, 'spam') check('', 0.0) check('8.2 + 6', 14.2) - check('2**64', float(2**64)) check('3.1 + $a', 6.1) check('2 + "$a.$b"', 5.6) check('4*[llength "6 2"]', 8.0) @@ -254,6 +254,8 @@ check('[string length "a\xbd\u20ac"]', 3.0) check(r'[string length "a\xbd\u20ac"]', 3.0) self.assertRaises(TclError, tcl.exprdouble, '"abc"') + if tcl_version >= (8, 5): + check('2**64', float(2**64)) def test_exprlong(self): tcl = self.interp @@ -270,7 +272,6 @@ self.assertRaises(TclError, tcl.exprlong, 'spam') check('', 0) check('8.2 + 6', 14) - self.assertRaises(TclError, tcl.exprlong, '2**64') check('3.1 + $a', 6) check('2 + "$a.$b"', 5) check('4*[llength "6 2"]', 8) @@ -285,6 +286,8 @@ check('[string length "a\xbd\u20ac"]', 3) check(r'[string length "a\xbd\u20ac"]', 3) self.assertRaises(TclError, tcl.exprlong, '"abc"') + if tcl_version >= (8, 5): + self.assertRaises(TclError, tcl.exprlong, '2**64') def test_exprboolean(self): tcl = self.interp @@ -310,7 +313,6 @@ check('"%s"' % value, True) check('{%s}' % value, True) check('8.2 + 6', True) - check('2**64', True) check('3.1 + $a', True) check('2 + "$a.$b"', True) check('4*[llength "6 2"]', True) @@ -325,6 +327,8 @@ check('[string length "a\xbd\u20ac"]', True) check(r'[string length "a\xbd\u20ac"]', True) self.assertRaises(TclError, tcl.exprboolean, '"abc"') + if tcl_version >= (8, 5): + check('2**64', True) def test_passing_values(self): def passValue(value): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 21:32:54 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 3 Feb 2014 21:32:54 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Skip_expr*_tests_for_large_integers_for_Tcl_=3C8=2E5=2E?= Message-ID: <3fJ11y2FjLz7LlC@mail.python.org> http://hg.python.org/cpython/rev/a5a622309a0b changeset: 88945:a5a622309a0b parent: 88941:a8bcfa290e68 parent: 88944:4f0e4a68dcdb user: Serhiy Storchaka date: Mon Feb 03 22:32:00 2014 +0200 summary: Skip expr* tests for large integers for Tcl <8.5. The '**' operator is available only since 8.5 and in any case such large integers are not supported on Tcl <8.5. files: Lib/test/test_tcl.py | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -205,7 +205,6 @@ self.assertRaises(TclError, tcl.exprstring, 'spam') check('', '0') check('8.2 + 6', '14.2') - check('2**64', str(2**64)) check('3.1 + $a', '6.1') check('2 + "$a.$b"', '5.6') check('4*[llength "6 2"]', '8') @@ -223,6 +222,8 @@ check('"a\xbd\u20ac"', 'a\xbd\u20ac') check(r'"a\xbd\u20ac"', 'a\xbd\u20ac') check(r'"a\0b"', 'a\x00b') + if tcl_version >= (8, 5): + check('2**64', str(2**64)) def test_exprdouble(self): tcl = self.interp @@ -239,7 +240,6 @@ self.assertRaises(TclError, tcl.exprdouble, 'spam') check('', 0.0) check('8.2 + 6', 14.2) - check('2**64', float(2**64)) check('3.1 + $a', 6.1) check('2 + "$a.$b"', 5.6) check('4*[llength "6 2"]', 8.0) @@ -254,6 +254,8 @@ check('[string length "a\xbd\u20ac"]', 3.0) check(r'[string length "a\xbd\u20ac"]', 3.0) self.assertRaises(TclError, tcl.exprdouble, '"abc"') + if tcl_version >= (8, 5): + check('2**64', float(2**64)) def test_exprlong(self): tcl = self.interp @@ -270,7 +272,6 @@ self.assertRaises(TclError, tcl.exprlong, 'spam') check('', 0) check('8.2 + 6', 14) - self.assertRaises(TclError, tcl.exprlong, '2**64') check('3.1 + $a', 6) check('2 + "$a.$b"', 5) check('4*[llength "6 2"]', 8) @@ -285,6 +286,8 @@ check('[string length "a\xbd\u20ac"]', 3) check(r'[string length "a\xbd\u20ac"]', 3) self.assertRaises(TclError, tcl.exprlong, '"abc"') + if tcl_version >= (8, 5): + self.assertRaises(TclError, tcl.exprlong, '2**64') def test_exprboolean(self): tcl = self.interp @@ -310,7 +313,6 @@ check('"%s"' % value, True) check('{%s}' % value, True) check('8.2 + 6', True) - check('2**64', True) check('3.1 + $a', True) check('2 + "$a.$b"', True) check('4*[llength "6 2"]', True) @@ -325,6 +327,8 @@ check('[string length "a\xbd\u20ac"]', True) check(r'[string length "a\xbd\u20ac"]', True) self.assertRaises(TclError, tcl.exprboolean, '"abc"') + if tcl_version >= (8, 5): + check('2**64', True) def test_passing_values(self): def passValue(value): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 23:02:56 2014 From: python-checkins at python.org (ned.deily) Date: Mon, 3 Feb 2014 23:02:56 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNDc0?= =?utf-8?q?=3A_Fix_=22unexpected_success=22_test=5Fsocket_failures_on_OS_X?= =?utf-8?q?_10=2E7+=2E?= Message-ID: <3fJ31r08n3z7LjV@mail.python.org> http://hg.python.org/cpython/rev/63efacd80f8e changeset: 88946:63efacd80f8e branch: 3.3 parent: 88944:4f0e4a68dcdb user: Ned Deily date: Mon Feb 03 13:58:31 2014 -0800 summary: Issue #20474: Fix "unexpected success" test_socket failures on OS X 10.7+. files: Lib/test/test_socket.py | 8 ++++---- Misc/NEWS | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -3490,12 +3490,12 @@ self.assertNotIsInstance(cm.exception, socket.timeout) self.assertEqual(cm.exception.errno, errno.EINTR) - # Issue #12958: The following tests have problems on Mac OS X - @support.anticipate_failure(sys.platform == "darwin") + # Issue #12958: The following tests have problems on OS X prior to 10.7 + @support.requires_mac_ver(10, 7) def testInterruptedSendTimeout(self): self.checkInterruptedSend(self.serv_conn.send, b"a"*512) - @support.anticipate_failure(sys.platform == "darwin") + @support.requires_mac_ver(10, 7) def testInterruptedSendtoTimeout(self): # Passing an actual address here as Python's wrapper for # sendto() doesn't allow passing a zero-length one; POSIX @@ -3504,7 +3504,7 @@ self.checkInterruptedSend(self.serv_conn.sendto, b"a"*512, self.serv_addr) - @support.anticipate_failure(sys.platform == "darwin") + @support.requires_mac_ver(10, 7) @requireAttrs(socket.socket, "sendmsg") def testInterruptedSendmsgTimeout(self): self.checkInterruptedSend(self.serv_conn.sendmsg, [b"a"*512]) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -365,6 +365,8 @@ - Issue #19085: Added basic tests for all tkinter widget options. +- Issue #20474: Fix test_socket "unexpected success" failures on OS X 10.7+. + Documentation ------------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 23:02:57 2014 From: python-checkins at python.org (ned.deily) Date: Mon, 3 Feb 2014 23:02:57 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320474=3A_Fix_=22unexpected_success=22_test=5Fso?= =?utf-8?q?cket_failures_on_OS_X_10=2E7+=2E?= Message-ID: <3fJ31s2Jl4z7LkQ@mail.python.org> http://hg.python.org/cpython/rev/036671354dc0 changeset: 88947:036671354dc0 parent: 88945:a5a622309a0b parent: 88946:63efacd80f8e user: Ned Deily date: Mon Feb 03 14:02:26 2014 -0800 summary: Issue #20474: Fix "unexpected success" test_socket failures on OS X 10.7+. files: Lib/test/test_socket.py | 8 ++++---- Misc/NEWS | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -3635,12 +3635,12 @@ self.assertNotIsInstance(cm.exception, socket.timeout) self.assertEqual(cm.exception.errno, errno.EINTR) - # Issue #12958: The following tests have problems on Mac OS X - @support.anticipate_failure(sys.platform == "darwin") + # Issue #12958: The following tests have problems on OS X prior to 10.7 + @support.requires_mac_ver(10, 7) def testInterruptedSendTimeout(self): self.checkInterruptedSend(self.serv_conn.send, b"a"*512) - @support.anticipate_failure(sys.platform == "darwin") + @support.requires_mac_ver(10, 7) def testInterruptedSendtoTimeout(self): # Passing an actual address here as Python's wrapper for # sendto() doesn't allow passing a zero-length one; POSIX @@ -3649,7 +3649,7 @@ self.checkInterruptedSend(self.serv_conn.sendto, b"a"*512, self.serv_addr) - @support.anticipate_failure(sys.platform == "darwin") + @support.requires_mac_ver(10, 7) @requireAttrs(socket.socket, "sendmsg") def testInterruptedSendmsgTimeout(self): self.checkInterruptedSend(self.serv_conn.sendmsg, [b"a"*512]) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -106,6 +106,8 @@ - Issue #19990: Added tests for the imghdr module. Based on patch by Claudiu Popa. +- Issue #20474: Fix test_socket "unexpected success" failures on OS X 10.7+. + Tools/Demos ----------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 23:26:44 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 3 Feb 2014 23:26:44 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=2Esubprocess=3A_Re?= =?utf-8?q?place_Process=2Eget=5Fsubprocess=28=29_method_with_a?= Message-ID: <3fJ3YJ6v1wz7Llg@mail.python.org> http://hg.python.org/cpython/rev/47cfd0980020 changeset: 88948:47cfd0980020 user: Victor Stinner date: Mon Feb 03 23:08:14 2014 +0100 summary: asyncio.subprocess: Replace Process.get_subprocess() method with a Process.subprocess read-only property files: Doc/library/asyncio-subprocess.rst | 34 ++++++------ Lib/asyncio/subprocess.py | 3 +- Lib/test/test_asyncio/test_subprocess.py | 16 ++-- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -57,6 +57,21 @@ .. class:: asyncio.subprocess.Process + .. attribute:: pid + + The identifier of the process. + + Note that if you set the *shell* argument to ``True``, this is the + process identifier of the spawned shell. + + .. attribute:: returncode + + Return code of the process when it exited. A ``None`` value indicates + that the process has not terminated yet. + + A negative value ``-N`` indicates that the child was terminated by signal + ``N`` (Unix only). + .. attribute:: stdin Standard input stream (write), ``None`` if the process was created with @@ -72,20 +87,9 @@ Standard error stream (read), ``None`` if the process was created with ``stderr=None``. - .. attribute:: pid + .. attribute:: subprocess - The identifier of the process. - - Note that if you set the *shell* argument to ``True``, this is the - process identifier of the spawned shell. - - .. attribute:: returncode - - Return code of the process when it exited. A ``None`` value indicates - that the process has not terminated yet. - - A negative value ``-N`` indicates that the child was terminated by signal - ``N`` (Unix only). + Underlying :class:`subprocess.Popen` object. .. method:: communicate(input=None) @@ -107,10 +111,6 @@ The data read is buffered in memory, so do not use this method if the data size is large or unlimited. - .. method:: get_subprocess() - - Get the underlying :class:`subprocess.Popen` object. - .. method:: kill() Kills the child. On Posix OSs the function sends :py:data:`SIGKILL` to diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py --- a/Lib/asyncio/subprocess.py +++ b/Lib/asyncio/subprocess.py @@ -106,7 +106,8 @@ yield from waiter return waiter.result() - def get_subprocess(self): + @property + def subprocess(self): return self._transport.get_extra_info('subprocess') def _check_alive(self): diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -119,7 +119,7 @@ returncode = self.loop.run_until_complete(proc.wait()) self.assertEqual(-signal.SIGHUP, returncode) - def test_get_subprocess(self): + def test_subprocess(self): args = PROGRAM_EXIT_FAST @asyncio.coroutine @@ -127,14 +127,14 @@ proc = yield from asyncio.create_subprocess_exec(*args, loop=self.loop) yield from proc.wait() + # need to poll subprocess.Popen, otherwise the returncode + # attribute is not set + proc.subprocess.wait() + return proc - popen = proc.get_subprocess() - popen.wait() - return (proc, popen) - - proc, popen = self.loop.run_until_complete(run()) - self.assertEqual(popen.returncode, proc.returncode) - self.assertEqual(popen.pid, proc.pid) + proc = self.loop.run_until_complete(run()) + self.assertEqual(proc.subprocess.returncode, proc.returncode) + self.assertEqual(proc.subprocess.pid, proc.pid) def test_broken_pipe(self): large_data = b'x' * support.PIPE_MAX_SIZE -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Feb 3 23:26:46 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 3 Feb 2014 23:26:46 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_doc=3A_add_an_exam?= =?utf-8?q?ple_of_asyncio=2Esubprocess_with_communicate=28=29_and_wait=28?= =?utf-8?q?=29?= Message-ID: <3fJ3YL1V94z7Lld@mail.python.org> http://hg.python.org/cpython/rev/0c2f707473fc changeset: 88949:0c2f707473fc user: Victor Stinner date: Mon Feb 03 23:26:28 2014 +0100 summary: asyncio doc: add an example of asyncio.subprocess with communicate() and wait() files: Doc/library/asyncio-subprocess.rst | 40 ++++++++++++++++++ 1 files changed, 40 insertions(+), 0 deletions(-) diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -138,3 +138,43 @@ Wait for child process to terminate. Set and return :attr:`returncode` attribute. + +Example +------- + +Implement a function similar to :func:`subprocess.getstatusoutput`, except that +it does not use a shell. Get the output of the "python -m platform" command and +display the output:: + + import asyncio + import sys + from asyncio import subprocess + + @asyncio.coroutine + def getstatusoutput(*args): + proc = yield from asyncio.create_subprocess_exec( + *args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + try: + stdout, _ = yield from proc.communicate() + except: + proc.kill() + yield from proc.wait() + raise + exitcode = yield from proc.wait() + return (exitcode, stdout) + + loop = asyncio.get_event_loop() + coro = getstatusoutput(sys.executable, '-m', 'platform') + exitcode, stdout = loop.run_until_complete(coro) + if not exitcode: + stdout = stdout.decode('ascii').rstrip() + print("Platform: %s" % stdout) + else: + print("Python failed with exit code %s:" % exitcode) + sys.stdout.flush() + sys.stdout.buffer.flush() + sys.stdout.buffer.write(stdout) + sys.stdout.buffer.flush() + loop.close() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 00:04:22 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 4 Feb 2014 00:04:22 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Oops=2C_undo_unwanted_chan?= =?utf-8?q?ges_in_test=5Fasyncio=3A_mistakes_of_my_the_last_sync_with?= Message-ID: <3fJ4Nk0RZfz7LjV@mail.python.org> http://hg.python.org/cpython/rev/d7dc5ebc33ef changeset: 88950:d7dc5ebc33ef user: Victor Stinner date: Mon Feb 03 23:59:52 2014 +0100 summary: Oops, undo unwanted changes in test_asyncio: mistakes of my the last sync with Tulip (changeset d7ac90c0463a) files: Lib/test/test_asyncio/test_base_events.py | 15 +++++---- Lib/test/test_asyncio/test_events.py | 8 +++++ Lib/test/test_asyncio/test_windows_events.py | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -116,17 +116,18 @@ self.loop.stop() self.loop._process_events = unittest.mock.Mock() - when = self.loop.time() + 0.1 + delay = 0.1 + + when = self.loop.time() + delay self.loop.call_at(when, cb) t0 = self.loop.time() self.loop.run_forever() dt = self.loop.time() - t0 - self.assertTrue(0.09 <= dt <= 0.9, - # Issue #20452: add more info in case of failure, - # to try to investigate the bug - (dt, - self.loop._granularity, - time.get_clock_info('monotonic'))) + + self.assertGreaterEqual(dt, delay - self.loop._granularity, dt) + # tolerate a difference of +800 ms because some Python buildbots + # are really slow + self.assertLessEqual(dt, 0.9, dt) def test_run_once_in_executor_handle(self): def cb(): diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1185,6 +1185,14 @@ calls.append(self.loop._run_once_counter) self.assertEqual(calls, [1, 3, 5, 6]) + def test_granularity(self): + granularity = self.loop._granularity + self.assertGreater(granularity, 0.0) + # Worst expected granularity: 1 ms on Linux (limited by poll/epoll + # resolution), 15.6 ms on Windows (limited by time.monotonic + # resolution) + self.assertLess(granularity, 0.050) + class SubprocessTestsMixin: diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -105,7 +105,7 @@ self.loop.run_until_complete(f) elapsed = self.loop.time() - start self.assertFalse(f.result()) - self.assertTrue(0.18 < elapsed < 0.5, elapsed) + self.assertTrue(0.18 < elapsed < 0.9, elapsed) _overlapped.SetEvent(event) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Tue Feb 4 09:12:54 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 04 Feb 2014 09:12:54 +0100 Subject: [Python-checkins] Daily reference leaks (d7dc5ebc33ef): sum=-13 Message-ID: results for d7dc5ebc33ef on branch "default" -------------------------------------------- test_site leaked [-2, 2, -2] references, sum=-2 test_site leaked [-2, 2, -2] memory blocks, sum=-2 test_urllib2net leaked [1590, -1601, 0] references, sum=-11 test_urllib2net leaked [1409, -1409, 2] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog1VaOpl', '-x'] From python-checkins at python.org Tue Feb 4 09:26:12 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 4 Feb 2014 09:26:12 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=3A_Fix_=5FProactor?= =?utf-8?q?WritePipeTransport=2E=5Fpipe=5Fclosed=28=29?= Message-ID: <3fJJs044Hcz7LjP@mail.python.org> http://hg.python.org/cpython/rev/632ff061636b changeset: 88951:632ff061636b user: Victor Stinner date: Tue Feb 04 08:57:48 2014 +0100 summary: asyncio: Fix _ProactorWritePipeTransport._pipe_closed() Do nothing if the pipe is already closed. _loop_writing() may call _force_close() when it gets ConnectionResetError. files: Lib/asyncio/proactor_events.py | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -304,9 +304,12 @@ if fut.cancelled(): # the transport has been closed return + assert fut.result() == b'' + if self._closing: + assert self._read_fut is None + return assert fut is self._read_fut, (fut, self._read_fut) self._read_fut = None - assert fut.result() == b'' if self._write_fut is not None: self._force_close(exc) else: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 09:33:23 2014 From: python-checkins at python.org (martin.v.loewis) Date: Tue, 4 Feb 2014 09:33:23 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317162=3A_Add_PyTy?= =?utf-8?q?pe=5FGetSlot=2E?= Message-ID: <3fJK1H0w8Cz7Ljd@mail.python.org> http://hg.python.org/cpython/rev/655d7a55c165 changeset: 88952:655d7a55c165 user: Martin v. L?wis date: Tue Feb 04 09:33:05 2014 +0100 summary: Issue #17162: Add PyType_GetSlot. files: Doc/c-api/type.rst | 10 ++++++++++ Include/object.h | 3 +++ Misc/NEWS | 2 ++ Modules/xxlimited.c | 2 +- Objects/typeobject.c | 13 +++++++++++++ setup.py | 2 +- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -97,3 +97,13 @@ types. This allows the caller to reference other heap types as base types. .. versionadded:: 3.3 + +.. c:function:: void* PyType_GetSlot(PyTypeObject *type, int slot) + + Return the function pointer stored int the given slot. If the + result is *NULL*, this indicates that either the slot is *NULL*, + or that the function was called with invalid parameters. + Callers will typically cast the result pointer into the appropriate + function type. + + .. versionadded:: 3.4 diff --git a/Include/object.h b/Include/object.h --- a/Include/object.h +++ b/Include/object.h @@ -439,6 +439,9 @@ #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 PyAPI_FUNC(PyObject*) PyType_FromSpecWithBases(PyType_Spec*, PyObject*); #endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03040000 +PyAPI_FUNC(void*) PyType_GetSlot(PyTypeObject*, int); +#endif #ifndef Py_LIMITED_API /* The *real* layout of a type object when allocated on the heap */ diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ Core and Builtins ----------------- +- Issue #17162: Add PyType_GetSlot. + - Issue #20162: Fix an alignment issue in the siphash24() hash function which caused a crash on PowerPC 64-bit (ppc64). diff --git a/Modules/xxlimited.c b/Modules/xxlimited.c --- a/Modules/xxlimited.c +++ b/Modules/xxlimited.c @@ -44,7 +44,7 @@ Xxo_dealloc(XxoObject *self) { Py_XDECREF(self->x_attr); - PyObject_Del(self); + ((freefunc)PyType_GetSlot(Py_TYPE(self), Py_tp_free))(self); } static PyObject * diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2641,6 +2641,19 @@ return PyType_FromSpecWithBases(spec, NULL); } +void * +PyType_GetSlot(PyTypeObject *type, int slot) +{ + if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE))?{ + PyErr_BadInternalCall(); + return NULL; + } + if (slot >= Py_ARRAY_LENGTH(slotoffsets)) { + /* Extension module requesting slot from a future version */ + return NULL; + } + return *(void**)(((char*)type) + slotoffsets[slot]); +} /* Internal API to look for a name through the MRO. This returns a borrowed reference, and doesn't set an exception! */ diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -1539,7 +1539,7 @@ if 'd' not in sys.abiflags: ext = Extension('xxlimited', ['xxlimited.c'], - define_macros=[('Py_LIMITED_API', 1)]) + define_macros=[('Py_LIMITED_API', '0x03040000')]) self.extensions.append(ext) return missing -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 09:49:30 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 4 Feb 2014 09:49:30 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317162=3A_Fix_comp?= =?utf-8?q?ilation=2C_replace_non-breaking_space_with_an_ASCII_space?= Message-ID: <3fJKMt2p1Mz7LjV@mail.python.org> http://hg.python.org/cpython/rev/eaae4008327d changeset: 88953:eaae4008327d user: Victor Stinner date: Tue Feb 04 09:49:14 2014 +0100 summary: Issue #17162: Fix compilation, replace non-breaking space with an ASCII space files: Objects/typeobject.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2644,7 +2644,7 @@ void * PyType_GetSlot(PyTypeObject *type, int slot) { - if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE))?{ + if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { PyErr_BadInternalCall(); return NULL; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 13:14:11 2014 From: python-checkins at python.org (nick.coghlan) Date: Tue, 4 Feb 2014 13:14:11 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_=2320404=3A_blacklis?= =?utf-8?q?t_non-text_encodings_in_io=2ETextIOWrapper?= Message-ID: <3fJPw36rf0z7LkY@mail.python.org> http://hg.python.org/cpython/rev/f3ec00d2b75e changeset: 88954:f3ec00d2b75e user: Nick Coghlan date: Tue Feb 04 22:11:18 2014 +1000 summary: Close #20404: blacklist non-text encodings in io.TextIOWrapper - io.TextIOWrapper (and hence the open() builtin) now use the internal codec marking system added for issue #19619 - also tweaked the C code to only look up the encoding once, rather than multiple times - the existing output type checks remain in place to deal with unmarked third party codecs. files: Include/codecs.h | 20 +++++++ Lib/_pyio.py | 5 + Lib/test/test_io.py | 33 +++++++++-- Misc/NEWS | 6 ++ Modules/_io/textio.c | 34 ++++++++---- Python/codecs.c | 88 +++++++++++++++++++++++-------- 6 files changed, 143 insertions(+), 43 deletions(-) diff --git a/Include/codecs.h b/Include/codecs.h --- a/Include/codecs.h +++ b/Include/codecs.h @@ -104,7 +104,14 @@ Please note that these APIs are internal and should not be used in Python C extensions. + XXX (ncoghlan): should we make these, or something like them, public + in Python 3.5+? + */ +PyAPI_FUNC(PyObject *) _PyCodec_LookupTextEncoding( + const char *encoding, + const char *alternate_command + ); PyAPI_FUNC(PyObject *) _PyCodec_EncodeText( PyObject *object, @@ -117,6 +124,19 @@ const char *encoding, const char *errors ); + +/* These two aren't actually text encoding specific, but _io.TextIOWrapper + * is the only current API consumer. + */ +PyAPI_FUNC(PyObject *) _PyCodecInfo_GetIncrementalDecoder( + PyObject *codec_info, + const char *errors + ); + +PyAPI_FUNC(PyObject *) _PyCodecInfo_GetIncrementalEncoder( + PyObject *codec_info, + const char *errors + ); #endif diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1503,6 +1503,11 @@ if not isinstance(encoding, str): raise ValueError("invalid encoding: %r" % encoding) + if not codecs.lookup(encoding)._is_text_encoding: + msg = ("%r is not a text encoding; " + "use codecs.open() to handle arbitrary codecs") + raise LookupError(msg % encoding) + if errors is None: errors = "strict" else: diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1929,6 +1929,15 @@ self.assertRaises(TypeError, t.__init__, b, newline=42) self.assertRaises(ValueError, t.__init__, b, newline='xyzzy') + def test_non_text_encoding_codecs_are_rejected(self): + # Ensure the constructor complains if passed a codec that isn't + # marked as a text encoding + # http://bugs.python.org/issue20404 + r = self.BytesIO() + b = self.BufferedWriter(r) + with self.assertRaisesRegex(LookupError, "is not a text encoding"): + self.TextIOWrapper(b, encoding="hex") + def test_detach(self): r = self.BytesIO() b = self.BufferedWriter(r) @@ -2579,15 +2588,22 @@ def test_illegal_decoder(self): # Issue #17106 + # Bypass the early encoding check added in issue 20404 + def _make_illegal_wrapper(): + quopri = codecs.lookup("quopri") + quopri._is_text_encoding = True + try: + t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), + newline='\n', encoding="quopri") + finally: + quopri._is_text_encoding = False + return t # Crash when decoder returns non-string - t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), newline='\n', - encoding='quopri_codec') + t = _make_illegal_wrapper() self.assertRaises(TypeError, t.read, 1) - t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), newline='\n', - encoding='quopri_codec') + t = _make_illegal_wrapper() self.assertRaises(TypeError, t.readline) - t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), newline='\n', - encoding='quopri_codec') + t = _make_illegal_wrapper() self.assertRaises(TypeError, t.read) def _check_create_at_shutdown(self, **kwargs): @@ -2616,8 +2632,7 @@ if err: # Can error out with a RuntimeError if the module state # isn't found. - self.assertIn("RuntimeError: could not find io module state", - err.decode()) + self.assertIn(self.shutdown_error, err.decode()) else: self.assertEqual("ok", out.decode().strip()) @@ -2630,6 +2645,7 @@ class CTextIOWrapperTest(TextIOWrapperTest): io = io + shutdown_error = "RuntimeError: could not find io module state" def test_initialization(self): r = self.BytesIO(b"\xc3\xa9\n\n") @@ -2674,6 +2690,7 @@ class PyTextIOWrapperTest(TextIOWrapperTest): io = pyio + shutdown_error = "LookupError: unknown encoding: ascii" class IncrementalNewlineDecoderTest(unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,12 @@ Core and Builtins ----------------- +- Issue #20404: io.TextIOWrapper (and hence the open() builtin) now uses the + internal codec marking system added for issue #19619 to throw LookupError + for known non-text encodings at stream construction time. The existing + output type checks remain in place to deal with unmarked third party + codecs. + - Issue #17162: Add PyType_GetSlot. - Issue #20162: Fix an alignment issue in the siphash24() hash function which diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -849,7 +849,7 @@ char *kwlist[] = {"buffer", "encoding", "errors", "newline", "line_buffering", "write_through", NULL}; - PyObject *buffer, *raw; + PyObject *buffer, *raw, *codec_info = NULL; char *encoding = NULL; char *errors = NULL; char *newline = NULL; @@ -961,6 +961,17 @@ "could not determine default encoding"); } + /* Check we have been asked for a real text encoding */ + codec_info = _PyCodec_LookupTextEncoding(encoding, "codecs.open()"); + if (codec_info == NULL) { + Py_CLEAR(self->encoding); + goto error; + } + + /* XXX: Failures beyond this point have the potential to leak elements + * of the partially constructed object (like self->encoding) + */ + if (errors == NULL) errors = "strict"; self->errors = PyBytes_FromString(errors); @@ -975,7 +986,7 @@ if (newline) { self->readnl = PyUnicode_FromString(newline); if (self->readnl == NULL) - return -1; + goto error; } self->writetranslate = (newline == NULL || newline[0] != '\0'); if (!self->readuniversal && self->readnl) { @@ -999,8 +1010,8 @@ if (r == -1) goto error; if (r == 1) { - self->decoder = PyCodec_IncrementalDecoder( - encoding, errors); + self->decoder = _PyCodecInfo_GetIncrementalDecoder(codec_info, + errors); if (self->decoder == NULL) goto error; @@ -1024,17 +1035,12 @@ if (r == -1) goto error; if (r == 1) { - PyObject *ci; - self->encoder = PyCodec_IncrementalEncoder( - encoding, errors); + self->encoder = _PyCodecInfo_GetIncrementalEncoder(codec_info, + errors); if (self->encoder == NULL) goto error; /* Get the normalized named of the codec */ - ci = _PyCodec_Lookup(encoding); - if (ci == NULL) - goto error; - res = _PyObject_GetAttrId(ci, &PyId_name); - Py_DECREF(ci); + res = _PyObject_GetAttrId(codec_info, &PyId_name); if (res == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_Clear(); @@ -1054,6 +1060,9 @@ Py_XDECREF(res); } + /* Finished sorting out the codec details */ + Py_DECREF(codec_info); + self->buffer = buffer; Py_INCREF(buffer); @@ -1116,6 +1125,7 @@ return 0; error: + Py_XDECREF(codec_info); return -1; } diff --git a/Python/codecs.c b/Python/codecs.c --- a/Python/codecs.c +++ b/Python/codecs.c @@ -243,20 +243,15 @@ return v; } -/* Helper function to create an incremental codec. */ +/* Helper functions to create an incremental codec. */ +static +PyObject *codec_makeincrementalcodec(PyObject *codec_info, + const char *errors, + const char *attrname) +{ + PyObject *ret, *inccodec; -static -PyObject *codec_getincrementalcodec(const char *encoding, - const char *errors, - const char *attrname) -{ - PyObject *codecs, *ret, *inccodec; - - codecs = _PyCodec_Lookup(encoding); - if (codecs == NULL) - return NULL; - inccodec = PyObject_GetAttrString(codecs, attrname); - Py_DECREF(codecs); + inccodec = PyObject_GetAttrString(codec_info, attrname); if (inccodec == NULL) return NULL; if (errors) @@ -267,6 +262,21 @@ return ret; } +static +PyObject *codec_getincrementalcodec(const char *encoding, + const char *errors, + const char *attrname) +{ + PyObject *codec_info, *ret; + + codec_info = _PyCodec_Lookup(encoding); + if (codec_info == NULL) + return NULL; + ret = codec_makeincrementalcodec(codec_info, errors, attrname); + Py_DECREF(codec_info); + return ret; +} + /* Helper function to create a stream codec. */ static @@ -290,6 +300,24 @@ return streamcodec; } +/* Helpers to work with the result of _PyCodec_Lookup + + */ +PyObject *_PyCodecInfo_GetIncrementalDecoder(PyObject *codec_info, + const char *errors) +{ + return codec_makeincrementalcodec(codec_info, errors, + "incrementaldecoder"); +} + +PyObject *_PyCodecInfo_GetIncrementalEncoder(PyObject *codec_info, + const char *errors) +{ + return codec_makeincrementalcodec(codec_info, errors, + "incrementalencoder"); +} + + /* Convenience APIs to query the Codec registry. All APIs return a codec object with incremented refcount. @@ -467,15 +495,12 @@ } /* Text encoding/decoding API */ -static -PyObject *codec_getitem_checked(const char *encoding, - const char *operation_name, - int index) +PyObject * _PyCodec_LookupTextEncoding(const char *encoding, + const char *alternate_command) { _Py_IDENTIFIER(_is_text_encoding); PyObject *codec; PyObject *attr; - PyObject *v; int is_text_codec; codec = _PyCodec_Lookup(encoding); @@ -502,27 +527,44 @@ Py_DECREF(codec); PyErr_Format(PyExc_LookupError, "'%.400s' is not a text encoding; " - "use codecs.%s() to handle arbitrary codecs", - encoding, operation_name); + "use %s to handle arbitrary codecs", + encoding, alternate_command); return NULL; } } } + /* This appears to be a valid text encoding */ + return codec; +} + + +static +PyObject *codec_getitem_checked(const char *encoding, + const char *alternate_command, + int index) +{ + PyObject *codec; + PyObject *v; + + codec = _PyCodec_LookupTextEncoding(encoding, alternate_command); + if (codec == NULL) + return NULL; + v = PyTuple_GET_ITEM(codec, index); + Py_INCREF(v); Py_DECREF(codec); - Py_INCREF(v); return v; } static PyObject * _PyCodec_TextEncoder(const char *encoding) { - return codec_getitem_checked(encoding, "encode", 0); + return codec_getitem_checked(encoding, "codecs.encode()", 0); } static PyObject * _PyCodec_TextDecoder(const char *encoding) { - return codec_getitem_checked(encoding, "decode", 1); + return codec_getitem_checked(encoding, "codecs.decode()", 1); } PyObject *_PyCodec_EncodeText(PyObject *object, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 14:03:01 2014 From: python-checkins at python.org (nick.coghlan) Date: Tue, 4 Feb 2014 14:03:01 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_=2320053=3A_ignore_d?= =?utf-8?q?efault_pip_config_settings?= Message-ID: <3fJR0P0KWbz7Lkt@mail.python.org> http://hg.python.org/cpython/rev/1b8ba1346e67 changeset: 88955:1b8ba1346e67 user: Nick Coghlan date: Tue Feb 04 23:02:36 2014 +1000 summary: Close #20053: ignore default pip config settings ensurepip now sets PIP_CONFIG_FILE to os.devnull before import pip from the wheel file. This also ensures venv ignores the default settings when bootstrapping pip. files: Lib/ensurepip/__init__.py | 9 ++++-- Lib/test/test_ensurepip.py | 16 +++++++++++ Lib/test/test_venv.py | 37 +++++++++++++++++++------ Misc/NEWS | 3 ++ 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -47,13 +47,16 @@ """ return _PIP_VERSION -def _clear_pip_environment_variables(): +def _disable_pip_configuration_settings(): # We deliberately ignore all pip environment variables # when invoking pip # See http://bugs.python.org/issue19734 for details keys_to_remove = [k for k in os.environ if k.startswith("PIP_")] for k in keys_to_remove: del os.environ[k] + # We also ignore the settings in the default pip configuration file + # See http://bugs.python.org/issue20053 for details + os.environ['PIP_CONFIG_FILE'] = os.devnull def bootstrap(*, root=None, upgrade=False, user=False, @@ -69,7 +72,7 @@ raise ValueError("Cannot use altinstall and default_pip together") _require_ssl_for_pip() - _clear_pip_environment_variables() + _disable_pip_configuration_settings() # By default, installing pip and setuptools installs all of the # following scripts (X.Y == running Python version): @@ -130,7 +133,7 @@ raise RuntimeError(msg.format(pip.__version__, _PIP_VERSION)) _require_ssl_for_pip() - _clear_pip_environment_variables() + _disable_pip_configuration_settings() # Construct the arguments to be passed to the pip command args = ["uninstall", "-y"] diff --git a/Lib/test/test_ensurepip.py b/Lib/test/test_ensurepip.py --- a/Lib/test/test_ensurepip.py +++ b/Lib/test/test_ensurepip.py @@ -36,9 +36,11 @@ self.addCleanup(run_pip_patch.stop) # Avoid side effects on the actual os module + real_devnull = os.devnull os_patch = unittest.mock.patch("ensurepip.os") patched_os = os_patch.start() self.addCleanup(os_patch.stop) + patched_os.devnull = real_devnull patched_os.path = os.path self.os_environ = patched_os.environ = os.environ.copy() @@ -161,6 +163,12 @@ ensurepip.bootstrap() self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ) + @requires_usable_pip + def test_pip_config_file_disabled(self): + # ensurepip deliberately ignores the pip config file + # See http://bugs.python.org/issue20053 for details + ensurepip.bootstrap() + self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull) @contextlib.contextmanager def fake_pip(version=ensurepip._PIP_VERSION): @@ -240,6 +248,14 @@ ensurepip._uninstall_helper() self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ) + @requires_usable_pip + def test_pip_config_file_disabled(self): + # ensurepip deliberately ignores the pip config file + # See http://bugs.python.org/issue20053 for details + with fake_pip(): + ensurepip._uninstall_helper() + self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull) + class TestMissingSSL(EnsurepipMixin, unittest.TestCase): diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -301,16 +301,35 @@ # that we want to ensure it ignores the normal pip environment # variable settings. We set PIP_NO_INSTALL here specifically # to check that ensurepip (and hence venv) ignores it. - # See http://bugs.python.org/issue19734 for details + # See http://bugs.python.org/issue19734 envvars["PIP_NO_INSTALL"] = "1" - try: - self.run_with_capture(venv.create, self.env_dir, with_pip=True) - except subprocess.CalledProcessError as exc: - # The output this produces can be a little hard to read, but - # least it has all the details - details = exc.output.decode(errors="replace") - msg = "{}\n\n**Subprocess Output**\n{}".format(exc, details) - self.fail(msg) + # Also check that we ignore the pip configuration file + # See http://bugs.python.org/issue20053 + with tempfile.TemporaryDirectory() as home_dir: + envvars["HOME"] = home_dir + bad_config = "[global]\nno-install=1" + # Write to both config file names on all platforms to reduce + # cross-platform variation in test code behaviour + win_location = ("pip", "pip.ini") + posix_location = (".pip", "pip.conf") + for dirname, fname in (win_location, posix_location): + dirpath = os.path.join(home_dir, dirname) + os.mkdir(dirpath) + fpath = os.path.join(dirpath, fname) + with open(fpath, 'w') as f: + f.write(bad_config) + + # Actually run the create command with all that unhelpful + # config in place to ensure we ignore it + try: + self.run_with_capture(venv.create, self.env_dir, + with_pip=True) + except subprocess.CalledProcessError as exc: + # The output this produces can be a little hard to read, + # but at least it has all the details + details = exc.output.decode(errors="replace") + msg = "{}\n\n**Subprocess Output**\n{}" + self.fail(msg.format(exc, details)) # Ensure pip is available in the virtual environment envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) cmd = [envpy, '-Im', 'pip', '--version'] diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,9 @@ Library ------- +- Issue #20053: ensurepip (and hence venv) are no longer affected by the + settings in the default pip configuration file. + - Issue #20426: When passing the re.DEBUG flag, re.compile() displays the debug output every time it is called, regardless of the compilation cache. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 16:12:26 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 4 Feb 2014 16:12:26 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_restore_namesp?= =?utf-8?q?acing_of_pyexpat_symbols_=28closes_=2319186=29?= Message-ID: <3fJTsk75JRz7LkX@mail.python.org> http://hg.python.org/cpython/rev/c242a8f30806 changeset: 88956:c242a8f30806 branch: 3.3 parent: 88946:63efacd80f8e user: Benjamin Peterson date: Tue Feb 04 10:10:55 2014 -0500 summary: restore namespacing of pyexpat symbols (closes #19186) files: Misc/NEWS | 2 ++ Modules/expat/expat_external.h | 4 ++++ 2 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,8 @@ Library ------- +- Issue #19186: Restore namespacing of expat symbols inside the pyexpat module. + - Issue #20426: When passing the re.DEBUG flag, re.compile() displays the debug output every time it is called, regardless of the compilation cache. diff --git a/Modules/expat/expat_external.h b/Modules/expat/expat_external.h --- a/Modules/expat/expat_external.h +++ b/Modules/expat/expat_external.h @@ -7,6 +7,10 @@ /* External API definitions */ +/* Namespace external symbols to allow multiple libexpat version to + co-exist. */ +#include "pyexpatns.h" + #if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) #define XML_USE_MSC_EXTENSIONS 1 #endif -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 16:12:28 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 4 Feb 2014 16:12:28 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy4zICgjMTkxODYp?= Message-ID: <3fJTsm2kkcz7LmH@mail.python.org> http://hg.python.org/cpython/rev/a2d877fb53f6 changeset: 88957:a2d877fb53f6 parent: 88955:1b8ba1346e67 parent: 88956:c242a8f30806 user: Benjamin Peterson date: Tue Feb 04 10:12:18 2014 -0500 summary: merge 3.3 (#19186) files: Misc/NEWS | 2 ++ Modules/expat/expat_external.h | 4 ++++ 2 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,8 @@ Library ------- +- Issue #19186: Restore namespacing of expat symbols inside the pyexpat module. + - Issue #20053: ensurepip (and hence venv) are no longer affected by the settings in the default pip configuration file. diff --git a/Modules/expat/expat_external.h b/Modules/expat/expat_external.h --- a/Modules/expat/expat_external.h +++ b/Modules/expat/expat_external.h @@ -7,6 +7,10 @@ /* External API definitions */ +/* Namespace external symbols to allow multiple libexpat version to + co-exist. */ +#include "pyexpatns.h" + #if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) #define XML_USE_MSC_EXTENSIONS 1 #endif -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 16:20:37 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 4 Feb 2014 16:20:37 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_mmap_obmalloc_?= =?utf-8?q?arenas_so_that_they_may_be_immediately_returned_to_the_system?= Message-ID: <3fJV394WFnz7LpZ@mail.python.org> http://hg.python.org/cpython/rev/4e43e5b3f7fc changeset: 88958:4e43e5b3f7fc branch: 2.7 parent: 88943:aed29f86bfdc user: Benjamin Peterson date: Tue Feb 04 10:20:26 2014 -0500 summary: mmap obmalloc arenas so that they may be immediately returned to the system when unused (closes #20494) files: Misc/NEWS | 4 ++ Objects/obmalloc.c | 50 +++++++++++++++++++++++++-------- configure | 2 +- configure.ac | 2 +- pyconfig.h.in | 3 ++ 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -9,6 +9,10 @@ Core and Builtins ----------------- +- Issue #20494: Ensure that free()d memory arenas are really released on POSIX + systems supporting anonymous memory mappings. Patch by Charles-Fran?ois + Natali. + - Issue #17825: Cursor "^" is correctly positioned for SyntaxError and IndentationError. diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -2,6 +2,13 @@ #ifdef WITH_PYMALLOC +#ifdef HAVE_MMAP + #include + #ifdef MAP_ANONYMOUS + #define ARENAS_USE_MMAP + #endif +#endif + #ifdef WITH_VALGRIND #include @@ -75,7 +82,8 @@ * Allocation strategy abstract: * * For small requests, the allocator sub-allocates blocks of memory. - * Requests greater than 256 bytes are routed to the system's allocator. + * Requests greater than SMALL_REQUEST_THRESHOLD bytes are routed to the + * system's allocator. * * Small requests are grouped in size classes spaced 8 bytes apart, due * to the required valid alignment of the returned address. Requests of @@ -107,10 +115,11 @@ * 57-64 64 7 * 65-72 72 8 * ... ... ... - * 241-248 248 30 - * 249-256 256 31 + * 497-504 504 62 + * 505-512 512 63 * - * 0, 257 and up: routed to the underlying allocator. + * 0, SMALL_REQUEST_THRESHOLD + 1 and up: routed to the underlying + * allocator. */ /*==========================================================================*/ @@ -143,10 +152,13 @@ * 1) ALIGNMENT <= SMALL_REQUEST_THRESHOLD <= 256 * 2) SMALL_REQUEST_THRESHOLD is evenly divisible by ALIGNMENT * + * Note: a size threshold of 512 guarantees that newly created dictionaries + * will be allocated from preallocated memory pools on 64-bit. + * * Although not required, for better performance and space efficiency, * it is recommended that SMALL_REQUEST_THRESHOLD is set to a power of 2. */ -#define SMALL_REQUEST_THRESHOLD 256 +#define SMALL_REQUEST_THRESHOLD 512 #define NB_SMALL_SIZE_CLASSES (SMALL_REQUEST_THRESHOLD / ALIGNMENT) /* @@ -174,15 +186,15 @@ /* * The allocator sub-allocates blocks of memory (called arenas) aligned * on a page boundary. This is a reserved virtual address space for the - * current process (obtained through a malloc call). In no way this means - * that the memory arenas will be used entirely. A malloc() is usually - * an address range reservation for bytes, unless all pages within this - * space are referenced subsequently. So malloc'ing big blocks and not using - * them does not mean "wasting memory". It's an addressable range wastage... + * current process (obtained through a malloc()/mmap() call). In no way this + * means that the memory arenas will be used entirely. A malloc() is + * usually an address range reservation for bytes, unless all pages within + * this space are referenced subsequently. So malloc'ing big blocks and not + * using them does not mean "wasting memory". It's an addressable range + * wastage... * - * Therefore, allocating arenas with malloc is not optimal, because there is - * some address space wastage, but this is the most portable way to request - * memory from the system across various platforms. + * Arenas are allocated with mmap() on systems supporting anonymous memory + * mappings to reduce heap fragmentation. */ #define ARENA_SIZE (256 << 10) /* 256KB */ @@ -440,6 +452,9 @@ , PT(48), PT(49), PT(50), PT(51), PT(52), PT(53), PT(54), PT(55) #if NB_SMALL_SIZE_CLASSES > 56 , PT(56), PT(57), PT(58), PT(59), PT(60), PT(61), PT(62), PT(63) +#if NB_SMALL_SIZE_CLASSES > 64 +#error "NB_SMALL_SIZE_CLASSES should be less than 64" +#endif /* NB_SMALL_SIZE_CLASSES > 64 */ #endif /* NB_SMALL_SIZE_CLASSES > 56 */ #endif /* NB_SMALL_SIZE_CLASSES > 48 */ #endif /* NB_SMALL_SIZE_CLASSES > 40 */ @@ -577,7 +592,12 @@ arenaobj = unused_arena_objects; unused_arena_objects = arenaobj->nextarena; assert(arenaobj->address == 0); +#ifdef ARENAS_USE_MMAP + arenaobj->address = (uptr)mmap(NULL, ARENA_SIZE, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +#else arenaobj->address = (uptr)malloc(ARENA_SIZE); +#endif if (arenaobj->address == 0) { /* The allocation failed: return NULL after putting the * arenaobj back. @@ -1054,7 +1074,11 @@ unused_arena_objects = ao; /* Free the entire arena. */ +#ifdef ARENAS_USE_MMAP + munmap((void *)ao->address, ARENA_SIZE); +#else free((void *)ao->address); +#endif ao->address = 0; /* mark unassociated */ --narenas_currently_allocated; diff --git a/configure b/configure --- a/configure +++ b/configure @@ -10164,7 +10164,7 @@ clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ - initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime \ + initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime mmap \ mremap nice pathconf pause plock poll pthread_init \ putenv readlink realpath \ select sem_open sem_timedwait sem_getvalue sem_unlink setegid seteuid \ diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -2905,7 +2905,7 @@ clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ - initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime \ + initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime mmap \ mremap nice pathconf pause plock poll pthread_init \ putenv readlink realpath \ select sem_open sem_timedwait sem_getvalue sem_unlink setegid seteuid \ diff --git a/pyconfig.h.in b/pyconfig.h.in --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -475,6 +475,9 @@ /* Define to 1 if you have the `mktime' function. */ #undef HAVE_MKTIME +/* Define to 1 if you have the `mmap' function. */ +#undef HAVE_MMAP + /* Define to 1 if you have the `mremap' function. */ #undef HAVE_MREMAP -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 17:42:31 2014 From: python-checkins at python.org (vinay.sajip) Date: Tue, 4 Feb 2014 17:42:31 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNTA5?= =?utf-8?q?=3A_Added_cross-reference_in_documentation=2E?= Message-ID: <3fJWsg3QMdz7Llf@mail.python.org> http://hg.python.org/cpython/rev/45aa817ec853 changeset: 88959:45aa817ec853 branch: 2.7 user: Vinay Sajip date: Tue Feb 04 16:25:41 2014 +0000 summary: Issue #20509: Added cross-reference in documentation. files: Doc/library/logging.config.rst | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -81,8 +81,9 @@ .. function:: fileConfig(fname, defaults=None, disable_existing_loggers=True) Reads the logging configuration from a :mod:`configparser`\-format file - named *fname*. This function can be called several times from an - application, allowing an end user to select from various pre-canned + named *fname*. The format of the file should be as described in + :ref:`logging-config-fileformat`. This function can be called several times + from an application, allowing an end user to select from various pre-canned configurations (if the developer provides a mechanism to present the choices and load the chosen configuration). -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 17:42:32 2014 From: python-checkins at python.org (vinay.sajip) Date: Tue, 4 Feb 2014 17:42:32 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTA5?= =?utf-8?q?=3A_Added_cross-reference_in_documentation=2E?= Message-ID: <3fJWsh5pv8z7LnV@mail.python.org> http://hg.python.org/cpython/rev/f8318b069146 changeset: 88960:f8318b069146 branch: 3.3 parent: 88956:c242a8f30806 user: Vinay Sajip date: Tue Feb 04 16:28:07 2014 +0000 summary: Issue #20509: Added cross-reference in documentation. files: Doc/library/logging.config.rst | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -81,8 +81,9 @@ .. function:: fileConfig(fname, defaults=None, disable_existing_loggers=True) Reads the logging configuration from a :mod:`configparser`\-format file - named *fname*. This function can be called several times from an - application, allowing an end user to select from various pre-canned + named *fname*. The format of the file should be as described in + :ref:`logging-config-fileformat`. This function can be called several times + from an application, allowing an end user to select from various pre-canned configurations (if the developer provides a mechanism to present the choices and load the chosen configuration). -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 17:42:34 2014 From: python-checkins at python.org (vinay.sajip) Date: Tue, 4 Feb 2014 17:42:34 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Closes_=2320509=3A_Merged_documentation_update_from_3=2E?= =?utf-8?q?3=2E?= Message-ID: <3fJWsk1HqPz7Llr@mail.python.org> http://hg.python.org/cpython/rev/7d0a4f89c6ce changeset: 88961:7d0a4f89c6ce parent: 88957:a2d877fb53f6 parent: 88960:f8318b069146 user: Vinay Sajip date: Tue Feb 04 16:42:04 2014 +0000 summary: Closes #20509: Merged documentation update from 3.3. files: Doc/library/logging.config.rst | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -80,7 +80,9 @@ .. function:: fileConfig(fname, defaults=None, disable_existing_loggers=True) - Reads the logging configuration from a :mod:`configparser`\-format file. + Reads the logging configuration from a :mod:`configparser`\-format file. The + format of the file should be as described in + :ref:`logging-config-fileformat`. This function can be called several times from an application, allowing an end user to select from various pre-canned configurations (if the developer provides a mechanism to present the choices and load the chosen -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 18:18:41 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 4 Feb 2014 18:18:41 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_doc=3A_add_an_exam?= =?utf-8?q?ple_to_schedule_a_coroutine_from_a_different_thread?= Message-ID: <3fJXgP0twQz7LjW@mail.python.org> http://hg.python.org/cpython/rev/854d05c13a8e changeset: 88962:854d05c13a8e user: Victor Stinner date: Tue Feb 04 18:18:27 2014 +0100 summary: asyncio doc: add an example to schedule a coroutine from a different thread files: Doc/library/asyncio-dev.rst | 13 ++++++++----- 1 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -13,12 +13,15 @@ ------------------------------ An event loop runs in a thread and executes all callbacks and tasks in the same -thread. If a callback should be scheduled from a different thread, the -:meth:`BaseEventLoop.call_soon_threadsafe` method should be used. +thread. While a task in running in the event loop, no other task is running in +the same thread. But when the task uses ``yield from``, the task is suspended +and the event loop executes the next task. -While a task in running in the event loop, no other task is running in the same -thread. But when the task uses ``yield from``, the task is suspended and the -event loop executes the next task. +To schedule a callback from a different thread, the +:meth:`BaseEventLoop.call_soon_threadsafe` method should be used. Example to +schedule a coroutine from a different:: + + loop.call_soon_threadsafe(asyncio.async, coro_func()) To handle signals and to execute subprocesses, the event loop must be run in the main thread. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 18:37:53 2014 From: python-checkins at python.org (zach.ware) Date: Tue, 4 Feb 2014 18:37:53 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?devguide=3A_Correct_and_future-proof_?= =?utf-8?q?PC_and_PCbuild_directory_descriptions=2E?= Message-ID: <3fJY5Y5RxQz7LjV@mail.python.org> http://hg.python.org/devguide/rev/6fb9db43a372 changeset: 664:6fb9db43a372 user: Zachary Ware date: Tue Feb 04 11:37:23 2014 -0600 summary: Correct and future-proof PC and PCbuild directory descriptions. files: setup.rst | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.rst b/setup.rst --- a/setup.rst +++ b/setup.rst @@ -315,11 +315,12 @@ Code for all built-in types. ``PC`` - Windows-specific code along with build files for VC 6, 7, & 8 along with - OS/2. + Windows-specific code along with legacy build files for previously used + versions of MSVC. ``PCbuild`` - Build files for VC 9 and newer. + Build files for the version of MSVC currently used for the Windows + installers provided on python.org. ``Parser`` Code related to the parser. The definition of the AST nodes is also kept -- Repository URL: http://hg.python.org/devguide From python-checkins at python.org Tue Feb 4 22:49:44 2014 From: python-checkins at python.org (guido.van.rossum) Date: Tue, 4 Feb 2014 22:49:44 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Add_missing_word_=28=22thr?= =?utf-8?q?ead=22=29_to_sentence_about_call=5Fsoon=5Fthreadsafe=2E?= Message-ID: <3fJfh80dFgz7LjS@mail.python.org> http://hg.python.org/cpython/rev/e0bcafbffe7e changeset: 88963:e0bcafbffe7e user: Guido van Rossum date: Tue Feb 04 13:49:34 2014 -0800 summary: Add missing word ("thread") to sentence about call_soon_threadsafe. files: Doc/library/asyncio-dev.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -19,7 +19,7 @@ To schedule a callback from a different thread, the :meth:`BaseEventLoop.call_soon_threadsafe` method should be used. Example to -schedule a coroutine from a different:: +schedule a coroutine from a different thread:: loop.call_soon_threadsafe(asyncio.async, coro_func()) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Feb 4 23:27:24 2014 From: python-checkins at python.org (guido.van.rossum) Date: Tue, 4 Feb 2014 23:27:24 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=3A_Cosmetic_improv?= =?utf-8?q?ement_to_test=5F=5Frun=5Fonce=5Flogging=28=29_mock_argument=2E?= Message-ID: <3fJgWc2SG6z7LjR@mail.python.org> http://hg.python.org/cpython/rev/92fc6850dea0 changeset: 88964:92fc6850dea0 user: Guido van Rossum date: Tue Feb 04 14:27:14 2014 -0800 summary: asyncio: Cosmetic improvement to test__run_once_logging() mock argument. files: Lib/test/test_asyncio/test_base_events.py | 8 +++----- 1 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -190,7 +190,7 @@ @unittest.mock.patch('asyncio.base_events.time') @unittest.mock.patch('asyncio.base_events.logger') - def test__run_once_logging(self, m_logging, m_time): + def test__run_once_logging(self, m_logger, m_time): # Log to INFO level if timeout > 1.0 sec. idx = -1 data = [10.0, 10.0, 12.0, 13.0] @@ -201,20 +201,18 @@ return data[idx] m_time.monotonic = monotonic - m_logging.INFO = logging.INFO - m_logging.DEBUG = logging.DEBUG self.loop._scheduled.append( asyncio.TimerHandle(11.0, lambda: True, ())) self.loop._process_events = unittest.mock.Mock() self.loop._run_once() - self.assertEqual(logging.INFO, m_logging.log.call_args[0][0]) + self.assertEqual(logging.INFO, m_logger.log.call_args[0][0]) idx = -1 data = [10.0, 10.0, 10.3, 13.0] self.loop._scheduled = [asyncio.TimerHandle(11.0, lambda:True, ())] self.loop._run_once() - self.assertEqual(logging.DEBUG, m_logging.log.call_args[0][0]) + self.assertEqual(logging.DEBUG, m_logger.log.call_args[0][0]) def test__run_once_schedule_handle(self): handle = None -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 00:31:44 2014 From: python-checkins at python.org (christian.heimes) Date: Wed, 5 Feb 2014 00:31:44 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTE1?= =?utf-8?q?=3A_Fix_NULL_pointer_dereference_introduced_by_issue_=2320368?= Message-ID: <3fJhxr1Jrsz7Lk4@mail.python.org> http://hg.python.org/cpython/rev/d83ce3a2d954 changeset: 88965:d83ce3a2d954 branch: 3.3 parent: 88960:f8318b069146 user: Christian Heimes date: Wed Feb 05 00:29:17 2014 +0100 summary: Issue #20515: Fix NULL pointer dereference introduced by issue #20368 CID 1167595 files: Misc/NEWS | 2 ++ Modules/_tkinter.c | 3 +++ 2 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,8 @@ Library ------- +- Issue #20515: Fix NULL pointer dereference introduced by issue #20368. + - Issue #19186: Restore namespacing of expat symbols inside the pyexpat module. - Issue #20426: When passing the re.DEBUG flag, re.compile() displays the diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -1568,6 +1568,9 @@ if (PyUnicode_Check(in)) { Py_ssize_t size; s = PyUnicode_AsUTF8AndSize(in, &size); + if (s == NULL) { + return 0; + } if (size > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "string is too long"); return 0; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 00:31:45 2014 From: python-checkins at python.org (christian.heimes) Date: Wed, 5 Feb 2014 00:31:45 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320515=3A_Fix_NULL_pointer_dereference_introduce?= =?utf-8?q?d_by_issue_=2320368?= Message-ID: <3fJhxs3vyjz7Lkb@mail.python.org> http://hg.python.org/cpython/rev/145032f626d3 changeset: 88966:145032f626d3 parent: 88964:92fc6850dea0 parent: 88965:d83ce3a2d954 user: Christian Heimes date: Wed Feb 05 00:29:48 2014 +0100 summary: Issue #20515: Fix NULL pointer dereference introduced by issue #20368 CID 1167595 files: Misc/NEWS | 2 ++ Modules/_tkinter.c | 3 +++ 2 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,8 @@ Library ------- +- Issue #20515: Fix NULL pointer dereference introduced by issue #20368. + - Issue #19186: Restore namespacing of expat symbols inside the pyexpat module. - Issue #20053: ensurepip (and hence venv) are no longer affected by the diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -1397,6 +1397,9 @@ if (PyUnicode_Check(in)) { Py_ssize_t size; s = PyUnicode_AsUTF8AndSize(in, &size); + if (s == NULL) { + return 0; + } if (size > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "string is too long"); return 0; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 03:45:08 2014 From: python-checkins at python.org (ned.deily) Date: Wed, 5 Feb 2014 03:45:08 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320465=3A_Update_S?= =?utf-8?q?QLite_shipped_with_OS_X_installer_to_3=2E8=2E3=2E?= Message-ID: <3fJnF02WFrz7LjS@mail.python.org> http://hg.python.org/cpython/rev/32af4954e46a changeset: 88967:32af4954e46a user: Ned Deily date: Tue Feb 04 18:44:17 2014 -0800 summary: Issue #20465: Update SQLite shipped with OS X installer to 3.8.3. files: Mac/BuildScript/build-installer.py | 6 +++--- Misc/NEWS | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -283,9 +283,9 @@ ), ), dict( - name="SQLite 3.8.1", - url="http://www.sqlite.org/2013/sqlite-autoconf-3080100.tar.gz", - checksum='8b5a0a02dfcb0c7daf90856a5cfd485a', + name="SQLite 3.8.3", + url="http://www.sqlite.org/2014/sqlite-autoconf-3080300.tar.gz", + checksum='11572878dc0ac74ae370367a464ab5cf', extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS4 ' '-DSQLITE_ENABLE_FTS3_PARENTHESIS ' diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -149,6 +149,11 @@ input and output sections of the block, allowing external tools to verify that the input has not changed (and thus the output is not out-of-date). +Build +----- + +- Issue #20465: Update SQLite shipped with OS X installer to 3.8.3. + What's New in Python 3.4.0 Beta 3? ================================== -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Wed Feb 5 09:44:32 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 05 Feb 2014 09:44:32 +0100 Subject: [Python-checkins] Daily reference leaks (145032f626d3): sum=-7 Message-ID: results for 145032f626d3 on branch "default" -------------------------------------------- test_site leaked [2, -2, 2] references, sum=2 test_site leaked [2, -2, 2] memory blocks, sum=2 test_urllib2net leaked [0, 1591, -1602] references, sum=-11 test_urllib2net leaked [0, 1410, -1410] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogYMmhlB', '-x'] From python-checkins at python.org Wed Feb 5 12:43:06 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 5 Feb 2014 12:43:06 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320489=3A_Explicit?= =?utf-8?q?ly_qualified_expressions_for_default_values_in_methods=2E?= Message-ID: <3fK19k12P7z7LjS@mail.python.org> http://hg.python.org/cpython/rev/31ddd45a05ba changeset: 88968:31ddd45a05ba user: Serhiy Storchaka date: Wed Feb 05 13:34:01 2014 +0200 summary: Issue #20489: Explicitly qualified expressions for default values in methods. files: Modules/clinic/zlibmodule.c.h | 6 +++--- Modules/zlibmodule.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/clinic/zlibmodule.c.h b/Modules/clinic/zlibmodule.c.h --- a/Modules/clinic/zlibmodule.c.h +++ b/Modules/clinic/zlibmodule.c.h @@ -245,7 +245,7 @@ } PyDoc_STRVAR(zlib_Compress_flush__doc__, -"sig=($self, mode=Z_FINISH)\n" +"sig=($self, mode=zlib.Z_FINISH)\n" "Return a bytes object containing any remaining compressed data.\n" "\n" " mode\n" @@ -325,7 +325,7 @@ #endif /* !defined(ZLIB_DECOMPRESS_COPY_METHODDEF) */ PyDoc_STRVAR(zlib_Decompress_flush__doc__, -"sig=($self, length=DEF_BUF_SIZE)\n" +"sig=($self, length=zlib.DEF_BUF_SIZE)\n" "Return a bytes object containing any remaining decompressed data.\n" "\n" " length\n" @@ -424,4 +424,4 @@ return return_value; } -/*[clinic end generated code: output=21556008559f839c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=67d3e81eafcfb982 input=a9049054013a1b77]*/ diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -828,7 +828,7 @@ /*[clinic input] zlib.Compress.flush - mode: int(c_default="Z_FINISH") = Z_FINISH + mode: int(c_default="Z_FINISH") = zlib.Z_FINISH One of the constants Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH. If mode == Z_FINISH, the compressor object can no longer be used after calling the flush() method. Otherwise, more data @@ -840,7 +840,7 @@ static PyObject * zlib_Compress_flush_impl(compobject *self, int mode) -/*[clinic end generated code: output=a203f4cefc9de727 input=6982996afe0772d8]*/ +/*[clinic end generated code: output=a203f4cefc9de727 input=73ed066794bd15bc]*/ { int err; unsigned int length = DEF_BUF_SIZE, new_length; @@ -1046,7 +1046,7 @@ /*[clinic input] zlib.Decompress.flush - length: uint(c_default="DEF_BUF_SIZE") = DEF_BUF_SIZE + length: uint(c_default="DEF_BUF_SIZE") = zlib.DEF_BUF_SIZE the initial size of the output buffer. / @@ -1055,7 +1055,7 @@ static PyObject * zlib_Decompress_flush_impl(compobject *self, unsigned int length) -/*[clinic end generated code: output=db6fb753ab698e22 input=fe7954136712c353]*/ +/*[clinic end generated code: output=db6fb753ab698e22 input=1580956505978993]*/ { int err; unsigned int new_length; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 12:43:07 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 5 Feb 2014 12:43:07 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNDk4?= =?utf-8?q?=3A_Fixed_io=2EStringIO_tests_for_newline=3D=27=5Cn=27=2E_Added?= =?utf-8?q?_new_tests=2E?= Message-ID: <3fK19l3fWbz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/07e7bb29a2c5 changeset: 88969:07e7bb29a2c5 branch: 2.7 parent: 88959:45aa817ec853 user: Serhiy Storchaka date: Wed Feb 05 13:41:38 2014 +0200 summary: Issue #20498: Fixed io.StringIO tests for newline='\n'. Added new tests. files: Lib/test/test_memoryio.py | 37 ++++++++++++++++++++++++++- 1 files changed, 36 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -522,6 +522,17 @@ self.assertIsNone(memio.errors) self.assertFalse(memio.line_buffering) + def test_newline_default(self): + memio = self.ioclass("a\nb\r\nc\rd") + self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + + memio = self.ioclass() + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + def test_newline_none(self): # newline=None memio = self.ioclass("a\nb\r\nc\rd", newline=None) @@ -531,6 +542,8 @@ self.assertEqual(memio.read(2), "\nb") self.assertEqual(memio.read(2), "\nc") self.assertEqual(memio.read(1), "\n") + self.assertEqual(memio.getvalue(), "a\nb\nc\nd") + memio = self.ioclass(newline=None) self.assertEqual(2, memio.write("a\n")) self.assertEqual(3, memio.write("b\r\n")) @@ -538,6 +551,7 @@ memio.seek(0) self.assertEqual(memio.read(), "a\nb\nc\nd") self.assertEqual(memio.getvalue(), "a\nb\nc\nd") + memio = self.ioclass("a\r\nb", newline=None) self.assertEqual(memio.read(3), "a\nb") @@ -550,6 +564,7 @@ self.assertEqual(memio.read(2), "\nc") self.assertEqual(memio.read(1), "\r") self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + memio = self.ioclass(newline="") self.assertEqual(2, memio.write("a\n")) self.assertEqual(2, memio.write("b\r")) @@ -557,11 +572,19 @@ self.assertEqual(2, memio.write("\rd")) memio.seek(0) self.assertEqual(list(memio), ["a\n", "b\r\n", "c\r", "d"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") def test_newline_lf(self): # newline="\n" - memio = self.ioclass("a\nb\r\nc\rd") + memio = self.ioclass("a\nb\r\nc\rd", newline="\n") self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + + memio = self.ioclass(newline="\n") + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") def test_newline_cr(self): # newline="\r" @@ -569,6 +592,12 @@ self.assertEqual(memio.read(), "a\rb\r\rc\rd") memio.seek(0) self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"]) + self.assertEqual(memio.getvalue(), "a\rb\r\rc\rd") + + memio = self.ioclass(newline="\r") + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"]) memio.seek(0) self.assertEqual(memio.readlines(), ["a\r", "b\r", "\r", "c\r", "d"]) self.assertEqual(memio.getvalue(), "a\rb\r\rc\rd") @@ -583,6 +612,12 @@ self.assertEqual(memio.readlines(), ["a\r\n", "b\r\r\n", "c\rd"]) self.assertEqual(memio.getvalue(), "a\r\nb\r\r\nc\rd") + memio = self.ioclass(newline="\r\n") + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\r\n", "b\r\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\r\nb\r\r\nc\rd") + def test_issue5265(self): # StringIO can duplicate newlines in universal newlines mode memio = self.ioclass("a\r\nb\r\n", newline=None) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 12:43:08 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 5 Feb 2014 12:43:08 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNDk4?= =?utf-8?q?=3A_Fixed_io=2EStringIO_tests_for_newline=3D=27=5Cn=27=2E_Added?= =?utf-8?q?_new_tests=2E?= Message-ID: <3fK19m6B69z7Lk3@mail.python.org> http://hg.python.org/cpython/rev/e23c928b9e39 changeset: 88970:e23c928b9e39 branch: 3.3 parent: 88965:d83ce3a2d954 user: Serhiy Storchaka date: Wed Feb 05 13:42:01 2014 +0200 summary: Issue #20498: Fixed io.StringIO tests for newline='\n'. Added new tests. files: Lib/test/test_memoryio.py | 37 ++++++++++++++++++++++++++- 1 files changed, 36 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -536,6 +536,17 @@ self.assertIsNone(memio.errors) self.assertFalse(memio.line_buffering) + def test_newline_default(self): + memio = self.ioclass("a\nb\r\nc\rd") + self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + + memio = self.ioclass() + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + def test_newline_none(self): # newline=None memio = self.ioclass("a\nb\r\nc\rd", newline=None) @@ -545,6 +556,8 @@ self.assertEqual(memio.read(2), "\nb") self.assertEqual(memio.read(2), "\nc") self.assertEqual(memio.read(1), "\n") + self.assertEqual(memio.getvalue(), "a\nb\nc\nd") + memio = self.ioclass(newline=None) self.assertEqual(2, memio.write("a\n")) self.assertEqual(3, memio.write("b\r\n")) @@ -552,6 +565,7 @@ memio.seek(0) self.assertEqual(memio.read(), "a\nb\nc\nd") self.assertEqual(memio.getvalue(), "a\nb\nc\nd") + memio = self.ioclass("a\r\nb", newline=None) self.assertEqual(memio.read(3), "a\nb") @@ -564,6 +578,7 @@ self.assertEqual(memio.read(2), "\nc") self.assertEqual(memio.read(1), "\r") self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + memio = self.ioclass(newline="") self.assertEqual(2, memio.write("a\n")) self.assertEqual(2, memio.write("b\r")) @@ -571,11 +586,19 @@ self.assertEqual(2, memio.write("\rd")) memio.seek(0) self.assertEqual(list(memio), ["a\n", "b\r\n", "c\r", "d"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") def test_newline_lf(self): # newline="\n" - memio = self.ioclass("a\nb\r\nc\rd") + memio = self.ioclass("a\nb\r\nc\rd", newline="\n") self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + + memio = self.ioclass(newline="\n") + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") def test_newline_cr(self): # newline="\r" @@ -583,6 +606,12 @@ self.assertEqual(memio.read(), "a\rb\r\rc\rd") memio.seek(0) self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"]) + self.assertEqual(memio.getvalue(), "a\rb\r\rc\rd") + + memio = self.ioclass(newline="\r") + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"]) memio.seek(0) self.assertEqual(memio.readlines(), ["a\r", "b\r", "\r", "c\r", "d"]) self.assertEqual(memio.getvalue(), "a\rb\r\rc\rd") @@ -597,6 +626,12 @@ self.assertEqual(memio.readlines(), ["a\r\n", "b\r\r\n", "c\rd"]) self.assertEqual(memio.getvalue(), "a\r\nb\r\r\nc\rd") + memio = self.ioclass(newline="\r\n") + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\r\n", "b\r\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\r\nb\r\r\nc\rd") + def test_issue5265(self): # StringIO can duplicate newlines in universal newlines mode memio = self.ioclass("a\r\nb\r\n", newline=None) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 12:43:10 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 5 Feb 2014 12:43:10 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320498=3A_Fixed_io=2EStringIO_tests_for_newline?= =?utf-8?b?PSdcbicuIEFkZGVkIG5ldyB0ZXN0cy4=?= Message-ID: <3fK19p1xnYz7LkH@mail.python.org> http://hg.python.org/cpython/rev/7ed8a9f9831d changeset: 88971:7ed8a9f9831d parent: 88968:31ddd45a05ba parent: 88970:e23c928b9e39 user: Serhiy Storchaka date: Wed Feb 05 13:42:29 2014 +0200 summary: Issue #20498: Fixed io.StringIO tests for newline='\n'. Added new tests. files: Lib/test/test_memoryio.py | 37 ++++++++++++++++++++++++++- 1 files changed, 36 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -536,6 +536,17 @@ self.assertIsNone(memio.errors) self.assertFalse(memio.line_buffering) + def test_newline_default(self): + memio = self.ioclass("a\nb\r\nc\rd") + self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + + memio = self.ioclass() + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + def test_newline_none(self): # newline=None memio = self.ioclass("a\nb\r\nc\rd", newline=None) @@ -545,6 +556,8 @@ self.assertEqual(memio.read(2), "\nb") self.assertEqual(memio.read(2), "\nc") self.assertEqual(memio.read(1), "\n") + self.assertEqual(memio.getvalue(), "a\nb\nc\nd") + memio = self.ioclass(newline=None) self.assertEqual(2, memio.write("a\n")) self.assertEqual(3, memio.write("b\r\n")) @@ -552,6 +565,7 @@ memio.seek(0) self.assertEqual(memio.read(), "a\nb\nc\nd") self.assertEqual(memio.getvalue(), "a\nb\nc\nd") + memio = self.ioclass("a\r\nb", newline=None) self.assertEqual(memio.read(3), "a\nb") @@ -564,6 +578,7 @@ self.assertEqual(memio.read(2), "\nc") self.assertEqual(memio.read(1), "\r") self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + memio = self.ioclass(newline="") self.assertEqual(2, memio.write("a\n")) self.assertEqual(2, memio.write("b\r")) @@ -571,11 +586,19 @@ self.assertEqual(2, memio.write("\rd")) memio.seek(0) self.assertEqual(list(memio), ["a\n", "b\r\n", "c\r", "d"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") def test_newline_lf(self): # newline="\n" - memio = self.ioclass("a\nb\r\nc\rd") + memio = self.ioclass("a\nb\r\nc\rd", newline="\n") self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") + + memio = self.ioclass(newline="\n") + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\nb\r\nc\rd") def test_newline_cr(self): # newline="\r" @@ -583,6 +606,12 @@ self.assertEqual(memio.read(), "a\rb\r\rc\rd") memio.seek(0) self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"]) + self.assertEqual(memio.getvalue(), "a\rb\r\rc\rd") + + memio = self.ioclass(newline="\r") + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"]) memio.seek(0) self.assertEqual(memio.readlines(), ["a\r", "b\r", "\r", "c\r", "d"]) self.assertEqual(memio.getvalue(), "a\rb\r\rc\rd") @@ -597,6 +626,12 @@ self.assertEqual(memio.readlines(), ["a\r\n", "b\r\r\n", "c\rd"]) self.assertEqual(memio.getvalue(), "a\r\nb\r\r\nc\rd") + memio = self.ioclass(newline="\r\n") + self.assertEqual(memio.write("a\nb\r\nc\rd"), 8) + memio.seek(0) + self.assertEqual(list(memio), ["a\r\n", "b\r\r\n", "c\rd"]) + self.assertEqual(memio.getvalue(), "a\r\nb\r\r\nc\rd") + def test_issue5265(self): # StringIO can duplicate newlines in universal newlines mode memio = self.ioclass("a\r\nb\r\n", newline=None) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 14:55:12 2014 From: python-checkins at python.org (nick.coghlan) Date: Wed, 5 Feb 2014 14:55:12 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320053=3A_new_test?= =?utf-8?q?_to_check_an_assumption?= Message-ID: <3fK4685hmxz7Llm@mail.python.org> http://hg.python.org/cpython/rev/ddc82c4d1a44 changeset: 88972:ddc82c4d1a44 user: Nick Coghlan date: Wed Feb 05 23:54:55 2014 +1000 summary: Issue #20053: new test to check an assumption files: Lib/test/test_venv.py | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -288,6 +288,13 @@ self.run_with_capture(venv.create, self.env_dir, with_pip=False) self.assert_pip_not_installed() + def test_devnull_exists_and_is_empty(self): + # Fix for issue #20053 uses os.devnull to force a config file to + # appear empty. Make sure that assumption is valid cross platform. + self.assertTrue(os.path.exists, os.devnull) + with open(os.devnull, "rb") as f: + self.assertEqual(f.read(), b"") + # Requesting pip fails without SSL (http://bugs.python.org/issue19744) @unittest.skipIf(ssl is None, ensurepip._MISSING_SSL_MESSAGE) def test_with_pip(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 16:33:24 2014 From: python-checkins at python.org (eric.smith) Date: Wed, 5 Feb 2014 16:33:24 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_TestNamedTuple=2Etest=5Fpi?= =?utf-8?q?ckle_was_only_testing_through_protocol_2=2E_Changed_to_have?= Message-ID: <3fK6HS0qf0z7LjP@mail.python.org> http://hg.python.org/cpython/rev/3dcbf8f928ec changeset: 88973:3dcbf8f928ec user: Eric V. Smith date: Wed Feb 05 10:33:14 2014 -0500 summary: TestNamedTuple.test_pickle was only testing through protocol 2. Changed to have it automatically test through the most recent version. files: Lib/test/test_collections.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -301,7 +301,7 @@ for module in (pickle,): loads = getattr(module, 'loads') dumps = getattr(module, 'dumps') - for protocol in -1, 0, 1, 2: + for protocol in range(-1, module.HIGHEST_PROTOCOL + 1): q = loads(dumps(p, protocol)) self.assertEqual(p, q) self.assertEqual(p._fields, q._fields) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 19:55:29 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 5 Feb 2014 19:55:29 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE5OTIw?= =?utf-8?q?=3A_TarFile=2Elist=28=29_no_longer_fails_when_outputs_a_listing?= Message-ID: <3fKBmd2WSnz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/a5895fca91f3 changeset: 88974:a5895fca91f3 branch: 3.3 parent: 88970:e23c928b9e39 user: Serhiy Storchaka date: Wed Feb 05 20:53:36 2014 +0200 summary: Issue #19920: TarFile.list() no longer fails when outputs a listing containing non-encodable characters. Added tests for TarFile.list(). Based on patch by Vajrasky Kok. files: Lib/tarfile.py | 28 ++++++--- Lib/test/test_tarfile.py | 78 ++++++++++++++++++++++++++++ Misc/NEWS | 5 + 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/Lib/tarfile.py b/Lib/tarfile.py --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -281,6 +281,12 @@ DeprecationWarning, 2) return stat.filemode(mode) +def _safe_print(s): + encoding = getattr(sys.stdout, 'encoding', None) + if encoding is not None: + s = s.encode(encoding, 'backslashreplace').decode(encoding) + print(s, end=' ') + class TarError(Exception): """Base exception.""" @@ -1870,24 +1876,24 @@ for tarinfo in self: if verbose: - print(stat.filemode(tarinfo.mode), end=' ') - print("%s/%s" % (tarinfo.uname or tarinfo.uid, - tarinfo.gname or tarinfo.gid), end=' ') + _safe_print(stat.filemode(tarinfo.mode)) + _safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid)) if tarinfo.ischr() or tarinfo.isblk(): - print("%10s" % ("%d,%d" \ - % (tarinfo.devmajor, tarinfo.devminor)), end=' ') + _safe_print("%10s" % + ("%d,%d" % (tarinfo.devmajor, tarinfo.devminor))) else: - print("%10d" % tarinfo.size, end=' ') - print("%d-%02d-%02d %02d:%02d:%02d" \ - % time.localtime(tarinfo.mtime)[:6], end=' ') + _safe_print("%10d" % tarinfo.size) + _safe_print("%d-%02d-%02d %02d:%02d:%02d" \ + % time.localtime(tarinfo.mtime)[:6]) - print(tarinfo.name + ("/" if tarinfo.isdir() else ""), end=' ') + _safe_print(tarinfo.name + ("/" if tarinfo.isdir() else "")) if verbose: if tarinfo.issym(): - print("->", tarinfo.linkname, end=' ') + _safe_print("-> " + tarinfo.linkname) if tarinfo.islnk(): - print("link to", tarinfo.linkname, end=' ') + _safe_print("link to " + tarinfo.linkname) print() def add(self, name, arcname=None, recursive=True, exclude=None, *, filter=None): diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -217,6 +217,84 @@ pass +class ListTest(ReadTest, unittest.TestCase): + + # Override setUp to use default encoding (UTF-8) + def setUp(self): + self.tar = tarfile.open(self.tarname, mode=self.mode) + + def test_list(self): + tio = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n') + with support.swap_attr(sys, 'stdout', tio): + self.tar.list(verbose=False) + out = tio.detach().getvalue() + self.assertIn(b'ustar/conttype', out) + self.assertIn(b'ustar/regtype', out) + self.assertIn(b'ustar/lnktype', out) + self.assertIn(b'ustar' + (b'/12345' * 40) + b'67/longname', out) + self.assertIn(b'./ustar/linktest2/symtype', out) + self.assertIn(b'./ustar/linktest2/lnktype', out) + # Make sure it puts trailing slash for directory + self.assertIn(b'ustar/dirtype/', out) + self.assertIn(b'ustar/dirtype-with-size/', out) + # Make sure it is able to print unencodable characters + self.assertIn(br'ustar/umlauts-' + br'\udcc4\udcd6\udcdc\udce4\udcf6\udcfc\udcdf', out) + self.assertIn(br'misc/regtype-hpux-signed-chksum-' + br'\udcc4\udcd6\udcdc\udce4\udcf6\udcfc\udcdf', out) + self.assertIn(br'misc/regtype-old-v7-signed-chksum-' + br'\udcc4\udcd6\udcdc\udce4\udcf6\udcfc\udcdf', out) + self.assertIn(br'pax/bad-pax-\udce4\udcf6\udcfc', out) + self.assertIn(br'pax/hdrcharset-\udce4\udcf6\udcfc', out) + # Make sure it prints files separated by one newline without any + # 'ls -l'-like accessories if verbose flag is not being used + # ... + # ustar/conttype + # ustar/regtype + # ... + self.assertRegex(out, br'ustar/conttype ?\r?\n' + br'ustar/regtype ?\r?\n') + # Make sure it does not print the source of link without verbose flag + self.assertNotIn(b'link to', out) + self.assertNotIn(b'->', out) + + def test_list_verbose(self): + tio = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n') + with support.swap_attr(sys, 'stdout', tio): + self.tar.list(verbose=True) + out = tio.detach().getvalue() + # Make sure it prints files separated by one newline with 'ls -l'-like + # accessories if verbose flag is being used + # ... + # ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/conttype + # ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/regtype + # ... + self.assertRegex(out, (br'-rw-r--r-- tarfile/tarfile\s+7011 ' + br'\d{4}-\d\d-\d\d\s+\d\d:\d\d:\d\d ' + br'ustar/\w+type ?\r?\n') * 2) + # Make sure it prints the source of link with verbose flag + self.assertIn(b'ustar/symtype -> regtype', out) + self.assertIn(b'./ustar/linktest2/symtype -> ../linktest1/regtype', out) + self.assertIn(b'./ustar/linktest2/lnktype link to ' + b'./ustar/linktest1/regtype', out) + self.assertIn(b'gnu' + (b'/123' * 125) + b'/longlink link to gnu' + + (b'/123' * 125) + b'/longname', out) + self.assertIn(b'pax' + (b'/123' * 125) + b'/longlink link to pax' + + (b'/123' * 125) + b'/longname', out) + + +class GzipListTest(GzipTest, ListTest): + pass + + +class Bz2ListTest(Bz2Test, ListTest): + pass + + +class LzmaListTest(LzmaTest, ListTest): + pass + + class CommonReadTest(ReadTest): def test_empty_tarfile(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,9 @@ Library ------- +- Issue #19920: TarFile.list() no longer fails when outputs a listing + containing non-encodable characters. Based on patch by Vajrasky Kok. + - Issue #20515: Fix NULL pointer dereference introduced by issue #20368. - Issue #19186: Restore namespacing of expat symbols inside the pyexpat module. @@ -321,6 +324,8 @@ Tests ----- +- Issue #19920: Added tests for TarFile.list(). Based on patch by Vajrasky Kok. + - Issue #19990: Added tests for the imghdr module. Based on patch by Claudiu Popa. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 19:55:30 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 5 Feb 2014 19:55:30 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2319920=3A_TarFile=2Elist=28=29_no_longer_fails_w?= =?utf-8?q?hen_outputs_a_listing?= Message-ID: <3fKBmf6PVcz7Lk9@mail.python.org> http://hg.python.org/cpython/rev/077aa6d4f3b7 changeset: 88975:077aa6d4f3b7 parent: 88973:3dcbf8f928ec parent: 88974:a5895fca91f3 user: Serhiy Storchaka date: Wed Feb 05 20:54:43 2014 +0200 summary: Issue #19920: TarFile.list() no longer fails when outputs a listing containing non-encodable characters. Added tests for TarFile.list(). Based on patch by Vajrasky Kok. files: Lib/tarfile.py | 28 +++-- Lib/test/test_tarfile.py | 117 ++++++++++++++++++++++---- Misc/NEWS | 5 + 3 files changed, 121 insertions(+), 29 deletions(-) diff --git a/Lib/tarfile.py b/Lib/tarfile.py --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -257,6 +257,12 @@ DeprecationWarning, 2) return stat.filemode(mode) +def _safe_print(s): + encoding = getattr(sys.stdout, 'encoding', None) + if encoding is not None: + s = s.encode(encoding, 'backslashreplace').decode(encoding) + print(s, end=' ') + class TarError(Exception): """Base exception.""" @@ -1846,24 +1852,24 @@ for tarinfo in self: if verbose: - print(stat.filemode(tarinfo.mode), end=' ') - print("%s/%s" % (tarinfo.uname or tarinfo.uid, - tarinfo.gname or tarinfo.gid), end=' ') + _safe_print(stat.filemode(tarinfo.mode)) + _safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid)) if tarinfo.ischr() or tarinfo.isblk(): - print("%10s" % ("%d,%d" \ - % (tarinfo.devmajor, tarinfo.devminor)), end=' ') + _safe_print("%10s" % + ("%d,%d" % (tarinfo.devmajor, tarinfo.devminor))) else: - print("%10d" % tarinfo.size, end=' ') - print("%d-%02d-%02d %02d:%02d:%02d" \ - % time.localtime(tarinfo.mtime)[:6], end=' ') + _safe_print("%10d" % tarinfo.size) + _safe_print("%d-%02d-%02d %02d:%02d:%02d" \ + % time.localtime(tarinfo.mtime)[:6]) - print(tarinfo.name + ("/" if tarinfo.isdir() else ""), end=' ') + _safe_print(tarinfo.name + ("/" if tarinfo.isdir() else "")) if verbose: if tarinfo.issym(): - print("->", tarinfo.linkname, end=' ') + _safe_print("-> " + tarinfo.linkname) if tarinfo.islnk(): - print("link to", tarinfo.linkname, end=' ') + _safe_print("link to " + tarinfo.linkname) print() def add(self, name, arcname=None, recursive=True, exclude=None, *, filter=None): diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -219,6 +219,84 @@ pass +class ListTest(ReadTest, unittest.TestCase): + + # Override setUp to use default encoding (UTF-8) + def setUp(self): + self.tar = tarfile.open(self.tarname, mode=self.mode) + + def test_list(self): + tio = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n') + with support.swap_attr(sys, 'stdout', tio): + self.tar.list(verbose=False) + out = tio.detach().getvalue() + self.assertIn(b'ustar/conttype', out) + self.assertIn(b'ustar/regtype', out) + self.assertIn(b'ustar/lnktype', out) + self.assertIn(b'ustar' + (b'/12345' * 40) + b'67/longname', out) + self.assertIn(b'./ustar/linktest2/symtype', out) + self.assertIn(b'./ustar/linktest2/lnktype', out) + # Make sure it puts trailing slash for directory + self.assertIn(b'ustar/dirtype/', out) + self.assertIn(b'ustar/dirtype-with-size/', out) + # Make sure it is able to print unencodable characters + self.assertIn(br'ustar/umlauts-' + br'\udcc4\udcd6\udcdc\udce4\udcf6\udcfc\udcdf', out) + self.assertIn(br'misc/regtype-hpux-signed-chksum-' + br'\udcc4\udcd6\udcdc\udce4\udcf6\udcfc\udcdf', out) + self.assertIn(br'misc/regtype-old-v7-signed-chksum-' + br'\udcc4\udcd6\udcdc\udce4\udcf6\udcfc\udcdf', out) + self.assertIn(br'pax/bad-pax-\udce4\udcf6\udcfc', out) + self.assertIn(br'pax/hdrcharset-\udce4\udcf6\udcfc', out) + # Make sure it prints files separated by one newline without any + # 'ls -l'-like accessories if verbose flag is not being used + # ... + # ustar/conttype + # ustar/regtype + # ... + self.assertRegex(out, br'ustar/conttype ?\r?\n' + br'ustar/regtype ?\r?\n') + # Make sure it does not print the source of link without verbose flag + self.assertNotIn(b'link to', out) + self.assertNotIn(b'->', out) + + def test_list_verbose(self): + tio = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n') + with support.swap_attr(sys, 'stdout', tio): + self.tar.list(verbose=True) + out = tio.detach().getvalue() + # Make sure it prints files separated by one newline with 'ls -l'-like + # accessories if verbose flag is being used + # ... + # ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/conttype + # ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/regtype + # ... + self.assertRegex(out, (br'\?rw-r--r-- tarfile/tarfile\s+7011 ' + br'\d{4}-\d\d-\d\d\s+\d\d:\d\d:\d\d ' + br'ustar/\w+type ?\r?\n') * 2) + # Make sure it prints the source of link with verbose flag + self.assertIn(b'ustar/symtype -> regtype', out) + self.assertIn(b'./ustar/linktest2/symtype -> ../linktest1/regtype', out) + self.assertIn(b'./ustar/linktest2/lnktype link to ' + b'./ustar/linktest1/regtype', out) + self.assertIn(b'gnu' + (b'/123' * 125) + b'/longlink link to gnu' + + (b'/123' * 125) + b'/longname', out) + self.assertIn(b'pax' + (b'/123' * 125) + b'/longlink link to pax' + + (b'/123' * 125) + b'/longname', out) + + +class GzipListTest(GzipTest, ListTest): + pass + + +class Bz2ListTest(Bz2Test, ListTest): + pass + + +class LzmaListTest(LzmaTest, ListTest): + pass + + class CommonReadTest(ReadTest): def test_empty_tarfile(self): @@ -1766,8 +1844,9 @@ class CommandLineTest(unittest.TestCase): - def tarfilecmd(self, *args): - rc, out, err = script_helper.assert_python_ok('-m', 'tarfile', *args) + def tarfilecmd(self, *args, **kwargs): + rc, out, err = script_helper.assert_python_ok('-m', 'tarfile', *args, + **kwargs) return out.replace(os.linesep.encode(), b'\n') def tarfilecmd_failure(self, *args): @@ -1815,24 +1894,26 @@ support.unlink(tmpname) def test_list_command(self): - self.make_simple_tarfile(tmpname) - with support.captured_stdout() as t: - with tarfile.open(tmpname, 'r') as tf: - tf.list(verbose=False) - expected = t.getvalue().encode(sys.getfilesystemencoding()) - for opt in '-l', '--list': - out = self.tarfilecmd(opt, tmpname) - self.assertEqual(out, expected) + for tar_name in testtarnames: + with support.captured_stdout() as t: + with tarfile.open(tar_name, 'r') as tf: + tf.list(verbose=False) + expected = t.getvalue().encode('ascii', 'backslashreplace') + for opt in '-l', '--list': + out = self.tarfilecmd(opt, tar_name, + PYTHONIOENCODING='ascii') + self.assertEqual(out, expected) def test_list_command_verbose(self): - self.make_simple_tarfile(tmpname) - with support.captured_stdout() as t: - with tarfile.open(tmpname, 'r') as tf: - tf.list(verbose=True) - expected = t.getvalue().encode(sys.getfilesystemencoding()) - for opt in '-v', '--verbose': - out = self.tarfilecmd(opt, '-l', tmpname) - self.assertEqual(out, expected) + for tar_name in testtarnames: + with support.captured_stdout() as t: + with tarfile.open(tar_name, 'r') as tf: + tf.list(verbose=True) + expected = t.getvalue().encode('ascii', 'backslashreplace') + for opt in '-v', '--verbose': + out = self.tarfilecmd(opt, '-l', tar_name, + PYTHONIOENCODING='ascii') + self.assertEqual(out, expected) def test_list_command_invalid_file(self): zipname = support.findfile('zipdir.zip') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,9 @@ Library ------- +- Issue #19920: TarFile.list() no longer fails when outputs a listing + containing non-encodable characters. Based on patch by Vajrasky Kok. + - Issue #20515: Fix NULL pointer dereference introduced by issue #20368. - Issue #19186: Restore namespacing of expat symbols inside the pyexpat module. @@ -118,6 +121,8 @@ Tests ----- +- Issue #19920: Added tests for TarFile.list(). Based on patch by Vajrasky Kok. + - Issue #19990: Added tests for the imghdr module. Based on patch by Claudiu Popa. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 19:55:32 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 5 Feb 2014 19:55:32 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5OTIw?= =?utf-8?q?=3A_Added_tests_for_TarFile=2Elist=28=29=2E_Based_on_patch_by_V?= =?utf-8?q?ajrasky_Kok=2E?= Message-ID: <3fKBmh2Z9Dz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/48c5c18110ae changeset: 88976:48c5c18110ae branch: 2.7 parent: 88969:07e7bb29a2c5 user: Serhiy Storchaka date: Wed Feb 05 20:55:13 2014 +0200 summary: Issue #19920: Added tests for TarFile.list(). Based on patch by Vajrasky Kok. files: Lib/test/test_tarfile.py | 77 ++++++++++++++++++++++++++++ Misc/NEWS | 2 + 2 files changed, 79 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -158,6 +158,80 @@ self._test_fileobj_link("symtype2", "ustar/regtype") +class ListTest(ReadTest, unittest.TestCase): + + # Override setUp to use default encoding (UTF-8) + def setUp(self): + self.tar = tarfile.open(self.tarname, mode=self.mode) + + def test_list(self): + with test_support.captured_stdout() as t: + self.tar.list(verbose=False) + out = t.getvalue() + self.assertIn('ustar/conttype', out) + self.assertIn('ustar/regtype', out) + self.assertIn('ustar/lnktype', out) + self.assertIn('ustar' + ('/12345' * 40) + '67/longname', out) + self.assertIn('./ustar/linktest2/symtype', out) + self.assertIn('./ustar/linktest2/lnktype', out) + # Make sure it puts trailing slash for directory + self.assertIn('ustar/dirtype/', out) + self.assertIn('ustar/dirtype-with-size/', out) + # Make sure it is able to print non-ASCII characters + self.assertIn('ustar/umlauts-' + '\xc4\xd6\xdc\xe4\xf6\xfc\xdf', out) + self.assertIn('misc/regtype-hpux-signed-chksum-' + '\xc4\xd6\xdc\xe4\xf6\xfc\xdf', out) + self.assertIn('misc/regtype-old-v7-signed-chksum-' + '\xc4\xd6\xdc\xe4\xf6\xfc\xdf', out) + # Make sure it prints files separated by one newline without any + # 'ls -l'-like accessories if verbose flag is not being used + # ... + # ustar/conttype + # ustar/regtype + # ... + self.assertRegexpMatches(out, r'ustar/conttype ?\r?\n' + r'ustar/regtype ?\r?\n') + # Make sure it does not print the source of link without verbose flag + self.assertNotIn('link to', out) + self.assertNotIn('->', out) + + def test_list_verbose(self): + with test_support.captured_stdout() as t: + self.tar.list(verbose=True) + out = t.getvalue() + # Make sure it prints files separated by one newline with 'ls -l'-like + # accessories if verbose flag is being used + # ... + # ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/conttype + # ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/regtype + # ... + self.assertRegexpMatches(out, (r'-rw-r--r-- tarfile/tarfile\s+7011 ' + r'\d{4}-\d\d-\d\d\s+\d\d:\d\d:\d\d ' + r'ustar/\w+type ?\r?\n') * 2) + # Make sure it prints the source of link with verbose flag + self.assertIn('ustar/symtype -> regtype', out) + self.assertIn('./ustar/linktest2/symtype -> ../linktest1/regtype', out) + self.assertIn('./ustar/linktest2/lnktype link to ' + './ustar/linktest1/regtype', out) + self.assertIn('gnu' + ('/123' * 125) + '/longlink link to gnu' + + ('/123' * 125) + '/longname', out) + self.assertIn('pax' + ('/123' * 125) + '/longlink link to pax' + + ('/123' * 125) + '/longname', out) + + +class GzipListTest(ListTest): + tarname = gzipname + mode = "r:gz" + taropen = tarfile.TarFile.gzopen + + +class Bz2ListTest(ListTest): + tarname = bz2name + mode = "r:bz2" + taropen = tarfile.TarFile.bz2open + + class CommonReadTest(ReadTest): def test_empty_tarfile(self): @@ -1646,6 +1720,7 @@ MemberReadTest, GNUReadTest, PaxReadTest, + ListTest, WriteTest, StreamWriteTest, GNUWriteTest, @@ -1677,6 +1752,7 @@ GzipMiscReadTest, GzipUstarReadTest, GzipStreamReadTest, + GzipListTest, GzipWriteTest, GzipStreamWriteTest, ] @@ -1691,6 +1767,7 @@ Bz2MiscReadTest, Bz2UstarReadTest, Bz2StreamReadTest, + Bz2ListTest, Bz2WriteTest, Bz2StreamWriteTest, Bz2PartialReadTest, diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -224,6 +224,8 @@ Tests ----- +- Issue #19920: Added tests for TarFile.list(). Based on patch by Vajrasky Kok. + - Issue #19990: Added tests for the imghdr module. Based on patch by Claudiu Popa. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 20:56:52 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 5 Feb 2014 20:56:52 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE0NTE1OiBjbGFy?= =?utf-8?q?ify_that_TemporaryDirectory=27s_=5F=5Fenter=5F=5F_returns_the_n?= =?utf-8?q?ame=2E?= Message-ID: <3fKD7S4swpz7LjW@mail.python.org> http://hg.python.org/cpython/rev/b5fe07d39e16 changeset: 88977:b5fe07d39e16 branch: 3.3 parent: 88974:a5895fca91f3 user: R David Murray date: Wed Feb 05 14:53:40 2014 -0500 summary: #14515: clarify that TemporaryDirectory's __enter__ returns the name. files: Doc/library/tempfile.rst | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -97,12 +97,14 @@ This function creates a temporary directory using :func:`mkdtemp` (the supplied arguments are passed directly to the underlying function). The resulting object can be used as a context manager (see - :ref:`context-managers`). On completion of the context (or destruction - of the temporary directory object), the newly created temporary directory + :ref:`context-managers`). On completion of the context or destruction + of the temporary directory object the newly created temporary directory and all its contents are removed from the filesystem. - The directory name can be retrieved from the :attr:`name` attribute - of the returned object. + The directory name can be retrieved from the :attr:`name` attribute of the + returned object. When the returned object is used as a context manager, the + :attr:`name` will be assigned to the target of the :keyword:`as` clause in + the :keyword:`with` statement, if there is one. The directory can be explicitly cleaned up by calling the :func:`cleanup` method. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Feb 5 20:56:53 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 5 Feb 2014 20:56:53 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_=2314515=3A_clarify_that_TemporaryDirectory=27s_=5F=5Fen?= =?utf-8?q?ter=5F=5F_returns_the_name=2E?= Message-ID: <3fKD7T6CY1z7Ljj@mail.python.org> http://hg.python.org/cpython/rev/7b7e17723787 changeset: 88978:7b7e17723787 parent: 88975:077aa6d4f3b7 parent: 88977:b5fe07d39e16 user: R David Murray date: Wed Feb 05 14:56:39 2014 -0500 summary: #14515: clarify that TemporaryDirectory's __enter__ returns the name. files: Doc/library/tempfile.rst | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -97,12 +97,14 @@ This function creates a temporary directory using :func:`mkdtemp` (the supplied arguments are passed directly to the underlying function). The resulting object can be used as a context manager (see - :ref:`context-managers`). On completion of the context (or destruction - of the temporary directory object), the newly created temporary directory + :ref:`context-managers`). On completion of the context or destruction + of the temporary directory object the newly created temporary directory and all its contents are removed from the filesystem. - The directory name can be retrieved from the :attr:`name` attribute - of the returned object. + The directory name can be retrieved from the :attr:`name` attribute of the + returned object. When the returned object is used as a context manager, the + :attr:`name` will be assigned to the target of the :keyword:`as` clause in + the :keyword:`with` statement, if there is one. The directory can be explicitly cleaned up by calling the :func:`cleanup` method. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 00:11:44 2014 From: python-checkins at python.org (yury.selivanov) Date: Thu, 6 Feb 2014 00:11:44 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=2Estreams=3A_Use_b?= =?utf-8?q?ytebuffer_in_StreamReader=3B_Add_assertion_in_feed=5Fdata?= Message-ID: <3fKJSJ5SL5z7LjW@mail.python.org> http://hg.python.org/cpython/rev/260d6e1e9b0f changeset: 88979:260d6e1e9b0f user: Yury Selivanov date: Wed Feb 05 18:11:13 2014 -0500 summary: asyncio.streams: Use bytebuffer in StreamReader; Add assertion in feed_data files: Lib/asyncio/streams.py | 71 ++++--------- Lib/test/test_asyncio/test_streams.py | 79 +++++++++----- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -4,8 +4,6 @@ 'open_connection', 'start_server', 'IncompleteReadError', ] -import collections - from . import events from . import futures from . import protocols @@ -259,9 +257,7 @@ if loop is None: loop = events.get_event_loop() self._loop = loop - # TODO: Use a bytearray for a buffer, like the transport. - self._buffer = collections.deque() # Deque of bytes objects. - self._byte_count = 0 # Bytes in buffer. + self._buffer = bytearray() self._eof = False # Whether we're done. self._waiter = None # A future. self._exception = None @@ -285,7 +281,7 @@ self._transport = transport def _maybe_resume_transport(self): - if self._paused and self._byte_count <= self._limit: + if self._paused and len(self._buffer) <= self._limit: self._paused = False self._transport.resume_reading() @@ -298,11 +294,12 @@ waiter.set_result(True) def feed_data(self, data): + assert not self._eof, 'feed_data after feed_eof' + if not data: return - self._buffer.append(data) - self._byte_count += len(data) + self._buffer.extend(data) waiter = self._waiter if waiter is not None: @@ -312,7 +309,7 @@ if (self._transport is not None and not self._paused and - self._byte_count > 2*self._limit): + len(self._buffer) > 2*self._limit): try: self._transport.pause_reading() except NotImplementedError: @@ -338,28 +335,22 @@ if self._exception is not None: raise self._exception - parts = [] - parts_size = 0 + line = bytearray() not_enough = True while not_enough: while self._buffer and not_enough: - data = self._buffer.popleft() - ichar = data.find(b'\n') + ichar = self._buffer.find(b'\n') if ichar < 0: - parts.append(data) - parts_size += len(data) + line.extend(self._buffer) + self._buffer.clear() else: ichar += 1 - head, tail = data[:ichar], data[ichar:] - if tail: - self._buffer.appendleft(tail) + line.extend(self._buffer[:ichar]) + del self._buffer[:ichar] not_enough = False - parts.append(head) - parts_size += len(head) - if parts_size > self._limit: - self._byte_count -= parts_size + if len(line) > self._limit: self._maybe_resume_transport() raise ValueError('Line is too long') @@ -373,11 +364,8 @@ finally: self._waiter = None - line = b''.join(parts) - self._byte_count -= parts_size self._maybe_resume_transport() - - return line + return bytes(line) @tasks.coroutine def read(self, n=-1): @@ -395,36 +383,23 @@ finally: self._waiter = None else: - if not self._byte_count and not self._eof: + if not self._buffer and not self._eof: self._waiter = self._create_waiter('read') try: yield from self._waiter finally: self._waiter = None - if n < 0 or self._byte_count <= n: - data = b''.join(self._buffer) + if n < 0 or len(self._buffer) <= n: + data = bytes(self._buffer) self._buffer.clear() - self._byte_count = 0 - self._maybe_resume_transport() - return data + else: + # n > 0 and len(self._buffer) > n + data = bytes(self._buffer[:n]) + del self._buffer[:n] - parts = [] - parts_bytes = 0 - while self._buffer and parts_bytes < n: - data = self._buffer.popleft() - data_bytes = len(data) - if n < parts_bytes + data_bytes: - data_bytes = n - parts_bytes - data, rest = data[:data_bytes], data[data_bytes:] - self._buffer.appendleft(rest) - - parts.append(data) - parts_bytes += data_bytes - self._byte_count -= data_bytes - self._maybe_resume_transport() - - return b''.join(parts) + self._maybe_resume_transport() + return data @tasks.coroutine def readexactly(self, n): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -79,13 +79,13 @@ stream = asyncio.StreamReader(loop=self.loop) stream.feed_data(b'') - self.assertEqual(0, stream._byte_count) + self.assertEqual(b'', stream._buffer) - def test_feed_data_byte_count(self): + def test_feed_nonempty_data(self): stream = asyncio.StreamReader(loop=self.loop) stream.feed_data(self.DATA) - self.assertEqual(len(self.DATA), stream._byte_count) + self.assertEqual(self.DATA, stream._buffer) def test_read_zero(self): # Read zero bytes. @@ -94,7 +94,7 @@ data = self.loop.run_until_complete(stream.read(0)) self.assertEqual(b'', data) - self.assertEqual(len(self.DATA), stream._byte_count) + self.assertEqual(self.DATA, stream._buffer) def test_read(self): # Read bytes. @@ -107,7 +107,7 @@ data = self.loop.run_until_complete(read_task) self.assertEqual(self.DATA, data) - self.assertFalse(stream._byte_count) + self.assertEqual(b'', stream._buffer) def test_read_line_breaks(self): # Read bytes without line breaks. @@ -118,7 +118,7 @@ data = self.loop.run_until_complete(stream.read(5)) self.assertEqual(b'line1', data) - self.assertEqual(5, stream._byte_count) + self.assertEqual(b'line2', stream._buffer) def test_read_eof(self): # Read bytes, stop at eof. @@ -131,7 +131,7 @@ data = self.loop.run_until_complete(read_task) self.assertEqual(b'', data) - self.assertFalse(stream._byte_count) + self.assertEqual(b'', stream._buffer) def test_read_until_eof(self): # Read all bytes until eof. @@ -147,7 +147,7 @@ data = self.loop.run_until_complete(read_task) self.assertEqual(b'chunk1\nchunk2', data) - self.assertFalse(stream._byte_count) + self.assertEqual(b'', stream._buffer) def test_read_exception(self): stream = asyncio.StreamReader(loop=self.loop) @@ -161,7 +161,8 @@ ValueError, self.loop.run_until_complete, stream.read(2)) def test_readline(self): - # Read one line. + # Read one line. 'readline' will need to wait for the data + # to come from 'cb' stream = asyncio.StreamReader(loop=self.loop) stream.feed_data(b'chunk1 ') read_task = asyncio.Task(stream.readline(), loop=self.loop) @@ -174,30 +175,40 @@ line = self.loop.run_until_complete(read_task) self.assertEqual(b'chunk1 chunk2 chunk3 \n', line) - self.assertEqual(len(b'\n chunk4')-1, stream._byte_count) + self.assertEqual(b' chunk4', stream._buffer) def test_readline_limit_with_existing_data(self): - stream = asyncio.StreamReader(3, loop=self.loop) + # Read one line. The data is in StreamReader's buffer + # before the event loop is run. + + stream = asyncio.StreamReader(limit=3, loop=self.loop) stream.feed_data(b'li') stream.feed_data(b'ne1\nline2\n') self.assertRaises( ValueError, self.loop.run_until_complete, stream.readline()) - self.assertEqual([b'line2\n'], list(stream._buffer)) + # The buffer should contain the remaining data after exception + self.assertEqual(b'line2\n', stream._buffer) - stream = asyncio.StreamReader(3, loop=self.loop) + stream = asyncio.StreamReader(limit=3, loop=self.loop) stream.feed_data(b'li') stream.feed_data(b'ne1') stream.feed_data(b'li') self.assertRaises( ValueError, self.loop.run_until_complete, stream.readline()) - self.assertEqual([b'li'], list(stream._buffer)) - self.assertEqual(2, stream._byte_count) + # No b'\n' at the end. The 'limit' is set to 3. So before + # waiting for the new data in buffer, 'readline' will consume + # the entire buffer, and since the length of the consumed data + # is more than 3, it will raise a ValudError. The buffer is + # expected to be empty now. + self.assertEqual(b'', stream._buffer) def test_readline_limit(self): - stream = asyncio.StreamReader(7, loop=self.loop) + # Read one line. StreamReaders are fed with data after + # their 'readline' methods are called. + stream = asyncio.StreamReader(limit=7, loop=self.loop) def cb(): stream.feed_data(b'chunk1') stream.feed_data(b'chunk2') @@ -207,10 +218,25 @@ self.assertRaises( ValueError, self.loop.run_until_complete, stream.readline()) - self.assertEqual([b'chunk3\n'], list(stream._buffer)) - self.assertEqual(7, stream._byte_count) + # The buffer had just one line of data, and after raising + # a ValueError it should be empty. + self.assertEqual(b'', stream._buffer) - def test_readline_line_byte_count(self): + stream = asyncio.StreamReader(limit=7, loop=self.loop) + def cb(): + stream.feed_data(b'chunk1') + stream.feed_data(b'chunk2\n') + stream.feed_data(b'chunk3\n') + stream.feed_eof() + self.loop.call_soon(cb) + + self.assertRaises( + ValueError, self.loop.run_until_complete, stream.readline()) + self.assertEqual(b'chunk3\n', stream._buffer) + + def test_readline_nolimit_nowait(self): + # All needed data for the first 'readline' call will be + # in the buffer. stream = asyncio.StreamReader(loop=self.loop) stream.feed_data(self.DATA[:6]) stream.feed_data(self.DATA[6:]) @@ -218,7 +244,7 @@ line = self.loop.run_until_complete(stream.readline()) self.assertEqual(b'line1\n', line) - self.assertEqual(len(self.DATA) - len(b'line1\n'), stream._byte_count) + self.assertEqual(b'line2\nline3\n', stream._buffer) def test_readline_eof(self): stream = asyncio.StreamReader(loop=self.loop) @@ -244,9 +270,7 @@ data = self.loop.run_until_complete(stream.read(7)) self.assertEqual(b'line2\nl', data) - self.assertEqual( - len(self.DATA) - len(b'line1\n') - len(b'line2\nl'), - stream._byte_count) + self.assertEqual(b'ine3\n', stream._buffer) def test_readline_exception(self): stream = asyncio.StreamReader(loop=self.loop) @@ -258,6 +282,7 @@ stream.set_exception(ValueError()) self.assertRaises( ValueError, self.loop.run_until_complete, stream.readline()) + self.assertEqual(b'', stream._buffer) def test_readexactly_zero_or_less(self): # Read exact number of bytes (zero or less). @@ -266,11 +291,11 @@ data = self.loop.run_until_complete(stream.readexactly(0)) self.assertEqual(b'', data) - self.assertEqual(len(self.DATA), stream._byte_count) + self.assertEqual(self.DATA, stream._buffer) data = self.loop.run_until_complete(stream.readexactly(-1)) self.assertEqual(b'', data) - self.assertEqual(len(self.DATA), stream._byte_count) + self.assertEqual(self.DATA, stream._buffer) def test_readexactly(self): # Read exact number of bytes. @@ -287,7 +312,7 @@ data = self.loop.run_until_complete(read_task) self.assertEqual(self.DATA + self.DATA, data) - self.assertEqual(len(self.DATA), stream._byte_count) + self.assertEqual(self.DATA, stream._buffer) def test_readexactly_eof(self): # Read exact number of bytes (eof). @@ -306,7 +331,7 @@ self.assertEqual(cm.exception.expected, n) self.assertEqual(str(cm.exception), '18 bytes read on a total of 36 expected bytes') - self.assertFalse(stream._byte_count) + self.assertEqual(b'', stream._buffer) def test_readexactly_exception(self): stream = asyncio.StreamReader(loop=self.loop) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 01:55:51 2014 From: python-checkins at python.org (ned.deily) Date: Thu, 6 Feb 2014 01:55:51 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwMzc0?= =?utf-8?q?=3A_Avoid_compiler_warnings_when_compiling_readline_with_libedi?= =?utf-8?q?t=2E?= Message-ID: <3fKLmR6r2zz7LjW@mail.python.org> http://hg.python.org/cpython/rev/0b5b0bfcc7b1 changeset: 88980:0b5b0bfcc7b1 branch: 2.7 parent: 88976:48c5c18110ae user: Ned Deily date: Wed Feb 05 16:52:26 2014 -0800 summary: Issue #20374: Avoid compiler warnings when compiling readline with libedit. files: Modules/readline.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c --- a/Modules/readline.c +++ b/Modules/readline.c @@ -749,15 +749,24 @@ return result; } + static int +#if defined(_RL_FUNCTION_TYPEDEF) on_startup_hook(void) +#else +on_startup_hook() +#endif { return on_hook(startup_hook); } #ifdef HAVE_RL_PRE_INPUT_HOOK static int +#if defined(_RL_FUNCTION_TYPEDEF) on_pre_input_hook(void) +#else +on_pre_input_hook() +#endif { return on_hook(pre_input_hook); } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 01:55:53 2014 From: python-checkins at python.org (ned.deily) Date: Thu, 6 Feb 2014 01:55:53 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwMzc0?= =?utf-8?q?=3A_Avoid_compiler_warnings_when_compiling_readline_with_libedi?= =?utf-8?q?t=2E?= Message-ID: <3fKLmT5JQQz7Lkb@mail.python.org> http://hg.python.org/cpython/rev/9131a9edcac4 changeset: 88981:9131a9edcac4 branch: 3.3 parent: 88977:b5fe07d39e16 user: Ned Deily date: Wed Feb 05 16:53:10 2014 -0800 summary: Issue #20374: Avoid compiler warnings when compiling readline with libedit. files: Modules/readline.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c --- a/Modules/readline.c +++ b/Modules/readline.c @@ -773,15 +773,24 @@ return result; } + static int +#if defined(_RL_FUNCTION_TYPEDEF) on_startup_hook(void) +#else +on_startup_hook() +#endif { return on_hook(startup_hook); } #ifdef HAVE_RL_PRE_INPUT_HOOK static int +#if defined(_RL_FUNCTION_TYPEDEF) on_pre_input_hook(void) +#else +on_pre_input_hook() +#endif { return on_hook(pre_input_hook); } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 01:55:55 2014 From: python-checkins at python.org (ned.deily) Date: Thu, 6 Feb 2014 01:55:55 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320374=3A_merge?= Message-ID: <3fKLmW017mz7Lkl@mail.python.org> http://hg.python.org/cpython/rev/0abf103f5559 changeset: 88982:0abf103f5559 parent: 88979:260d6e1e9b0f parent: 88981:9131a9edcac4 user: Ned Deily date: Wed Feb 05 16:55:20 2014 -0800 summary: Issue #20374: merge files: Modules/readline.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c --- a/Modules/readline.c +++ b/Modules/readline.c @@ -815,8 +815,13 @@ return result; } + static int +#if defined(_RL_FUNCTION_TYPEDEF) on_startup_hook(void) +#else +on_startup_hook() +#endif { int r; #ifdef WITH_THREAD @@ -831,7 +836,11 @@ #ifdef HAVE_RL_PRE_INPUT_HOOK static int +#if defined(_RL_FUNCTION_TYPEDEF) on_pre_input_hook(void) +#else +on_pre_input_hook() +#endif { int r; #ifdef WITH_THREAD -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 02:04:29 2014 From: python-checkins at python.org (ned.deily) Date: Thu, 6 Feb 2014 02:04:29 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwMzc0?= =?utf-8?q?=3A_delete_spurious_empty_line?= Message-ID: <3fKLyP1Bfcz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/74c7a6fd8b45 changeset: 88983:74c7a6fd8b45 branch: 2.7 parent: 88980:0b5b0bfcc7b1 user: Ned Deily date: Wed Feb 05 17:01:41 2014 -0800 summary: Issue #20374: delete spurious empty line files: Modules/readline.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c --- a/Modules/readline.c +++ b/Modules/readline.c @@ -749,7 +749,6 @@ return result; } - static int #if defined(_RL_FUNCTION_TYPEDEF) on_startup_hook(void) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 02:04:30 2014 From: python-checkins at python.org (ned.deily) Date: Thu, 6 Feb 2014 02:04:30 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwMzc0?= =?utf-8?q?=3A_delete_spurious_empty_line?= Message-ID: <3fKLyQ2vV6z7Ljh@mail.python.org> http://hg.python.org/cpython/rev/6616c94d6149 changeset: 88984:6616c94d6149 branch: 3.3 parent: 88981:9131a9edcac4 user: Ned Deily date: Wed Feb 05 17:02:29 2014 -0800 summary: Issue #20374: delete spurious empty line files: Modules/readline.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c --- a/Modules/readline.c +++ b/Modules/readline.c @@ -773,7 +773,6 @@ return result; } - static int #if defined(_RL_FUNCTION_TYPEDEF) on_startup_hook(void) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 02:04:31 2014 From: python-checkins at python.org (ned.deily) Date: Thu, 6 Feb 2014 02:04:31 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320374=3A_merge?= Message-ID: <3fKLyR4b4qz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/0b91e764b889 changeset: 88985:0b91e764b889 parent: 88982:0abf103f5559 parent: 88984:6616c94d6149 user: Ned Deily date: Wed Feb 05 17:03:42 2014 -0800 summary: Issue #20374: merge files: Modules/readline.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c --- a/Modules/readline.c +++ b/Modules/readline.c @@ -815,7 +815,6 @@ return result; } - static int #if defined(_RL_FUNCTION_TYPEDEF) on_startup_hook(void) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 06:19:08 2014 From: python-checkins at python.org (yury.selivanov) Date: Thu, 6 Feb 2014 06:19:08 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=2Estreams=2EStream?= =?utf-8?b?UmVhZGVyOiBBZGQgJ2F0X2VvZigpJyBtZXRob2Q=?= Message-ID: <3fKScD26KPz7LjR@mail.python.org> http://hg.python.org/cpython/rev/4a3761dedbd2 changeset: 88986:4a3761dedbd2 parent: 88979:260d6e1e9b0f user: Yury Selivanov date: Thu Feb 06 00:14:30 2014 -0500 summary: asyncio.streams.StreamReader: Add 'at_eof()' method files: Lib/asyncio/streams.py | 4 ++++ Lib/test/test_asyncio/test_streams.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 0 deletions(-) diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -293,6 +293,10 @@ if not waiter.cancelled(): waiter.set_result(True) + def at_eof(self): + """Return True if the buffer is empty and 'feed_eof' was called.""" + return self._eof and not self._buffer + def feed_data(self, data): assert not self._eof, 'feed_data after feed_eof' diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -204,6 +204,21 @@ # expected to be empty now. self.assertEqual(b'', stream._buffer) + def test_at_eof(self): + stream = asyncio.StreamReader(loop=self.loop) + self.assertFalse(stream.at_eof()) + + stream.feed_data(b'some data\n') + self.assertFalse(stream.at_eof()) + + self.loop.run_until_complete(stream.readline()) + self.assertFalse(stream.at_eof()) + + stream.feed_data(b'some data\n') + stream.feed_eof() + self.loop.run_until_complete(stream.readline()) + self.assertTrue(stream.at_eof()) + def test_readline_limit(self): # Read one line. StreamReaders are fed with data after # their 'readline' methods are called. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 06:19:09 2014 From: python-checkins at python.org (yury.selivanov) Date: Thu, 6 Feb 2014 06:19:09 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge_heads?= Message-ID: <3fKScF3pnwz7LjR@mail.python.org> http://hg.python.org/cpython/rev/259857de72e1 changeset: 88987:259857de72e1 parent: 88986:4a3761dedbd2 parent: 88985:0b91e764b889 user: Yury Selivanov date: Thu Feb 06 00:18:48 2014 -0500 summary: merge heads files: Modules/readline.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c --- a/Modules/readline.c +++ b/Modules/readline.c @@ -816,7 +816,11 @@ } static int +#if defined(_RL_FUNCTION_TYPEDEF) on_startup_hook(void) +#else +on_startup_hook() +#endif { int r; #ifdef WITH_THREAD @@ -831,7 +835,11 @@ #ifdef HAVE_RL_PRE_INPUT_HOOK static int +#if defined(_RL_FUNCTION_TYPEDEF) on_pre_input_hook(void) +#else +on_pre_input_hook() +#endif { int r; #ifdef WITH_THREAD -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 08:28:02 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 08:28:02 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNTIw?= =?utf-8?q?=3A_Fixed_readline_test_in_test=5Fcodecs=2E?= Message-ID: <3fKWSy1zXqz7LjM@mail.python.org> http://hg.python.org/cpython/rev/305b6a5da852 changeset: 88988:305b6a5da852 branch: 2.7 parent: 88983:74c7a6fd8b45 user: Serhiy Storchaka date: Thu Feb 06 09:26:32 2014 +0200 summary: Issue #20520: Fixed readline test in test_codecs. files: Lib/test/test_codecs.py | 21 +++++++++++++++------ 1 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -97,19 +97,20 @@ self.assertEqual(readalllines(s, True, 10), sexpected) self.assertEqual(readalllines(s, False, 10), sexpectednoends) + lineends = ("\n", "\r\n", "\r", u"\u2028") # Test long lines (multiple calls to read() in readline()) vw = [] vwo = [] - for (i, lineend) in enumerate(u"\n \r\n \r \u2028".split()): - vw.append((i*200)*u"\3042" + lineend) - vwo.append((i*200)*u"\3042") - self.assertEqual(readalllines("".join(vw), True), "".join(vw)) - self.assertEqual(readalllines("".join(vw), False),"".join(vwo)) + for (i, lineend) in enumerate(lineends): + vw.append((i*200+200)*u"\u3042" + lineend) + vwo.append((i*200+200)*u"\u3042") + self.assertEqual(readalllines("".join(vw), True), "|".join(vw)) + self.assertEqual(readalllines("".join(vw), False), "|".join(vwo)) # Test lines where the first read might end with \r, so the # reader has to look ahead whether this is a lone \r or a \r\n for size in xrange(80): - for lineend in u"\n \r\n \r \u2028".split(): + for lineend in lineends: s = 10*(size*u"a" + lineend + u"xxx\n") reader = getreader(s) for i in xrange(10): @@ -117,12 +118,20 @@ reader.readline(keepends=True), size*u"a" + lineend, ) + self.assertEqual( + reader.readline(keepends=True), + "xxx\n", + ) reader = getreader(s) for i in xrange(10): self.assertEqual( reader.readline(keepends=False), size*u"a", ) + self.assertEqual( + reader.readline(keepends=False), + "xxx", + ) def test_mixed_readline_and_read(self): lines = ["Humpty Dumpty sat on a wall,\n", -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 08:28:03 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 08:28:03 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTIw?= =?utf-8?q?=3A_Fixed_readline_test_in_test=5Fcodecs=2E?= Message-ID: <3fKWSz3nYTz7LjM@mail.python.org> http://hg.python.org/cpython/rev/82d374a9bbc7 changeset: 88989:82d374a9bbc7 branch: 3.3 parent: 88984:6616c94d6149 user: Serhiy Storchaka date: Thu Feb 06 09:26:56 2014 +0200 summary: Issue #20520: Fixed readline test in test_codecs. files: Lib/test/test_codecs.py | 21 +++++++++++++++------ 1 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -146,19 +146,20 @@ self.assertEqual(readalllines(s, True, 10), sexpected) self.assertEqual(readalllines(s, False, 10), sexpectednoends) + lineends = ("\n", "\r\n", "\r", "\u2028") # Test long lines (multiple calls to read() in readline()) vw = [] vwo = [] - for (i, lineend) in enumerate("\n \r\n \r \u2028".split()): - vw.append((i*200)*"\3042" + lineend) - vwo.append((i*200)*"\3042") - self.assertEqual(readalllines("".join(vw), True), "".join(vw)) - self.assertEqual(readalllines("".join(vw), False),"".join(vwo)) + for (i, lineend) in enumerate(lineends): + vw.append((i*200+200)*"\u3042" + lineend) + vwo.append((i*200+200)*"\u3042") + self.assertEqual(readalllines("".join(vw), True), "|".join(vw)) + self.assertEqual(readalllines("".join(vw), False), "|".join(vwo)) # Test lines where the first read might end with \r, so the # reader has to look ahead whether this is a lone \r or a \r\n for size in range(80): - for lineend in "\n \r\n \r \u2028".split(): + for lineend in lineends: s = 10*(size*"a" + lineend + "xxx\n") reader = getreader(s) for i in range(10): @@ -166,12 +167,20 @@ reader.readline(keepends=True), size*"a" + lineend, ) + self.assertEqual( + reader.readline(keepends=True), + "xxx\n", + ) reader = getreader(s) for i in range(10): self.assertEqual( reader.readline(keepends=False), size*"a", ) + self.assertEqual( + reader.readline(keepends=False), + "xxx", + ) def test_mixed_readline_and_read(self): lines = ["Humpty Dumpty sat on a wall,\n", -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 08:28:04 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 08:28:04 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320520=3A_Fixed_readline_test_in_test=5Fcodecs?= =?utf-8?q?=2E?= Message-ID: <3fKWT05Fhjz7LlQ@mail.python.org> http://hg.python.org/cpython/rev/1457fa0da200 changeset: 88990:1457fa0da200 parent: 88987:259857de72e1 parent: 88989:82d374a9bbc7 user: Serhiy Storchaka date: Thu Feb 06 09:27:28 2014 +0200 summary: Issue #20520: Fixed readline test in test_codecs. files: Lib/test/test_codecs.py | 21 +++++++++++++++------ 1 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -148,19 +148,20 @@ self.assertEqual(readalllines(s, True, 10), sexpected) self.assertEqual(readalllines(s, False, 10), sexpectednoends) + lineends = ("\n", "\r\n", "\r", "\u2028") # Test long lines (multiple calls to read() in readline()) vw = [] vwo = [] - for (i, lineend) in enumerate("\n \r\n \r \u2028".split()): - vw.append((i*200)*"\3042" + lineend) - vwo.append((i*200)*"\3042") - self.assertEqual(readalllines("".join(vw), True), "".join(vw)) - self.assertEqual(readalllines("".join(vw), False),"".join(vwo)) + for (i, lineend) in enumerate(lineends): + vw.append((i*200+200)*"\u3042" + lineend) + vwo.append((i*200+200)*"\u3042") + self.assertEqual(readalllines("".join(vw), True), "|".join(vw)) + self.assertEqual(readalllines("".join(vw), False), "|".join(vwo)) # Test lines where the first read might end with \r, so the # reader has to look ahead whether this is a lone \r or a \r\n for size in range(80): - for lineend in "\n \r\n \r \u2028".split(): + for lineend in lineends: s = 10*(size*"a" + lineend + "xxx\n") reader = getreader(s) for i in range(10): @@ -168,12 +169,20 @@ reader.readline(keepends=True), size*"a" + lineend, ) + self.assertEqual( + reader.readline(keepends=True), + "xxx\n", + ) reader = getreader(s) for i in range(10): self.assertEqual( reader.readline(keepends=False), size*"a", ) + self.assertEqual( + reader.readline(keepends=False), + "xxx", + ) def test_mixed_readline_and_read(self): lines = ["Humpty Dumpty sat on a wall,\n", -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Thu Feb 6 09:49:07 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 06 Feb 2014 09:49:07 +0100 Subject: [Python-checkins] Daily reference leaks (0b91e764b889): sum=0 Message-ID: results for 0b91e764b889 on branch "default" -------------------------------------------- test_site leaked [-2, 2, 0] references, sum=0 test_site leaked [-2, 2, 0] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogXZyqG1', '-x'] From python-checkins at python.org Thu Feb 6 11:19:37 2014 From: python-checkins at python.org (ronald.oussoren) Date: Thu, 6 Feb 2014 11:19:37 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2314455=3A_fix_hand?= =?utf-8?q?ling_of_unsigned_long_long_values_for_binary_plist_files?= Message-ID: <3fKbGx2vyLz7LjN@mail.python.org> http://hg.python.org/cpython/rev/0121c2b7dcce changeset: 88991:0121c2b7dcce user: Ronald Oussoren date: Thu Feb 06 11:19:18 2014 +0100 summary: Issue #14455: fix handling of unsigned long long values for binary plist files Values in the range of an unsigned long long, but outside of the range of a signed long long were serialized as a negative value. Due to a bug in PyObjC my test scripts indicated that the previous behavior matched Apple's plist code, instead the handle large unsigned values correctly. The change to plistlib.py is from a patch by Serhiy. files: Doc/library/plistlib.rst | 7 - Lib/plistlib.py | 11 +- Lib/test/test_plistlib.py | 132 ++++++----- Mac/Tools/plistlib_generate_testdata.py | 3 +- Misc/NEWS | 4 + 5 files changed, 79 insertions(+), 78 deletions(-) diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -107,13 +107,6 @@ An :exc:`OverflowError` will be raised for integer values that cannot be represented in (binary) plist files. - .. warning:: - - For compatibility with Apple's libraries it is possible to write - an integer in the range from 2 ** 63 upto (and including) 2 ** 64 - to binary plists, even though these will be read back as negative - values. - .. versionadded: 3.4 diff --git a/Lib/plistlib.py b/Lib/plistlib.py --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -879,18 +879,19 @@ try: self._fp.write(struct.pack('>Bq', 0x13, value)) except struct.error: - raise OverflowError(value) + raise OverflowError(value) from None elif value < 1 << 8: self._fp.write(struct.pack('>BB', 0x10, value)) elif value < 1 << 16: self._fp.write(struct.pack('>BH', 0x11, value)) elif value < 1 << 32: self._fp.write(struct.pack('>BL', 0x12, value)) + elif value < 1 << 63: + self._fp.write(struct.pack('>BQ', 0x13, value)) + elif value < 1 << 64: + self._fp.write(b'\x14' + value.to_bytes(16, 'big', signed=True)) else: - try: - self._fp.write(struct.pack('>BQ', 0x13, value)) - except struct.error: - raise OverflowError(value) + raise OverflowError(value) elif isinstance(value, float): self._fp.write(struct.pack('>Bd', 0x23, value)) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -22,72 +22,73 @@ IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+YUJp Z0ludDwva2V5PgoJPGludGVnZXI+OTIyMzM3MjAzNjg1NDc3NTc2NDwvaW50 - ZWdlcj4KCTxrZXk+YURhdGU8L2tleT4KCTxkYXRlPjIwMDQtMTAtMjZUMTA6 - MzM6MzNaPC9kYXRlPgoJPGtleT5hRGljdDwva2V5PgoJPGRpY3Q+CgkJPGtl - eT5hRmFsc2VWYWx1ZTwva2V5PgoJCTxmYWxzZS8+CgkJPGtleT5hVHJ1ZVZh - bHVlPC9rZXk+CgkJPHRydWUvPgoJCTxrZXk+YVVuaWNvZGVWYWx1ZTwva2V5 - PgoJCTxzdHJpbmc+TcOkc3NpZywgTWHDnzwvc3RyaW5nPgoJCTxrZXk+YW5v - dGhlclN0cmluZzwva2V5PgoJCTxzdHJpbmc+Jmx0O2hlbGxvICZhbXA7ICdo - aScgdGhlcmUhJmd0Ozwvc3RyaW5nPgoJCTxrZXk+ZGVlcGVyRGljdDwva2V5 - PgoJCTxkaWN0PgoJCQk8a2V5PmE8L2tleT4KCQkJPGludGVnZXI+MTc8L2lu - dGVnZXI+CgkJCTxrZXk+Yjwva2V5PgoJCQk8cmVhbD4zMi41PC9yZWFsPgoJ - CQk8a2V5PmM8L2tleT4KCQkJPGFycmF5PgoJCQkJPGludGVnZXI+MTwvaW50 - ZWdlcj4KCQkJCTxpbnRlZ2VyPjI8L2ludGVnZXI+CgkJCQk8c3RyaW5nPnRl - eHQ8L3N0cmluZz4KCQkJPC9hcnJheT4KCQk8L2RpY3Q+Cgk8L2RpY3Q+Cgk8 - a2V5PmFGbG9hdDwva2V5PgoJPHJlYWw+MC41PC9yZWFsPgoJPGtleT5hTGlz - dDwva2V5PgoJPGFycmF5PgoJCTxzdHJpbmc+QTwvc3RyaW5nPgoJCTxzdHJp - bmc+Qjwvc3RyaW5nPgoJCTxpbnRlZ2VyPjEyPC9pbnRlZ2VyPgoJCTxyZWFs - PjMyLjU8L3JlYWw+CgkJPGFycmF5PgoJCQk8aW50ZWdlcj4xPC9pbnRlZ2Vy - PgoJCQk8aW50ZWdlcj4yPC9pbnRlZ2VyPgoJCQk8aW50ZWdlcj4zPC9pbnRl - Z2VyPgoJCTwvYXJyYXk+Cgk8L2FycmF5PgoJPGtleT5hTmVnYXRpdmVCaWdJ - bnQ8L2tleT4KCTxpbnRlZ2VyPi04MDAwMDAwMDAwMDwvaW50ZWdlcj4KCTxr - ZXk+YU5lZ2F0aXZlSW50PC9rZXk+Cgk8aW50ZWdlcj4tNTwvaW50ZWdlcj4K - CTxrZXk+YVN0cmluZzwva2V5PgoJPHN0cmluZz5Eb29kYWg8L3N0cmluZz4K - CTxrZXk+YW5FbXB0eURpY3Q8L2tleT4KCTxkaWN0Lz4KCTxrZXk+YW5FbXB0 - eUxpc3Q8L2tleT4KCTxhcnJheS8+Cgk8a2V5PmFuSW50PC9rZXk+Cgk8aW50 - ZWdlcj43Mjg8L2ludGVnZXI+Cgk8a2V5Pm5lc3RlZERhdGE8L2tleT4KCTxh - cnJheT4KCQk8ZGF0YT4KCQlQR3h2ZEhNZ2IyWWdZbWx1WVhKNUlHZDFibXMr - QUFFQ0F6eHNiM1J6SUc5bUlHSnBibUZ5ZVNCbmRXNXIKCQlQZ0FCQWdNOGJH - OTBjeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlEUEd4dmRITWdiMllnWW1s - dVlYSjUKCQlJR2QxYm1zK0FBRUNBenhzYjNSeklHOW1JR0pwYm1GeWVTQm5k - VzVyUGdBQkFnTThiRzkwY3lCdlppQmkKCQlhVzVoY25rZ1ozVnVhejRBQVFJ - RFBHeHZkSE1nYjJZZ1ltbHVZWEo1SUdkMWJtcytBQUVDQXp4c2IzUnoKCQlJ - RzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004Ykc5MGN5QnZaaUJpYVc1aGNu - a2daM1Z1YXo0QUFRSUQKCQlQR3h2ZEhNZ2IyWWdZbWx1WVhKNUlHZDFibXMr - QUFFQ0F3PT0KCQk8L2RhdGE+Cgk8L2FycmF5PgoJPGtleT5zb21lRGF0YTwv - a2V5PgoJPGRhdGE+CglQR0pwYm1GeWVTQm5kVzVyUGc9PQoJPC9kYXRhPgoJ - PGtleT5zb21lTW9yZURhdGE8L2tleT4KCTxkYXRhPgoJUEd4dmRITWdiMlln + ZWdlcj4KCTxrZXk+YUJpZ0ludDI8L2tleT4KCTxpbnRlZ2VyPjkyMjMzNzIw + MzY4NTQ3NzU4NTI8L2ludGVnZXI+Cgk8a2V5PmFEYXRlPC9rZXk+Cgk8ZGF0 + ZT4yMDA0LTEwLTI2VDEwOjMzOjMzWjwvZGF0ZT4KCTxrZXk+YURpY3Q8L2tl + eT4KCTxkaWN0PgoJCTxrZXk+YUZhbHNlVmFsdWU8L2tleT4KCQk8ZmFsc2Uv + PgoJCTxrZXk+YVRydWVWYWx1ZTwva2V5PgoJCTx0cnVlLz4KCQk8a2V5PmFV + bmljb2RlVmFsdWU8L2tleT4KCQk8c3RyaW5nPk3DpHNzaWcsIE1hw588L3N0 + cmluZz4KCQk8a2V5PmFub3RoZXJTdHJpbmc8L2tleT4KCQk8c3RyaW5nPiZs + dDtoZWxsbyAmYW1wOyAnaGknIHRoZXJlISZndDs8L3N0cmluZz4KCQk8a2V5 + PmRlZXBlckRpY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5hPC9rZXk+CgkJ + CTxpbnRlZ2VyPjE3PC9pbnRlZ2VyPgoJCQk8a2V5PmI8L2tleT4KCQkJPHJl + YWw+MzIuNTwvcmVhbD4KCQkJPGtleT5jPC9rZXk+CgkJCTxhcnJheT4KCQkJ + CTxpbnRlZ2VyPjE8L2ludGVnZXI+CgkJCQk8aW50ZWdlcj4yPC9pbnRlZ2Vy + PgoJCQkJPHN0cmluZz50ZXh0PC9zdHJpbmc+CgkJCTwvYXJyYXk+CgkJPC9k + aWN0PgoJPC9kaWN0PgoJPGtleT5hRmxvYXQ8L2tleT4KCTxyZWFsPjAuNTwv + cmVhbD4KCTxrZXk+YUxpc3Q8L2tleT4KCTxhcnJheT4KCQk8c3RyaW5nPkE8 + L3N0cmluZz4KCQk8c3RyaW5nPkI8L3N0cmluZz4KCQk8aW50ZWdlcj4xMjwv + aW50ZWdlcj4KCQk8cmVhbD4zMi41PC9yZWFsPgoJCTxhcnJheT4KCQkJPGlu + dGVnZXI+MTwvaW50ZWdlcj4KCQkJPGludGVnZXI+MjwvaW50ZWdlcj4KCQkJ + PGludGVnZXI+MzwvaW50ZWdlcj4KCQk8L2FycmF5PgoJPC9hcnJheT4KCTxr + ZXk+YU5lZ2F0aXZlQmlnSW50PC9rZXk+Cgk8aW50ZWdlcj4tODAwMDAwMDAw + MDA8L2ludGVnZXI+Cgk8a2V5PmFOZWdhdGl2ZUludDwva2V5PgoJPGludGVn + ZXI+LTU8L2ludGVnZXI+Cgk8a2V5PmFTdHJpbmc8L2tleT4KCTxzdHJpbmc+ + RG9vZGFoPC9zdHJpbmc+Cgk8a2V5PmFuRW1wdHlEaWN0PC9rZXk+Cgk8ZGlj + dC8+Cgk8a2V5PmFuRW1wdHlMaXN0PC9rZXk+Cgk8YXJyYXkvPgoJPGtleT5h + bkludDwva2V5PgoJPGludGVnZXI+NzI4PC9pbnRlZ2VyPgoJPGtleT5uZXN0 + ZWREYXRhPC9rZXk+Cgk8YXJyYXk+CgkJPGRhdGE+CgkJUEd4dmRITWdiMlln WW1sdVlYSjVJR2QxYm1zK0FBRUNBenhzYjNSeklHOW1JR0pwYm1GeWVTQm5k - VzVyUGdBQkFnTTgKCWJHOTBjeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlE - UEd4dmRITWdiMllnWW1sdVlYSjVJR2QxYm1zK0FBRUNBenhzCgliM1J6SUc5 - bUlHSnBibUZ5ZVNCbmRXNXJQZ0FCQWdNOGJHOTBjeUJ2WmlCaWFXNWhjbmtn - WjNWdWF6NEFBUUlEUEd4dgoJZEhNZ2IyWWdZbWx1WVhKNUlHZDFibXMrQUFF - Q0F6eHNiM1J6SUc5bUlHSnBibUZ5ZVNCbmRXNXJQZ0FCQWdNOGJHOTAKCWN5 - QnZaaUJpYVc1aGNua2daM1Z1YXo0QUFRSURQR3h2ZEhNZ2IyWWdZbWx1WVhK - NUlHZDFibXMrQUFFQ0F3PT0KCTwvZGF0YT4KCTxrZXk+w4ViZW5yYWE8L2tl - eT4KCTxzdHJpbmc+VGhhdCB3YXMgYSB1bmljb2RlIGtleS48L3N0cmluZz4K - PC9kaWN0Pgo8L3BsaXN0Pgo='''), + VzVyCgkJUGdBQkFnTThiRzkwY3lCdlppQmlhVzVoY25rZ1ozVnVhejRBQVFJ + RFBHeHZkSE1nYjJZZ1ltbHVZWEo1CgkJSUdkMWJtcytBQUVDQXp4c2IzUnpJ + RzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004Ykc5MGN5QnZaaUJpCgkJYVc1 + aGNua2daM1Z1YXo0QUFRSURQR3h2ZEhNZ2IyWWdZbWx1WVhKNUlHZDFibXMr + QUFFQ0F6eHNiM1J6CgkJSUc5bUlHSnBibUZ5ZVNCbmRXNXJQZ0FCQWdNOGJH + OTBjeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlECgkJUEd4dmRITWdiMlln + WW1sdVlYSjVJR2QxYm1zK0FBRUNBdz09CgkJPC9kYXRhPgoJPC9hcnJheT4K + CTxrZXk+c29tZURhdGE8L2tleT4KCTxkYXRhPgoJUEdKcGJtRnllU0JuZFc1 + clBnPT0KCTwvZGF0YT4KCTxrZXk+c29tZU1vcmVEYXRhPC9rZXk+Cgk8ZGF0 + YT4KCVBHeHZkSE1nYjJZZ1ltbHVZWEo1SUdkMWJtcytBQUVDQXp4c2IzUnpJ + RzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004CgliRzkwY3lCdlppQmlhVzVo + Y25rZ1ozVnVhejRBQVFJRFBHeHZkSE1nYjJZZ1ltbHVZWEo1SUdkMWJtcytB + QUVDQXp4cwoJYjNSeklHOW1JR0pwYm1GeWVTQm5kVzVyUGdBQkFnTThiRzkw + Y3lCdlppQmlhVzVoY25rZ1ozVnVhejRBQVFJRFBHeHYKCWRITWdiMllnWW1s + dVlYSjVJR2QxYm1zK0FBRUNBenhzYjNSeklHOW1JR0pwYm1GeWVTQm5kVzVy + UGdBQkFnTThiRzkwCgljeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlEUEd4 + dmRITWdiMllnWW1sdVlYSjVJR2QxYm1zK0FBRUNBdz09Cgk8L2RhdGE+Cgk8 + a2V5PsOFYmVucmFhPC9rZXk+Cgk8c3RyaW5nPlRoYXQgd2FzIGEgdW5pY29k + ZSBrZXkuPC9zdHJpbmc+CjwvZGljdD4KPC9wbGlzdD4K'''), plistlib.FMT_BINARY: binascii.a2b_base64(b''' - YnBsaXN0MDDfEA8BAgMEBQYHCAkKCwwNDg8QERImJy0uLzAxMjM1NDZXYUJp - Z0ludFVhRGF0ZVVhRGljdFZhRmxvYXRVYUxpc3RfEA9hTmVnYXRpdmVCaWdJ - bnRcYU5lZ2F0aXZlSW50V2FTdHJpbmdbYW5FbXB0eURpY3RbYW5FbXB0eUxp - c3RVYW5JbnRabmVzdGVkRGF0YVhzb21lRGF0YVxzb21lTW9yZURhdGFnAMUA - YgBlAG4AcgBhAGETf////////9QzQZy5ffQAAADVExQVFhcYGRobHFthRmFs - c2VWYWx1ZVphVHJ1ZVZhbHVlXWFVbmljb2RlVmFsdWVdYW5vdGhlclN0cmlu - Z1pkZWVwZXJEaWN0CAlrAE0A5ABzAHMAaQBnACwAIABNAGEA318QFTxoZWxs - byAmICdoaScgdGhlcmUhPtMdHh8gISJRYVFiUWMQESNAQEAAAAAAAKMjJCUQ - ARACVHRleHQjP+AAAAAAAAClKCkqIStRQVFCEAyjIyQsEAMT////7V+g4AAT - //////////tWRG9vZGFo0KARAtihNE8Q+jxsb3RzIG9mIGJpbmFyeSBndW5r - PgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5 - IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBi - aW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3Rz - IG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQID - PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5r - PgABAgNNPGJpbmFyeSBndW5rPl8QF1RoYXQgd2FzIGEgdW5pY29kZSBrZXku - AAgAKQAxADcAPQBEAEoAXABpAHEAfQCJAI8AmgCjALAAvwDIANEA3ADoAPMB - AQEPARoBGwEcATMBSwFSAVQBVgFYAVoBYwFnAWkBawFwAXkBfwGBAYMBhQGJ - AYsBlAGdAaQBpQGmAakBqwKoArYAAAAAAAACAQAAAAAAAAA3AAAAAAAAAAAA - AAAAAAAC0A=='''), + YnBsaXN0MDDfEBABAgMEBQYHCAkKCwwNDg8QERITFCgpLzAxMjM0NTc2OFdh + QmlnSW50WGFCaWdJbnQyVWFEYXRlVWFEaWN0VmFGbG9hdFVhTGlzdF8QD2FO + ZWdhdGl2ZUJpZ0ludFxhTmVnYXRpdmVJbnRXYVN0cmluZ1thbkVtcHR5RGlj + dFthbkVtcHR5TGlzdFVhbkludFpuZXN0ZWREYXRhWHNvbWVEYXRhXHNvbWVN + b3JlRGF0YWcAxQBiAGUAbgByAGEAYRN/////////1BQAAAAAAAAAAIAAAAAA + AAAsM0GcuX30AAAA1RUWFxgZGhscHR5bYUZhbHNlVmFsdWVaYVRydWVWYWx1 + ZV1hVW5pY29kZVZhbHVlXWFub3RoZXJTdHJpbmdaZGVlcGVyRGljdAgJawBN + AOQAcwBzAGkAZwAsACAATQBhAN9fEBU8aGVsbG8gJiAnaGknIHRoZXJlIT7T + HyAhIiMkUWFRYlFjEBEjQEBAAAAAAACjJSYnEAEQAlR0ZXh0Iz/gAAAAAAAA + pSorLCMtUUFRQhAMoyUmLhADE////+1foOAAE//////////7VkRvb2RhaNCg + EQLYoTZPEPo8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmlu + YXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBv + ZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxs + b3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4A + AQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBn + dW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDTTxiaW5hcnkgZ3Vu + az5fEBdUaGF0IHdhcyBhIHVuaWNvZGUga2V5LgAIACsAMwA8AEIASABPAFUA + ZwB0AHwAiACUAJoApQCuALsAygDTAOQA7QD4AQQBDwEdASsBNgE3ATgBTwFn + AW4BcAFyAXQBdgF/AYMBhQGHAYwBlQGbAZ0BnwGhAaUBpwGwAbkBwAHBAcIB + xQHHAsQC0gAAAAAAAAIBAAAAAAAAADkAAAAAAAAAAAAAAAAAAALs'''), } @@ -106,6 +107,7 @@ aFloat = 0.5, anInt = 728, aBigInt = 2 ** 63 - 44, + aBigInt2 = 2 ** 63 + 44, aNegativeInt = -5, aNegativeBigInt = -80000000000, aDict=dict( @@ -152,7 +154,7 @@ def test_int(self): for pl in [0, 2**8-1, 2**8, 2**16-1, 2**16, 2**32-1, 2**32, - 2**63-1, 1, -2**63]: + 2**63-1, 2**64-1, 1, -2**63]: for fmt in ALL_FORMATS: with self.subTest(pl=pl, fmt=fmt): data = plistlib.dumps(pl, fmt=fmt) diff --git a/Mac/Tools/plistlib_generate_testdata.py b/Mac/Tools/plistlib_generate_testdata.py --- a/Mac/Tools/plistlib_generate_testdata.py +++ b/Mac/Tools/plistlib_generate_testdata.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from Cocoa import NSMutableDictionary, NSMutableArray, NSString, NSDate +from Cocoa import NSMutableDictionary, NSMutableArray, NSString, NSDate, NSNumber from Cocoa import NSPropertyListSerialization, NSPropertyListOpenStepFormat from Cocoa import NSPropertyListXMLFormat_v1_0, NSPropertyListBinaryFormat_v1_0 from Cocoa import CFUUIDCreateFromString, NSNull, NSUUID, CFPropertyListCreateData @@ -30,6 +30,7 @@ seconds = datetime.datetime(2004, 10, 26, 10, 33, 33, tzinfo=datetime.timezone(datetime.timedelta(0))).timestamp() pl[nsstr('aBigInt')] = 2 ** 63 - 44 + pl[nsstr('aBigInt2')] = NSNumber.numberWithUnsignedLongLong_(2 ** 63 + 44) pl[nsstr('aDate')] = NSDate.dateWithTimeIntervalSince1970_(seconds) pl[nsstr('aDict')] = d = OrderedDict() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -109,6 +109,10 @@ - Issue #15304: concurrent.futures.wait() can block forever even if Futures have completed. Patch by Glenn Langford. +- Issue #14455: plistlib: fix serializing integers integers in the range + of an unsigned long long but outside of the range of signed long long for + binary plist files. + IDLE ---- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 15:23:03 2014 From: python-checkins at python.org (brett.cannon) Date: Thu, 6 Feb 2014 15:23:03 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=236386=3A_When_exec?= =?utf-8?q?uting_a_script_that=27s_a_symlink=2C_the_directory?= Message-ID: <3fKhgq4cq8z7M0b@mail.python.org> http://hg.python.org/cpython/rev/47c31e7d3779 changeset: 88992:47c31e7d3779 user: Brett Cannon date: Thu Feb 06 09:22:51 2014 -0500 summary: Issue #6386: When executing a script that's a symlink, the directory where the symlink resolves to is added to sys.path, not the directory containing the symlink itself. Thanks to Sanko Resic for an initial attempt at the patch. files: Doc/tutorial/modules.rst | 10 ++++++++-- Misc/NEWS | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst --- a/Doc/tutorial/modules.rst +++ b/Doc/tutorial/modules.rst @@ -165,10 +165,16 @@ named :file:`spam.py` in a list of directories given by the variable :data:`sys.path`. :data:`sys.path` is initialized from these locations: -* the directory containing the input script (or the current directory). +* The directory containing the input script (or the current directory when no + file is specified). * :envvar:`PYTHONPATH` (a list of directory names, with the same syntax as the shell variable :envvar:`PATH`). -* the installation-dependent default. +* The installation-dependent default. + +.. note:: + On file systems which support symlinks, the directory containing the input + script is calculated after the symlink is followed. In other words the + directory containing the symlink is **not** added to the module search path. After initialization, Python programs can modify :data:`sys.path`. The directory containing the script being run is placed at the beginning of the diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -163,6 +163,13 @@ - Issue #20465: Update SQLite shipped with OS X installer to 3.8.3. +Documentation +------------- + +- Issue #6386: Clarify in the tutorial that specifying a symlink to execute + means the directory containing the executed script and not the symlink is + added to sys.path. + What's New in Python 3.4.0 Beta 3? ================================== -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 15:50:07 2014 From: python-checkins at python.org (brett.cannon) Date: Thu, 6 Feb 2014 15:50:07 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNDg4?= =?utf-8?q?=3A_Update_docs_to_say_importlib_is_*the*_implementaiton_of?= Message-ID: <3fKjH30rG5z7Lk2@mail.python.org> http://hg.python.org/cpython/rev/cc08bf665dea changeset: 88993:cc08bf665dea branch: 3.3 parent: 88989:82d374a9bbc7 user: Brett Cannon date: Thu Feb 06 09:46:08 2014 -0500 summary: Issue #20488: Update docs to say importlib is *the* implementaiton of import and not *an* implementation. files: Doc/library/importlib.rst | 14 ++++++++------ Misc/NEWS | 3 +++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1,8 +1,8 @@ -:mod:`importlib` -- An implementation of :keyword:`import` -========================================================== +:mod:`importlib` -- The implementation of :keyword:`import` +=========================================================== .. module:: importlib - :synopsis: An implementation of the import machinery. + :synopsis: The implementation of the import machinery. .. moduleauthor:: Brett Cannon .. sectionauthor:: Brett Cannon @@ -13,17 +13,16 @@ Introduction ------------ -The purpose of the :mod:`importlib` package is two-fold. One is to provide an +The purpose of the :mod:`importlib` package is two-fold. One is to provide the implementation of the :keyword:`import` statement (and thus, by extension, the :func:`__import__` function) in Python source code. This provides an implementation of :keyword:`import` which is portable to any Python -interpreter. This also provides a reference implementation which is easier to +interpreter. This also provides an implementation which is easier to comprehend than one implemented in a programming language other than Python. Two, the components to implement :keyword:`import` are exposed in this package, making it easier for users to create their own custom objects (known generically as an :term:`importer`) to participate in the import process. -Details on custom importers can be found in :pep:`302`. .. seealso:: @@ -53,6 +52,9 @@ :pep:`366` Main module explicit relative imports + :pep:`451` + A ModuleSpec Type for the Import System + :pep:`3120` Using UTF-8 as the Default Source Encoding diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -379,6 +379,9 @@ Documentation ------------- +- Issue #20488: Importlib is no longer *an* implementation of import, it's *the* + implementation. + - Issue #20265: Updated some parts of the Using Windows document. - Issue #20266: Updated some parts of the Windows FAQ. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 15:50:08 2014 From: python-checkins at python.org (brett.cannon) Date: Thu, 6 Feb 2014 15:50:08 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge_for_issue_=2320488?= Message-ID: <3fKjH42y2wz7LjW@mail.python.org> http://hg.python.org/cpython/rev/5641c0b50072 changeset: 88994:5641c0b50072 parent: 88992:47c31e7d3779 parent: 88993:cc08bf665dea user: Brett Cannon date: Thu Feb 06 09:49:53 2014 -0500 summary: Merge for issue #20488 files: Doc/library/importlib.rst | 14 ++++++++------ Misc/NEWS | 3 +++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1,8 +1,8 @@ -:mod:`importlib` -- An implementation of :keyword:`import` -========================================================== +:mod:`importlib` -- The implementation of :keyword:`import` +=========================================================== .. module:: importlib - :synopsis: An implementation of the import machinery. + :synopsis: The implementation of the import machinery. .. moduleauthor:: Brett Cannon .. sectionauthor:: Brett Cannon @@ -13,17 +13,16 @@ Introduction ------------ -The purpose of the :mod:`importlib` package is two-fold. One is to provide an +The purpose of the :mod:`importlib` package is two-fold. One is to provide the implementation of the :keyword:`import` statement (and thus, by extension, the :func:`__import__` function) in Python source code. This provides an implementation of :keyword:`import` which is portable to any Python -interpreter. This also provides a reference implementation which is easier to +interpreter. This also provides an implementation which is easier to comprehend than one implemented in a programming language other than Python. Two, the components to implement :keyword:`import` are exposed in this package, making it easier for users to create their own custom objects (known generically as an :term:`importer`) to participate in the import process. -Details on custom importers can be found in :pep:`302`. .. seealso:: @@ -53,6 +52,9 @@ :pep:`366` Main module explicit relative imports + :pep:`451` + A ModuleSpec Type for the Import System + :pep:`3120` Using UTF-8 as the Default Source Encoding diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -166,6 +166,9 @@ Documentation ------------- +- Issue #20488: Change wording to say importlib is *the* implementation of + import instead of just *an* implementation. + - Issue #6386: Clarify in the tutorial that specifying a symlink to execute means the directory containing the executed script and not the symlink is added to sys.path. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 17:13:13 2014 From: python-checkins at python.org (ethan.furman) Date: Thu, 6 Feb 2014 17:13:13 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_issue20412=3A_Update?= =?utf-8?q?d_Enum_docs_to_have_referencable_Enum_and_IntEnum?= Message-ID: <3fKl6x1ypqz7LjW@mail.python.org> http://hg.python.org/cpython/rev/eeb582c0c014 changeset: 88995:eeb582c0c014 user: Ethan Furman date: Thu Feb 06 08:13:14 2014 -0800 summary: Close issue20412: Updated Enum docs to have referencable Enum and IntEnum classes files: Doc/library/enum.rst | 27 ++++++++++++++++++++++----- 1 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -15,14 +15,31 @@ ---------------- -An enumeration is a set of symbolic names (members) bound to unique, constant -values. Within an enumeration, the members can be compared by identity, and -the enumeration itself can be iterated over. +An enumeration is a set of symbolic names (members) bound to unique, +constant values. Within an enumeration, the members can be compared +by identity, and the enumeration itself can be iterated over. + + +Module Contents +--------------- This module defines two enumeration classes that can be used to define unique sets of names and values: :class:`Enum` and :class:`IntEnum`. It also defines -one decorator, :func:`unique`, that ensures only unique member values are -present in an enumeration. +one decorator, :func:`unique`. + +.. class:: Enum + + Base class for creating enumerated constants. See section + :ref:`Functional API` for an alternate construction syntax. + +.. class:: IntEnum + + Base class for creating enumerated constants that are also + subclasses of :class:`int`. + +.. function:: unique + + Enum class decorator that ensures only one name is bound to any one value. Creating an Enum -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 18:04:02 2014 From: python-checkins at python.org (yury.selivanov) Date: Thu, 6 Feb 2014 18:04:02 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=2Etasks=2Egather?= =?utf-8?q?=3A_Fix_docstring?= Message-ID: <3fKmFZ2xLhz7LjM@mail.python.org> http://hg.python.org/cpython/rev/a46862fc4d18 changeset: 88996:a46862fc4d18 user: Yury Selivanov date: Thu Feb 06 12:03:53 2014 -0500 summary: asyncio.tasks.gather: Fix docstring files: Lib/asyncio/tasks.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -555,7 +555,7 @@ All futures must share the same event loop. If all the tasks are done successfully, the returned future's result is the list of results (in the order of the original sequence, not necessarily - the order of results arrival). If *result_exception* is True, + the order of results arrival). If *return_exceptions* is True, exceptions in the tasks are treated the same as successful results, and gathered in the result list; otherwise, the first raised exception will be immediately propagated to the returned -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 20:13:55 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 20:13:55 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Fix_empty_stri?= =?utf-8?q?ngs_to_empty_bytes_objects=2E?= Message-ID: <3fKq7R3rgnz7Lk2@mail.python.org> http://hg.python.org/cpython/rev/733e19123915 changeset: 88997:733e19123915 branch: 3.3 parent: 88993:cc08bf665dea user: Serhiy Storchaka date: Thu Feb 06 21:10:41 2014 +0200 summary: Fix empty strings to empty bytes objects. files: Doc/library/asynchat.rst | 6 +++--- Doc/library/asyncore.rst | 3 ++- Doc/library/chunk.rst | 8 ++++---- Doc/library/xml.etree.elementtree.rst | 2 +- Doc/library/zlib.rst | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Doc/library/asynchat.rst b/Doc/library/asynchat.rst --- a/Doc/library/asynchat.rst +++ b/Doc/library/asynchat.rst @@ -56,8 +56,8 @@ have only one method, :meth:`more`, which should return data to be transmitted on the channel. The producer indicates exhaustion (*i.e.* that it contains no more data) by - having its :meth:`more` method return the empty string. At this point the - :class:`async_chat` object removes the producer from the fifo and starts + having its :meth:`more` method return the empty bytes object. At this point + the :class:`async_chat` object removes the producer from the fifo and starts using the next producer, if any. When the producer fifo is empty the :meth:`handle_write` method does nothing. You use the channel object's :meth:`set_terminator` method to describe how to recognize the end of, or @@ -221,7 +221,7 @@ def found_terminator(self): if self.reading_headers: self.reading_headers = False - self.parse_headers("".join(self.ibuffer)) + self.parse_headers(b"".join(self.ibuffer)) self.ibuffer = [] if self.op.upper() == b"POST": clen = self.headers.getheader("content-length") diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst --- a/Doc/library/asyncore.rst +++ b/Doc/library/asyncore.rst @@ -208,7 +208,8 @@ .. method:: recv(buffer_size) Read at most *buffer_size* bytes from the socket's remote end-point. An - empty string implies that the channel has been closed from the other end. + empty bytes object implies that the channel has been closed from the + other end. .. method:: listen(backlog) diff --git a/Doc/library/chunk.rst b/Doc/library/chunk.rst --- a/Doc/library/chunk.rst +++ b/Doc/library/chunk.rst @@ -113,15 +113,15 @@ Read at most *size* bytes from the chunk (less if the read hits the end of the chunk before obtaining *size* bytes). If the *size* argument is - negative or omitted, read all data until the end of the chunk. The bytes - are returned as a string object. An empty string is returned when the end - of the chunk is encountered immediately. + negative or omitted, read all data until the end of the chunk. An empty + bytes object is returned when the end of the chunk is encountered + immediately. .. method:: skip() Skip to the end of the chunk. All further calls to :meth:`read` for the - chunk will return ``''``. If you are not interested in the contents of + chunk will return ``b''``. If you are not interested in the contents of the chunk, this method should be called so that the file points to the start of the next chunk. diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -457,7 +457,7 @@ is either ``"xml"``, ``"html"`` or ``"text"`` (default is ``"xml"``). Returns a list of (optionally) encoded strings containing the XML data. It does not guarantee any specific sequence, except that - ``"".join(tostringlist(element)) == tostring(element)``. + ``b"".join(tostringlist(element)) == tostring(element)``. .. versionadded:: 3.2 diff --git a/Doc/library/zlib.rst b/Doc/library/zlib.rst --- a/Doc/library/zlib.rst +++ b/Doc/library/zlib.rst @@ -197,7 +197,7 @@ .. attribute:: Decompress.unused_data A bytes object which contains any bytes past the end of the compressed data. That is, - this remains ``""`` until the last byte that contains compression data is + this remains ``b""`` until the last byte that contains compression data is available. If the whole bytestring turned out to contain compressed data, this is ``b""``, an empty bytes object. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 20:13:56 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 20:13:56 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Fix_empty_strings_to_empty_bytes_objects=2E?= Message-ID: <3fKq7S6jHjz7LlL@mail.python.org> http://hg.python.org/cpython/rev/50d18b5ac776 changeset: 88998:50d18b5ac776 parent: 88996:a46862fc4d18 parent: 88997:733e19123915 user: Serhiy Storchaka date: Thu Feb 06 21:11:33 2014 +0200 summary: Fix empty strings to empty bytes objects. files: Doc/library/asynchat.rst | 6 +++--- Doc/library/asyncore.rst | 3 ++- Doc/library/chunk.rst | 8 ++++---- Doc/library/xml.etree.elementtree.rst | 2 +- Doc/library/zlib.rst | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Doc/library/asynchat.rst b/Doc/library/asynchat.rst --- a/Doc/library/asynchat.rst +++ b/Doc/library/asynchat.rst @@ -61,8 +61,8 @@ have only one method, :meth:`more`, which should return data to be transmitted on the channel. The producer indicates exhaustion (*i.e.* that it contains no more data) by - having its :meth:`more` method return the empty string. At this point the - :class:`async_chat` object removes the producer from the fifo and starts + having its :meth:`more` method return the empty bytes object. At this point + the :class:`async_chat` object removes the producer from the fifo and starts using the next producer, if any. When the producer fifo is empty the :meth:`handle_write` method does nothing. You use the channel object's :meth:`set_terminator` method to describe how to recognize the end of, or @@ -226,7 +226,7 @@ def found_terminator(self): if self.reading_headers: self.reading_headers = False - self.parse_headers("".join(self.ibuffer)) + self.parse_headers(b"".join(self.ibuffer)) self.ibuffer = [] if self.op.upper() == b"POST": clen = self.headers.getheader("content-length") diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst --- a/Doc/library/asyncore.rst +++ b/Doc/library/asyncore.rst @@ -213,7 +213,8 @@ .. method:: recv(buffer_size) Read at most *buffer_size* bytes from the socket's remote end-point. An - empty string implies that the channel has been closed from the other end. + empty bytes object implies that the channel has been closed from the + other end. .. method:: listen(backlog) diff --git a/Doc/library/chunk.rst b/Doc/library/chunk.rst --- a/Doc/library/chunk.rst +++ b/Doc/library/chunk.rst @@ -113,15 +113,15 @@ Read at most *size* bytes from the chunk (less if the read hits the end of the chunk before obtaining *size* bytes). If the *size* argument is - negative or omitted, read all data until the end of the chunk. The bytes - are returned as a string object. An empty string is returned when the end - of the chunk is encountered immediately. + negative or omitted, read all data until the end of the chunk. An empty + bytes object is returned when the end of the chunk is encountered + immediately. .. method:: skip() Skip to the end of the chunk. All further calls to :meth:`read` for the - chunk will return ``''``. If you are not interested in the contents of + chunk will return ``b''``. If you are not interested in the contents of the chunk, this method should be called so that the file points to the start of the next chunk. diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -511,7 +511,7 @@ *short_empty_elements* has the same meaning as in :meth:`ElementTree.write`. Returns a list of (optionally) encoded strings containing the XML data. It does not guarantee any specific sequence, except that - ``"".join(tostringlist(element)) == tostring(element)``. + ``b"".join(tostringlist(element)) == tostring(element)``. .. versionadded:: 3.2 diff --git a/Doc/library/zlib.rst b/Doc/library/zlib.rst --- a/Doc/library/zlib.rst +++ b/Doc/library/zlib.rst @@ -197,7 +197,7 @@ .. attribute:: Decompress.unused_data A bytes object which contains any bytes past the end of the compressed data. That is, - this remains ``""`` until the last byte that contains compression data is + this remains ``b""`` until the last byte that contains compression data is available. If the whole bytestring turned out to contain compressed data, this is ``b""``, an empty bytes object. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 21:57:15 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 21:57:15 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Catch_deprecation_warnings?= =?utf-8?q?_emitted_when_non-integers_are_formatted_with_=25c=2C_=25o?= Message-ID: <3fKsQg35HYz7Lkh@mail.python.org> http://hg.python.org/cpython/rev/775fb736b4b8 changeset: 88999:775fb736b4b8 user: Serhiy Storchaka date: Thu Feb 06 22:44:27 2014 +0200 summary: Catch deprecation warnings emitted when non-integers are formatted with %c, %o and %x (introduced in issue #19995). files: Lib/test/string_tests.py | 3 ++- Lib/test/test_format.py | 15 ++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -1179,7 +1179,8 @@ self.checkraises(TypeError, 'abc', '__mod__') self.checkraises(TypeError, '%(foo)s', '__mod__', 42) self.checkraises(TypeError, '%s%s', '__mod__', (42,)) - self.checkraises(TypeError, '%c', '__mod__', (None,)) + with self.assertWarns(DeprecationWarning): + self.checkraises(TypeError, '%c', '__mod__', (None,)) self.checkraises(ValueError, '%(foo', '__mod__', {}) self.checkraises(TypeError, '%(foo)s %(bar)s', '__mod__', ('foo', 42)) self.checkraises(TypeError, '%d', '__mod__', "42") # not numeric diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -142,7 +142,8 @@ testformat("%#+027.23X", big, "+0X0001234567890ABCDEF12345") # same, except no 0 flag testformat("%#+27.23X", big, " +0X001234567890ABCDEF12345") - testformat("%x", float(big), "123456_______________", 6) + with self.assertWarns(DeprecationWarning): + testformat("%x", float(big), "123456_______________", 6) big = 0o12345670123456701234567012345670 # 32 octal digits testformat("%o", big, "12345670123456701234567012345670") testformat("%o", -big, "-12345670123456701234567012345670") @@ -182,7 +183,8 @@ testformat("%034.33o", big, "0012345670123456701234567012345670") # base marker shouldn't change that testformat("%0#34.33o", big, "0o012345670123456701234567012345670") - testformat("%o", float(big), "123456__________________________", 6) + with self.assertWarns(DeprecationWarning): + testformat("%o", float(big), "123456__________________________", 6) # Some small ints, in both Python int and flavors). testformat("%d", 42, "42") testformat("%d", -42, "-42") @@ -193,7 +195,8 @@ testformat("%#x", 1, "0x1") testformat("%#X", 1, "0X1") testformat("%#X", 1, "0X1") - testformat("%#x", 1.0, "0x1") + with self.assertWarns(DeprecationWarning): + testformat("%#x", 1.0, "0x1") testformat("%#o", 1, "0o1") testformat("%#o", 1, "0o1") testformat("%#o", 0, "0o0") @@ -210,12 +213,14 @@ testformat("%x", -0x42, "-42") testformat("%x", 0x42, "42") testformat("%x", -0x42, "-42") - testformat("%x", float(0x42), "42") + with self.assertWarns(DeprecationWarning): + testformat("%x", float(0x42), "42") testformat("%o", 0o42, "42") testformat("%o", -0o42, "-42") testformat("%o", 0o42, "42") testformat("%o", -0o42, "-42") - testformat("%o", float(0o42), "42") + with self.assertWarns(DeprecationWarning): + testformat("%o", float(0o42), "42") testformat("%r", "\u0378", "'\\u0378'") # non printable testformat("%a", "\u0378", "'\\u0378'") # non printable testformat("%r", "\u0374", "'\u0374'") # printable -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 21:57:16 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 21:57:16 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwMzYz?= =?utf-8?q?=2E_Fixed_BytesWarning_triggerred_by_test_suite=2E?= Message-ID: <3fKsQh5vJCz7LlB@mail.python.org> http://hg.python.org/cpython/rev/791674a74e47 changeset: 89000:791674a74e47 branch: 3.3 parent: 88997:733e19123915 user: Serhiy Storchaka date: Thu Feb 06 22:49:45 2014 +0200 summary: Issue #20363. Fixed BytesWarning triggerred by test suite. Patch by Berker Peksag. files: Lib/configparser.py | 4 ++-- Lib/distutils/command/register.py | 2 +- Lib/test/test_hash.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/configparser.py b/Lib/configparser.py --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -286,7 +286,7 @@ raise ValueError("Required argument `source' not given.") elif filename: source = filename - Error.__init__(self, 'Source contains parsing errors: %s' % source) + Error.__init__(self, 'Source contains parsing errors: %r' % source) self.source = source self.errors = [] self.args = (source, ) @@ -322,7 +322,7 @@ def __init__(self, filename, lineno, line): Error.__init__( self, - 'File contains no section headers.\nfile: %s, line: %d\n%r' % + 'File contains no section headers.\nfile: %r, line: %d\n%r' % (filename, lineno, line)) self.source = filename self.lineno = lineno diff --git a/Lib/distutils/command/register.py b/Lib/distutils/command/register.py --- a/Lib/distutils/command/register.py +++ b/Lib/distutils/command/register.py @@ -300,5 +300,5 @@ result = 200, 'OK' if self.show_response: dashes = '-' * 75 - self.announce('%s%s%s' % (dashes, data, dashes)) + self.announce('%s%r%s' % (dashes, data, dashes)) return result diff --git a/Lib/test/test_hash.py b/Lib/test/test_hash.py --- a/Lib/test/test_hash.py +++ b/Lib/test/test_hash.py @@ -138,7 +138,7 @@ # an object to be tested def get_hash_command(self, repr_): - return 'print(hash(%s))' % repr_ + return 'print(hash(eval(%a)))' % repr_ def get_hash(self, repr_, seed=None): env = os.environ.copy() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 21:57:18 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 21:57:18 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320363=2E_Fixed_BytesWarning_triggerred_by_test_?= =?utf-8?q?suite=2E?= Message-ID: <3fKsQk0dxrz7Lk4@mail.python.org> http://hg.python.org/cpython/rev/a4431dce107a changeset: 89001:a4431dce107a parent: 88999:775fb736b4b8 parent: 89000:791674a74e47 user: Serhiy Storchaka date: Thu Feb 06 22:52:23 2014 +0200 summary: Issue #20363. Fixed BytesWarning triggerred by test suite. Patch by Berker Peksag. files: Lib/base64.py | 2 +- Lib/configparser.py | 4 ++-- Lib/distutils/command/register.py | 2 +- Lib/test/test_hash.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/base64.py b/Lib/base64.py --- a/Lib/base64.py +++ b/Lib/base64.py @@ -362,7 +362,7 @@ if adobe: if not (b.startswith(_A85START) and b.endswith(_A85END)): raise ValueError("Ascii85 encoded byte sequences must be bracketed " - "by {} and {}".format(_A85START, _A85END)) + "by {!r} and {!r}".format(_A85START, _A85END)) b = b[2:-2] # Strip off start/end markers # # We have to go through this stepwise, so as to ignore spaces and handle diff --git a/Lib/configparser.py b/Lib/configparser.py --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -286,7 +286,7 @@ raise ValueError("Required argument `source' not given.") elif filename: source = filename - Error.__init__(self, 'Source contains parsing errors: %s' % source) + Error.__init__(self, 'Source contains parsing errors: %r' % source) self.source = source self.errors = [] self.args = (source, ) @@ -322,7 +322,7 @@ def __init__(self, filename, lineno, line): Error.__init__( self, - 'File contains no section headers.\nfile: %s, line: %d\n%r' % + 'File contains no section headers.\nfile: %r, line: %d\n%r' % (filename, lineno, line)) self.source = filename self.lineno = lineno diff --git a/Lib/distutils/command/register.py b/Lib/distutils/command/register.py --- a/Lib/distutils/command/register.py +++ b/Lib/distutils/command/register.py @@ -300,5 +300,5 @@ result = 200, 'OK' if self.show_response: dashes = '-' * 75 - self.announce('%s%s%s' % (dashes, data, dashes)) + self.announce('%s%r%s' % (dashes, data, dashes)) return result diff --git a/Lib/test/test_hash.py b/Lib/test/test_hash.py --- a/Lib/test/test_hash.py +++ b/Lib/test/test_hash.py @@ -172,7 +172,7 @@ # an object to be tested def get_hash_command(self, repr_): - return 'print(hash(eval(%r.decode("utf-8"))))' % repr_.encode("utf-8") + return 'print(hash(eval(%a)))' % repr_ def get_hash(self, repr_, seed=None): env = os.environ.copy() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 22:47:09 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 6 Feb 2014 22:47:09 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=233158=3A_Provide_a?= =?utf-8?q?_couple_of_fallbacks_for_in_case_a_method=5Fdescriptor?= Message-ID: <3fKtXF0qvvzMrl@mail.python.org> http://hg.python.org/cpython/rev/c964b6b83720 changeset: 89002:c964b6b83720 user: Zachary Ware date: Thu Feb 06 15:46:38 2014 -0600 summary: Issue #3158: Provide a couple of fallbacks for in case a method_descriptor doesn't have __objclass__. files: Lib/doctest.py | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-) diff --git a/Lib/doctest.py b/Lib/doctest.py --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -945,7 +945,13 @@ elif inspect.isfunction(object): return module.__dict__ is object.__globals__ elif inspect.ismethoddescriptor(object): - return module.__name__ == object.__objclass__.__module__ + if hasattr(object, '__objclass__'): + obj_mod = object.__objclass__.__module__ + elif hasattr(object, '__module__'): + obj_mod = object.__module__ + else: + return True # [XX] no easy way to tell otherwise + return module.__name__ == obj_mod elif inspect.isclass(object): return module.__name__ == object.__module__ elif hasattr(object, '__module__'): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 23:29:06 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 23:29:06 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Silence_BytesW?= =?utf-8?q?arning_=28backport_267a4d4d9d65=29=2E?= Message-ID: <3fKvSf5kCWz7Lkk@mail.python.org> http://hg.python.org/cpython/rev/98d9d152e94b changeset: 89003:98d9d152e94b branch: 3.3 parent: 89000:791674a74e47 user: Serhiy Storchaka date: Fri Feb 07 00:26:57 2014 +0200 summary: Silence BytesWarning (backport 267a4d4d9d65). files: Lib/test/test_exceptions.py | 11 ++++++----- 1 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -8,8 +8,8 @@ import errno from test.support import (TESTFN, captured_output, check_impl_detail, - cpython_only, gc_collect, run_unittest, no_tracing, - unlink) + check_warnings, cpython_only, gc_collect, + no_tracing, run_unittest, unlink) class NaiveException(Exception): def __init__(self, x): @@ -960,9 +960,10 @@ def test_non_str_argument(self): # Issue #15778 - arg = b'abc' - exc = ImportError(arg) - self.assertEqual(str(arg), str(exc)) + with check_warnings(('', BytesWarning), quiet=True): + arg = b'abc' + exc = ImportError(arg) + self.assertEqual(str(arg), str(exc)) def test_main(): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Feb 6 23:29:08 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 6 Feb 2014 23:29:08 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Null_merge?= Message-ID: <3fKvSh0211z7Lkx@mail.python.org> http://hg.python.org/cpython/rev/0f313b285970 changeset: 89004:0f313b285970 parent: 89002:c964b6b83720 parent: 89003:98d9d152e94b user: Serhiy Storchaka date: Fri Feb 07 00:29:03 2014 +0200 summary: Null merge files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 02:28:47 2014 From: python-checkins at python.org (ethan.furman) Date: Fri, 7 Feb 2014 02:28:47 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_issue20534=3A_test?= =?utf-8?q?=5Fenum_now_tests_all_supported_pickle_protocols_=282_-?= Message-ID: <3fKzRz631sz7LjM@mail.python.org> http://hg.python.org/cpython/rev/35f57ab9389b changeset: 89005:35f57ab9389b user: Ethan Furman date: Thu Feb 06 17:28:50 2014 -0800 summary: Close issue20534: test_enum now tests all supported pickle protocols (2 - HIGHEST_PROTOCOL, inclusive). files: Lib/test/test_enum.py | 60 ++++++++++++++++++------------ 1 files changed, 36 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -5,7 +5,7 @@ from collections import OrderedDict from enum import Enum, IntEnum, EnumMeta, unique from io import StringIO -from pickle import dumps, loads, PicklingError +from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL # for pickle tests try: @@ -61,6 +61,16 @@ except Exception: pass +def test_pickle_dump_load(assertion, source, target=None): + if target is None: + target = source + for protocol in range(2, HIGHEST_PROTOCOL+1): + assertion(loads(dumps(source, protocol=protocol)), target) + +def test_pickle_exception(assertion, exception, obj): + for protocol in range(2, HIGHEST_PROTOCOL+1): + with assertion(exception): + dumps(obj, protocol=protocol) class TestHelpers(unittest.TestCase): # _is_descriptor, _is_sunder, _is_dunder @@ -503,41 +513,40 @@ def test_pickle_enum(self): if isinstance(Stooges, Exception): raise Stooges - self.assertIs(Stooges.CURLY, loads(dumps(Stooges.CURLY))) - self.assertIs(Stooges, loads(dumps(Stooges))) + test_pickle_dump_load(self.assertIs, Stooges.CURLY) + test_pickle_dump_load(self.assertIs, Stooges) def test_pickle_int(self): if isinstance(IntStooges, Exception): raise IntStooges - self.assertIs(IntStooges.CURLY, loads(dumps(IntStooges.CURLY))) - self.assertIs(IntStooges, loads(dumps(IntStooges))) + test_pickle_dump_load(self.assertIs, IntStooges.CURLY) + test_pickle_dump_load(self.assertIs, IntStooges) def test_pickle_float(self): if isinstance(FloatStooges, Exception): raise FloatStooges - self.assertIs(FloatStooges.CURLY, loads(dumps(FloatStooges.CURLY))) - self.assertIs(FloatStooges, loads(dumps(FloatStooges))) + test_pickle_dump_load(self.assertIs, FloatStooges.CURLY) + test_pickle_dump_load(self.assertIs, FloatStooges) def test_pickle_enum_function(self): if isinstance(Answer, Exception): raise Answer - self.assertIs(Answer.him, loads(dumps(Answer.him))) - self.assertIs(Answer, loads(dumps(Answer))) + test_pickle_dump_load(self.assertIs, Answer.him) + test_pickle_dump_load(self.assertIs, Answer) def test_pickle_enum_function_with_module(self): if isinstance(Question, Exception): raise Question - self.assertIs(Question.who, loads(dumps(Question.who))) - self.assertIs(Question, loads(dumps(Question))) + test_pickle_dump_load(self.assertIs, Question.who) + test_pickle_dump_load(self.assertIs, Question) def test_exploding_pickle(self): BadPickle = Enum('BadPickle', 'dill sweet bread-n-butter') - enum._make_class_unpicklable(BadPickle) + BadPickle.__qualname__ = 'BadPickle' # needed for pickle protocol 4 globals()['BadPickle'] = BadPickle - with self.assertRaises(TypeError): - dumps(BadPickle.dill) - with self.assertRaises(PicklingError): - dumps(BadPickle) + enum._make_class_unpicklable(BadPickle) # will overwrite __qualname__ + test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill) + test_pickle_exception(self.assertRaises, PicklingError, BadPickle) def test_string_enum(self): class SkillLevel(str, Enum): @@ -690,7 +699,7 @@ self.assertEqual(Name.BDFL, 'Guido van Rossum') self.assertTrue(Name.BDFL, Name('Guido van Rossum')) self.assertIs(Name.BDFL, getattr(Name, 'BDFL')) - self.assertIs(Name.BDFL, loads(dumps(Name.BDFL))) + test_pickle_dump_load(self.assertIs, Name.BDFL) def test_extending(self): class Color(Enum): @@ -864,6 +873,7 @@ def test_subclasses_with_getnewargs(self): class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 def __new__(cls, *args): _args = args name, *args = args @@ -902,6 +912,7 @@ return temp class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 x = ('the-x', 1) y = ('the-y', 2) @@ -912,12 +923,13 @@ globals()['NEI'] = NEI NI5 = NamedInt('test', 5) self.assertEqual(NI5, 5) - self.assertEqual(loads(dumps(NI5)), 5) + test_pickle_dump_load(self.assertEqual, NI5, 5) self.assertEqual(NEI.y.value, 2) - self.assertIs(loads(dumps(NEI.y)), NEI.y) + test_pickle_dump_load(self.assertIs, NEI.y) def test_subclasses_without_getnewargs(self): class NamedInt(int): + __qualname__ = 'NamedInt' def __new__(cls, *args): _args = args name, *args = args @@ -954,6 +966,7 @@ return temp class NEI(NamedInt, Enum): + __qualname__ = 'NEI' x = ('the-x', 1) y = ('the-y', 2) @@ -964,13 +977,12 @@ NI5 = NamedInt('test', 5) self.assertEqual(NI5, 5) self.assertEqual(NEI.y.value, 2) - with self.assertRaises(TypeError): - dumps(NEI.x) - with self.assertRaises(PicklingError): - dumps(NEI) + test_pickle_exception(self.assertRaises, TypeError, NEI.x) + test_pickle_exception(self.assertRaises, PicklingError, NEI) def test_tuple_subclass(self): class SomeTuple(tuple, Enum): + __qualname__ = 'SomeTuple' # needed for pickle protocol 4 first = (1, 'for the money') second = (2, 'for the show') third = (3, 'for the music') @@ -978,7 +990,7 @@ self.assertIsInstance(SomeTuple.second, tuple) self.assertEqual(SomeTuple.third, (3, 'for the music')) globals()['SomeTuple'] = SomeTuple - self.assertIs(loads(dumps(SomeTuple.first)), SomeTuple.first) + test_pickle_dump_load(self.assertIs, SomeTuple.first) def test_duplicate_values_give_unique_enum_items(self): class AutoNumber(Enum): -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Fri Feb 7 04:03:33 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 07 Feb 2014 04:03:33 +0100 Subject: [Python-checkins] Daily reference leaks (35f57ab9389b): sum=4 Message-ID: results for 35f57ab9389b on branch "default" -------------------------------------------- test_asyncio leaked [0, 0, 4] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogCDGlEC', '-x'] From python-checkins at python.org Fri Feb 7 04:07:30 2014 From: python-checkins at python.org (yury.selivanov) Date: Fri, 7 Feb 2014 04:07:30 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=2Etasks=3A_Fix_as?= =?utf-8?q?=5Fcompleted=2C_gather_=26_wait_to_work_with_duplicate_coroutin?= =?utf-8?q?es?= Message-ID: <3fL1dt6YYlzSsF@mail.python.org> http://hg.python.org/cpython/rev/844879389a17 changeset: 89006:844879389a17 user: Yury Selivanov date: Thu Feb 06 22:06:16 2014 -0500 summary: asyncio.tasks: Fix as_completed, gather & wait to work with duplicate coroutines files: Lib/asyncio/tasks.py | 7 +- Lib/test/test_asyncio/test_tasks.py | 55 ++++++++++++++-- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -364,7 +364,7 @@ if loop is None: loop = events.get_event_loop() - fs = set(async(f, loop=loop) for f in fs) + fs = {async(f, loop=loop) for f in set(fs)} if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED): raise ValueError('Invalid return_when value: {}'.format(return_when)) @@ -476,7 +476,7 @@ """ loop = loop if loop is not None else events.get_event_loop() deadline = None if timeout is None else loop.time() + timeout - todo = set(async(f, loop=loop) for f in fs) + todo = {async(f, loop=loop) for f in set(fs)} completed = collections.deque() @coroutine @@ -568,7 +568,8 @@ prevent the cancellation of one child to cause other children to be cancelled.) """ - children = [async(fut, loop=loop) for fut in coros_or_futures] + arg_to_fut = {arg: async(arg, loop=loop) for arg in set(coros_or_futures)} + children = [arg_to_fut[arg] for arg in coros_or_futures] n = len(children) if n == 0: outer = futures.Future(loop=loop) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -483,6 +483,21 @@ self.assertEqual(res, 42) + def test_wait_duplicate_coroutines(self): + @asyncio.coroutine + def coro(s): + return s + c = coro('test') + + task = asyncio.Task( + asyncio.wait([c, c, coro('spam')], loop=self.loop), + loop=self.loop) + + done, pending = self.loop.run_until_complete(task) + + self.assertFalse(pending) + self.assertEqual(set(f.result() for f in done), {'test', 'spam'}) + def test_wait_errors(self): self.assertRaises( ValueError, self.loop.run_until_complete, @@ -757,14 +772,10 @@ def test_as_completed_with_timeout(self): def gen(): - when = yield - self.assertAlmostEqual(0.12, when) - when = yield 0 - self.assertAlmostEqual(0.1, when) - when = yield 0 - self.assertAlmostEqual(0.15, when) - when = yield 0.1 - self.assertAlmostEqual(0.12, when) + yield + yield 0 + yield 0 + yield 0.1 yield 0.02 loop = test_utils.TestLoop(gen) @@ -840,6 +851,25 @@ done, pending = loop.run_until_complete(waiter) self.assertEqual(set(f.result() for f in done), {'a', 'b'}) + def test_as_completed_duplicate_coroutines(self): + @asyncio.coroutine + def coro(s): + return s + + @asyncio.coroutine + def runner(): + result = [] + c = coro('ham') + for f in asyncio.as_completed({c, c, coro('spam')}, loop=self.loop): + result.append((yield from f)) + return result + + fut = asyncio.Task(runner(), loop=self.loop) + self.loop.run_until_complete(fut) + result = fut.result() + self.assertEqual(set(result), {'ham', 'spam'}) + self.assertEqual(len(result), 2) + def test_sleep(self): def gen(): @@ -1505,6 +1535,15 @@ gen3.close() gen4.close() + def test_duplicate_coroutines(self): + @asyncio.coroutine + def coro(s): + return s + c = coro('abc') + fut = asyncio.gather(c, c, coro('def'), c, loop=self.one_loop) + self._run_loop(self.one_loop) + self.assertEqual(fut.result(), ['abc', 'abc', 'def', 'abc']) + def test_cancellation_broadcast(self): # Cancelling outer() cancels all children. proof = 0 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 09:11:06 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 7 Feb 2014 09:11:06 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNTMy?= =?utf-8?q?=3A_Tests_which_use_=5Ftestcapi_now_are_marked_as_CPython_only?= =?utf-8?q?=2E?= Message-ID: <3fL8NB5HJjz7LjP@mail.python.org> http://hg.python.org/cpython/rev/75c75d90f3a4 changeset: 89007:75c75d90f3a4 branch: 2.7 parent: 88988:305b6a5da852 user: Serhiy Storchaka date: Fri Feb 07 10:06:05 2014 +0200 summary: Issue #20532: Tests which use _testcapi now are marked as CPython only. files: Lib/test/string_tests.py | 33 ++++--- Lib/test/test_capi.py | 4 +- Lib/test/test_code.py | 5 +- Lib/test/test_codecs.py | 88 ++++++++++++++-------- Lib/test/test_descr.py | 1 + Lib/test/test_fcntl.py | 46 ++++++---- Lib/test/test_fileio.py | 7 +- Lib/test/test_format.py | 22 ++++- Lib/test/test_getargs2.py | 2 + Lib/test/test_poll.py | 15 +++- Lib/test/test_socket.py | 29 ++++++- Lib/test/test_structmembers.py | 8 +- Lib/test/test_support.py | 2 +- Lib/test/test_sys.py | 1 + Lib/test/test_tcl.py | 13 ++- Lib/test/test_threading.py | 3 +- Lib/test/test_traceback.py | 5 +- Lib/test/test_ucn.py | 20 ++-- Lib/test/test_unicode.py | 9 ++- Misc/NEWS | 2 + 20 files changed, 208 insertions(+), 107 deletions(-) diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -5,7 +5,6 @@ import unittest, string, sys, struct from test import test_support from UserList import UserList -import _testcapi class Sequence: def __init__(self, seq='wxyz'): self.seq = seq @@ -1114,20 +1113,6 @@ self.checkraises(TypeError, '%10.*f', '__mod__', ('foo', 42.)) self.checkraises(ValueError, '%10', '__mod__', (42,)) - width = int(_testcapi.PY_SSIZE_T_MAX + 1) - if width <= sys.maxint: - self.checkraises(OverflowError, '%*s', '__mod__', (width, '')) - prec = int(_testcapi.INT_MAX + 1) - if prec <= sys.maxint: - self.checkraises(OverflowError, '%.*f', '__mod__', (prec, 1. / 7)) - # Issue 15989 - width = int(1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1)) - if width <= sys.maxint: - self.checkraises(OverflowError, '%*s', '__mod__', (width, '')) - prec = int(_testcapi.UINT_MAX + 1) - if prec <= sys.maxint: - self.checkraises(OverflowError, '%.*f', '__mod__', (prec, 1. / 7)) - class X(object): pass self.checkraises(TypeError, 'abc', '__mod__', X()) class X(Exception): @@ -1135,6 +1120,24 @@ return k self.checkequal('melon apple', '%(melon)s %(apple)s', '__mod__', X()) + @test_support.cpython_only + def test_formatting_c_limits(self): + from _testcapi import PY_SSIZE_T_MAX, INT_MAX, UINT_MAX + SIZE_MAX = (1 << (PY_SSIZE_T_MAX.bit_length() + 1)) - 1 + width = int(PY_SSIZE_T_MAX + 1) + if width <= sys.maxint: + self.checkraises(OverflowError, '%*s', '__mod__', (width, '')) + prec = int(INT_MAX + 1) + if prec <= sys.maxint: + self.checkraises(OverflowError, '%.*f', '__mod__', (prec, 1. / 7)) + # Issue 15989 + width = int(SIZE_MAX + 1) + if width <= sys.maxint: + self.checkraises(OverflowError, '%*s', '__mod__', (width, '')) + prec = int(UINT_MAX + 1) + if prec <= sys.maxint: + self.checkraises(OverflowError, '%.*f', '__mod__', (prec, 1. / 7)) + def test_floatformatting(self): # float formatting for prec in xrange(100): diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -13,7 +13,9 @@ except ImportError: thread = None threading = None -import _testcapi +# Skip this test if the _testcapi module isn't available. +_testcapi = test_support.import_module('_testcapi') + @unittest.skipUnless(threading, 'Threading required for this test.') class TestPendingCalls(unittest.TestCase): diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -82,7 +82,7 @@ import unittest import weakref -import _testcapi +from test.test_support import run_doctest, run_unittest, cpython_only def consts(t): @@ -104,7 +104,9 @@ class CodeTest(unittest.TestCase): + @cpython_only def test_newempty(self): + import _testcapi co = _testcapi.code_newempty("filename", "funcname", 15) self.assertEqual(co.co_filename, "filename") self.assertEqual(co.co_name, "funcname") @@ -137,7 +139,6 @@ def test_main(verbose=None): - from test.test_support import run_doctest, run_unittest from test import test_code run_doctest(test_code, verbose) run_unittest(CodeTest, CodeWeakRefTest) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -2,7 +2,7 @@ import unittest import codecs import locale -import sys, StringIO, _testcapi +import sys, StringIO def coding_checker(self, coder): def check(input, expect): @@ -1539,7 +1539,7 @@ class BasicUnicodeTest(unittest.TestCase): def test_basics(self): - s = u"abc123" # all codecs should be able to encode these + s = u"abc123" # all codecs should be able to encode these for encoding in all_unicode_encodings: name = codecs.lookup(encoding).name if encoding.endswith("_codec"): @@ -1548,9 +1548,9 @@ name = "latin_1" self.assertEqual(encoding.replace("_", "-"), name.replace("_", "-")) (bytes, size) = codecs.getencoder(encoding)(s) - self.assertEqual(size, len(s), "%r != %r (encoding=%r)" % (size, len(s), encoding)) + self.assertEqual(size, len(s), "encoding=%r" % encoding) (chars, size) = codecs.getdecoder(encoding)(bytes) - self.assertEqual(chars, s, "%r != %r (encoding=%r)" % (chars, s, encoding)) + self.assertEqual(chars, s, "encoding=%r" % encoding) if encoding not in broken_unicode_with_streams: # check stream reader/writer @@ -1566,15 +1566,13 @@ for c in encodedresult: q.write(c) decodedresult += reader.read() - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + self.assertEqual(decodedresult, s, "encoding=%r" % encoding) if encoding not in broken_incremental_coders: - # check incremental decoder/encoder (fetched via the Python - # and C API) and iterencode()/iterdecode() + # check incremental decoder/encoder and iterencode()/iterdecode() try: encoder = codecs.getincrementalencoder(encoding)() - cencoder = _testcapi.codec_incrementalencoder(encoding) - except LookupError: # no IncrementalEncoder + except LookupError: # no IncrementalEncoder pass else: # check incremental decoder/encoder @@ -1587,45 +1585,71 @@ for c in encodedresult: decodedresult += decoder.decode(c) decodedresult += decoder.decode("", True) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) - - # check C API - encodedresult = "" - for c in s: - encodedresult += cencoder.encode(c) - encodedresult += cencoder.encode(u"", True) - cdecoder = _testcapi.codec_incrementaldecoder(encoding) - decodedresult = u"" - for c in encodedresult: - decodedresult += cdecoder.decode(c) - decodedresult += cdecoder.decode("", True) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) # check iterencode()/iterdecode() - result = u"".join(codecs.iterdecode(codecs.iterencode(s, encoding), encoding)) - self.assertEqual(result, s, "%r != %r (encoding=%r)" % (result, s, encoding)) + result = u"".join(codecs.iterdecode( + codecs.iterencode(s, encoding), encoding)) + self.assertEqual(result, s, "encoding=%r" % encoding) # check iterencode()/iterdecode() with empty string - result = u"".join(codecs.iterdecode(codecs.iterencode(u"", encoding), encoding)) + result = u"".join(codecs.iterdecode( + codecs.iterencode(u"", encoding), encoding)) self.assertEqual(result, u"") if encoding not in only_strict_mode: # check incremental decoder/encoder with errors argument try: encoder = codecs.getincrementalencoder(encoding)("ignore") - cencoder = _testcapi.codec_incrementalencoder(encoding, "ignore") - except LookupError: # no IncrementalEncoder + except LookupError: # no IncrementalEncoder pass else: encodedresult = "".join(encoder.encode(c) for c in s) decoder = codecs.getincrementaldecoder(encoding)("ignore") - decodedresult = u"".join(decoder.decode(c) for c in encodedresult) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + decodedresult = u"".join(decoder.decode(c) + for c in encodedresult) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) + @test_support.cpython_only + def test_basics_capi(self): + from _testcapi import codec_incrementalencoder, codec_incrementaldecoder + s = u"abc123" # all codecs should be able to encode these + for encoding in all_unicode_encodings: + if encoding not in broken_incremental_coders: + # check incremental decoder/encoder and iterencode()/iterdecode() + try: + cencoder = codec_incrementalencoder(encoding) + except LookupError: # no IncrementalEncoder + pass + else: + # check C API + encodedresult = "" + for c in s: + encodedresult += cencoder.encode(c) + encodedresult += cencoder.encode(u"", True) + cdecoder = codec_incrementaldecoder(encoding) + decodedresult = u"" + for c in encodedresult: + decodedresult += cdecoder.decode(c) + decodedresult += cdecoder.decode("", True) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) + + if encoding not in only_strict_mode: + # check incremental decoder/encoder with errors argument + try: + cencoder = codec_incrementalencoder(encoding, "ignore") + except LookupError: # no IncrementalEncoder + pass + else: encodedresult = "".join(cencoder.encode(c) for c in s) - cdecoder = _testcapi.codec_incrementaldecoder(encoding, "ignore") - decodedresult = u"".join(cdecoder.decode(c) for c in encodedresult) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + cdecoder = codec_incrementaldecoder(encoding, "ignore") + decodedresult = u"".join(cdecoder.decode(c) + for c in encodedresult) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) def test_seek(self): # all codecs should be able to encode these diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2092,6 +2092,7 @@ prop2 = property(fset=setter) self.assertEqual(prop2.__doc__, None) + @test_support.cpython_only def test_testcapi_no_segfault(self): # this segfaulted in 2.5b2 try: diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -6,10 +6,9 @@ import os import struct import sys -import _testcapi import unittest from test.test_support import (verbose, TESTFN, unlink, run_unittest, - import_module) + import_module, cpython_only) # Skip test if no fcntl module. fcntl = import_module('fcntl') @@ -52,6 +51,12 @@ lockdata = get_lockdata() +class BadFile: + def __init__(self, fn): + self.fn = fn + def fileno(self): + return self.fn + class TestFcntl(unittest.TestCase): def setUp(self): @@ -83,24 +88,27 @@ self.f.close() def test_fcntl_bad_file(self): - class F: - def __init__(self, fn): - self.fn = fn - def fileno(self): - return self.fn - self.assertRaises(ValueError, fcntl.fcntl, -1, fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(ValueError, fcntl.fcntl, F(-1), fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(TypeError, fcntl.fcntl, 'spam', fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(TypeError, fcntl.fcntl, F('spam'), fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(-1, fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(BadFile(-1), fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(TypeError): + fcntl.fcntl('spam', fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(TypeError): + fcntl.fcntl(BadFile('spam'), fcntl.F_SETFL, os.O_NONBLOCK) + + @cpython_only + def test_fcntl_bad_file_overflow(self): + from _testcapi import INT_MAX, INT_MIN # Issue 15989 - self.assertRaises(ValueError, fcntl.fcntl, _testcapi.INT_MAX + 1, - fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(ValueError, fcntl.fcntl, F(_testcapi.INT_MAX + 1), - fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(ValueError, fcntl.fcntl, _testcapi.INT_MIN - 1, - fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(ValueError, fcntl.fcntl, F(_testcapi.INT_MIN - 1), - fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(INT_MAX + 1, fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(BadFile(INT_MAX + 1), fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(INT_MIN - 1, fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(BadFile(INT_MIN - 1), fcntl.F_SETFL, os.O_NONBLOCK) def test_fcntl_64_bit(self): # Issue #1309352: fcntl shouldn't fail when the third arg fits in a diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -10,10 +10,9 @@ from weakref import proxy from functools import wraps from UserList import UserList -import _testcapi from test.test_support import TESTFN, check_warnings, run_unittest, make_bad_fd -from test.test_support import py3k_bytes as bytes +from test.test_support import py3k_bytes as bytes, cpython_only from test.script_helper import run_python from _io import FileIO as _FileIO @@ -359,7 +358,11 @@ if sys.platform == 'win32': import msvcrt self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd()) + + @cpython_only + def testInvalidFd_overflow(self): # Issue 15989 + import _testcapi self.assertRaises(TypeError, _FileIO, _testcapi.INT_MAX + 1) self.assertRaises(TypeError, _FileIO, _testcapi.INT_MIN - 1) diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -303,21 +303,31 @@ test_support.run_unittest(FormatTest) def test_precision(self): - INT_MAX = 2147483647 - f = 1.2 self.assertEqual(format(f, ".0f"), "1") self.assertEqual(format(f, ".3f"), "1.200") with self.assertRaises(ValueError) as cm: - format(f, ".%sf" % (INT_MAX + 1)) + format(f, ".%sf" % (sys.maxsize + 1)) self.assertEqual(str(cm.exception), "precision too big") c = complex(f) - self.assertEqual(format(f, ".0f"), "1") - self.assertEqual(format(f, ".3f"), "1.200") + self.assertEqual(format(c, ".0f"), "1+0j") + self.assertEqual(format(c, ".3f"), "1.200+0.000j") + with self.assertRaises(ValueError) as cm: + format(c, ".%sf" % (sys.maxsize + 1)) + self.assertEqual(str(cm.exception), "precision too big") + + @test_support.cpython_only + def test_precision_c_limits(self): + from _testcapi import INT_MAX + + f = 1.2 with self.assertRaises(ValueError) as cm: format(f, ".%sf" % (INT_MAX + 1)) - self.assertEqual(str(cm.exception), "precision too big") + + c = complex(f) + with self.assertRaises(ValueError) as cm: + format(c, ".%sf" % (INT_MAX + 1)) if __name__ == "__main__": diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -1,5 +1,7 @@ import unittest from test import test_support +# Skip this test if the _testcapi module isn't available. +test_support.import_module('_testcapi') from _testcapi import getargs_keywords import warnings diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py @@ -3,14 +3,13 @@ import os import random import select -from _testcapi import USHRT_MAX, INT_MAX, UINT_MAX try: import threading except ImportError: threading = None import time import unittest -from test.test_support import TESTFN, run_unittest, reap_threads +from test.test_support import TESTFN, run_unittest, reap_threads, cpython_only try: select.poll @@ -161,8 +160,18 @@ # Issues #15989, #17919 self.assertRaises(OverflowError, pollster.register, 0, -1) + self.assertRaises(OverflowError, pollster.register, 0, 1 << 64) + self.assertRaises(OverflowError, pollster.modify, 1, -1) + self.assertRaises(OverflowError, pollster.modify, 1, 1 << 64) + + @cpython_only + def test_poll_c_limits(self): + from _testcapi import USHRT_MAX, INT_MAX, UINT_MAX + pollster = select.poll() + pollster.register(1) + + # Issues #15989, #17919 self.assertRaises(OverflowError, pollster.register, 0, USHRT_MAX + 1) - self.assertRaises(OverflowError, pollster.modify, 1, -1) self.assertRaises(OverflowError, pollster.modify, 1, USHRT_MAX + 1) self.assertRaises(OverflowError, pollster.poll, INT_MAX + 1) self.assertRaises(OverflowError, pollster.poll, UINT_MAX + 1) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -4,7 +4,6 @@ import errno import socket import select -import _testcapi import time import traceback import Queue @@ -711,7 +710,10 @@ srv.listen(backlog) srv.close() + @test_support.cpython_only + def test_listen_backlog_overflow(self): # Issue 15989 + import _testcapi srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.bind((HOST, 0)) self.assertRaises(OverflowError, srv.listen, _testcapi.INT_MAX + 1) @@ -818,6 +820,14 @@ def _testShutdown(self): self.serv_conn.send(MSG) + self.serv_conn.shutdown(2) + + testShutdown_overflow = test_support.cpython_only(testShutdown) + + @test_support.cpython_only + def _testShutdown_overflow(self): + import _testcapi + self.serv_conn.send(MSG) # Issue 15989 self.assertRaises(OverflowError, self.serv_conn.shutdown, _testcapi.INT_MAX + 1) @@ -911,14 +921,23 @@ pass end = time.time() self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.") - # Issue 15989 - if _testcapi.UINT_MAX < _testcapi.ULONG_MAX: - self.serv.setblocking(_testcapi.UINT_MAX + 1) - self.assertIsNone(self.serv.gettimeout()) def _testSetBlocking(self): pass + @test_support.cpython_only + def testSetBlocking_overflow(self): + # Issue 15989 + import _testcapi + if _testcapi.UINT_MAX >= _testcapi.ULONG_MAX: + self.skipTest('needs UINT_MAX < ULONG_MAX') + self.serv.setblocking(False) + self.assertEqual(self.serv.gettimeout(), 0.0) + self.serv.setblocking(_testcapi.UINT_MAX + 1) + self.assertIsNone(self.serv.gettimeout()) + + _testSetBlocking_overflow = test_support.cpython_only(_testSetBlocking) + def testAccept(self): # Testing non-blocking accept self.serv.setblocking(0) diff --git a/Lib/test/test_structmembers.py b/Lib/test/test_structmembers.py --- a/Lib/test/test_structmembers.py +++ b/Lib/test/test_structmembers.py @@ -1,3 +1,8 @@ +import unittest +from test import test_support + +# Skip this test if the _testcapi module isn't available. +test_support.import_module('_testcapi') from _testcapi import _test_structmembersType, \ CHAR_MAX, CHAR_MIN, UCHAR_MAX, \ SHRT_MAX, SHRT_MIN, USHRT_MAX, \ @@ -5,9 +10,6 @@ LONG_MAX, LONG_MIN, ULONG_MAX, \ LLONG_MAX, LLONG_MIN, ULLONG_MAX -import unittest -from test import test_support - ts=_test_structmembersType(False, 1, 2, 3, 4, 5, 6, 7, 8, 9.99999, 10.1010101010, "hi") diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -19,7 +19,6 @@ import re import time import struct -import _testcapi import sysconfig try: import thread @@ -1002,6 +1001,7 @@ _TPFLAGS_HEAPTYPE = 1<<9 def check_sizeof(test, o, size): + import _testcapi result = sys.getsizeof(o) # add GC header size if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\ diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -489,6 +489,7 @@ p.wait() self.assertIn(executable, ["''", repr(sys.executable)]) + at test.test_support.cpython_only class SizeofTest(unittest.TestCase): def setUp(self): diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -1,7 +1,6 @@ import unittest import sys import os -import _testcapi from test import test_support from subprocess import Popen, PIPE @@ -11,6 +10,11 @@ from Tkinter import Tcl from _tkinter import TclError +try: + from _testcapi import INT_MAX, PY_SSIZE_T_MAX +except ImportError: + INT_MAX = PY_SSIZE_T_MAX = sys.maxsize + tcl_version = _tkinter.TCL_VERSION.split('.') try: for i in range(len(tcl_version)): @@ -523,10 +527,9 @@ def setUp(self): self.interp = Tcl() - @unittest.skipUnless(_testcapi.INT_MAX < _testcapi.PY_SSIZE_T_MAX, - "needs UINT_MAX < SIZE_MAX") - @test_support.precisionbigmemtest(size=_testcapi.INT_MAX + 1, memuse=5, - dry_run=False) + @test_support.cpython_only + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @test_support.precisionbigmemtest(size=INT_MAX + 1, memuse=5, dry_run=False) def test_huge_string(self, size): value = ' ' * size self.assertRaises(OverflowError, self.interp.call, 'set', '_', value) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1,7 +1,7 @@ # Very rudimentary test of threading module import test.test_support -from test.test_support import verbose +from test.test_support import verbose, cpython_only from test.script_helper import assert_python_ok import random @@ -724,6 +724,7 @@ for t in threads: t.join() + @cpython_only @unittest.skipIf(_testcapi is None, "need _testcapi module") def test_frame_tstate_tracing(self): # Issue #14432: Crash when a generator is created in a C thread that is diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1,11 +1,10 @@ """Test cases for traceback module""" -from _testcapi import traceback_print from StringIO import StringIO import sys import unittest from imp import reload -from test.test_support import run_unittest, is_jython, Error +from test.test_support import run_unittest, is_jython, Error, cpython_only import traceback @@ -181,7 +180,9 @@ class TracebackFormatTests(unittest.TestCase): + @cpython_only def test_traceback_format(self): + from _testcapi import traceback_print try: raise KeyError('blah') except KeyError: diff --git a/Lib/test/test_ucn.py b/Lib/test/test_ucn.py --- a/Lib/test/test_ucn.py +++ b/Lib/test/test_ucn.py @@ -9,10 +9,14 @@ import unittest import sys -import _testcapi from test import test_support +try: + from _testcapi import INT_MAX, PY_SSIZE_T_MAX, UINT_MAX +except ImportError: + INT_MAX = PY_SSIZE_T_MAX = UINT_MAX = 2**64 - 1 + class UnicodeNamesTest(unittest.TestCase): def checkletter(self, name, code): @@ -139,11 +143,10 @@ unicode, "\\NSPACE", 'unicode-escape', 'strict' ) - @unittest.skipUnless(_testcapi.INT_MAX < _testcapi.PY_SSIZE_T_MAX, - "needs UINT_MAX < SIZE_MAX") - @unittest.skipUnless(_testcapi.UINT_MAX < sys.maxint, - "needs UINT_MAX < sys.maxint") - @test_support.bigmemtest(minsize=_testcapi.UINT_MAX + 1, + @test_support.cpython_only + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @unittest.skipUnless(UINT_MAX < sys.maxint, "needs UINT_MAX < sys.maxint") + @test_support.bigmemtest(minsize=UINT_MAX + 1, memuse=2 + 4 // len(u'\U00010000')) def test_issue16335(self, size): func = self.test_issue16335 @@ -151,9 +154,8 @@ raise unittest.SkipTest("not enough memory: %.1fG minimum needed" % (func.minsize * func.memuse / float(1024**3),)) # very very long bogus character name - x = b'\\N{SPACE' + b'x' * int(_testcapi.UINT_MAX + 1) + b'}' - self.assertEqual(len(x), len(b'\\N{SPACE}') + - (_testcapi.UINT_MAX + 1)) + x = b'\\N{SPACE' + b'x' * int(UINT_MAX + 1) + b'}' + self.assertEqual(len(x), len(b'\\N{SPACE}') + (UINT_MAX + 1)) self.assertRaisesRegexp(UnicodeError, 'unknown Unicode character name', x.decode, 'unicode-escape' diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -644,8 +644,13 @@ return u'\u1234' self.assertEqual('%s' % Wrapper(), u'\u1234') + def test_formatting_huge_precision(self): + format_string = u"%.{}f".format(sys.maxsize + 1) + with self.assertRaises(ValueError): + result = format_string % 2.34 + @test_support.cpython_only - def test_formatting_huge_precision(self): + def test_formatting_huge_precision_c_limits(self): from _testcapi import INT_MAX format_string = u"%.{}f".format(INT_MAX + 1) with self.assertRaises(ValueError): @@ -1633,6 +1638,7 @@ self.assertEqual("%s" % u, u'__unicode__ overridden') self.assertEqual("{}".format(u), '__unicode__ overridden') + @test_support.cpython_only def test_encode_decimal(self): from _testcapi import unicode_encodedecimal self.assertEqual(unicode_encodedecimal(u'123'), @@ -1658,6 +1664,7 @@ self.assertEqual(unicode_encodedecimal(u"123\u20ac\u0660", "replace"), b'123?0') + @test_support.cpython_only def test_encode_decimal_with_surrogates(self): from _testcapi import unicode_encodedecimal tests = [(u'\U0001f49d', '💝'), diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -224,6 +224,8 @@ Tests ----- +- Issue #20532: Tests which use _testcapi now are marked as CPython only. + - Issue #19920: Added tests for TarFile.list(). Based on patch by Vajrasky Kok. - Issue #19990: Added tests for the imghdr module. Based on patch by -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 09:11:08 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 7 Feb 2014 09:11:08 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTMy?= =?utf-8?q?=3A_Tests_which_use_=5Ftestcapi_now_are_marked_as_CPython_only?= =?utf-8?q?=2E?= Message-ID: <3fL8ND3Qgsz7Ljt@mail.python.org> http://hg.python.org/cpython/rev/e5a78f7c2dcb changeset: 89008:e5a78f7c2dcb branch: 3.3 parent: 89003:98d9d152e94b user: Serhiy Storchaka date: Fri Feb 07 10:06:39 2014 +0200 summary: Issue #20532: Tests which use _testcapi now are marked as CPython only. files: Lib/test/string_tests.py | 25 ++++-- Lib/test/support/__init__.py | 2 +- Lib/test/test_capi.py | 3 +- Lib/test/test_code.py | 5 +- Lib/test/test_codecs.py | 87 +++++++++++++-------- Lib/test/test_descr.py | 1 + Lib/test/test_devpoll.py | 15 +++- Lib/test/test_exceptions.py | 2 + Lib/test/test_fcntl.py | 47 +++++++---- Lib/test/test_fileio.py | 7 +- Lib/test/test_format.py | 22 ++++- Lib/test/test_getargs2.py | 2 + Lib/test/test_io.py | 5 +- Lib/test/test_poll.py | 15 +++- Lib/test/test_posix.py | 6 +- Lib/test/test_socket.py | 36 +++++++- Lib/test/test_structmembers.py | 8 +- Lib/test/test_sys.py | 1 + Lib/test/test_tcl.py | 12 ++- Lib/test/test_threading.py | 3 +- Lib/test/test_time.py | 3 + Lib/test/test_traceback.py | 7 +- Lib/test/test_ucn.py | 18 ++-- Lib/test/test_unicode.py | 11 ++- Misc/NEWS | 2 + 25 files changed, 239 insertions(+), 106 deletions(-) diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -5,7 +5,6 @@ import unittest, string, sys, struct from test import support from collections import UserList -import _testcapi class Sequence: def __init__(self, seq='wxyz'): self.seq = seq @@ -1185,20 +1184,28 @@ # Outrageously large width or precision should raise ValueError. self.checkraises(ValueError, '%%%df' % (2**64), '__mod__', (3.2)) self.checkraises(ValueError, '%%.%df' % (2**64), '__mod__', (3.2)) - self.checkraises(OverflowError, '%*s', '__mod__', - (_testcapi.PY_SSIZE_T_MAX + 1, '')) + (sys.maxsize + 1, '')) self.checkraises(OverflowError, '%.*f', '__mod__', - (_testcapi.INT_MAX + 1, 1. / 7)) - # Issue 15989 - self.checkraises(OverflowError, '%*s', '__mod__', - (1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1), '')) - self.checkraises(OverflowError, '%.*f', '__mod__', - (_testcapi.UINT_MAX + 1, 1. / 7)) + (sys.maxsize + 1, 1. / 7)) class X(object): pass self.checkraises(TypeError, 'abc', '__mod__', X()) + @support.cpython_only + def test_formatting_c_limits(self): + from _testcapi import PY_SSIZE_T_MAX, INT_MAX, UINT_MAX + SIZE_MAX = (1 << (PY_SSIZE_T_MAX.bit_length() + 1)) - 1 + self.checkraises(OverflowError, '%*s', '__mod__', + (PY_SSIZE_T_MAX + 1, '')) + self.checkraises(OverflowError, '%.*f', '__mod__', + (INT_MAX + 1, 1. / 7)) + # Issue 15989 + self.checkraises(OverflowError, '%*s', '__mod__', + (SIZE_MAX + 1, '')) + self.checkraises(OverflowError, '%.*f', '__mod__', + (UINT_MAX + 1, 1. / 7)) + def test_floatformatting(self): # float formatting for prec in range(100): diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -25,7 +25,6 @@ import logging.handlers import struct import tempfile -import _testcapi try: import _thread, threading @@ -1349,6 +1348,7 @@ _TPFLAGS_HEAPTYPE = 1<<9 def check_sizeof(test, o, size): + import _testcapi result = sys.getsizeof(o) # add GC header size if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\ diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -17,7 +17,8 @@ import threading except ImportError: threading = None -import _testcapi +# Skip this test if the _testcapi module isn't available. +_testcapi = support.import_module('_testcapi') def testfunction(self): diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -104,7 +104,7 @@ import unittest import weakref -import _testcapi +from test.support import run_doctest, run_unittest, cpython_only def consts(t): @@ -126,7 +126,9 @@ class CodeTest(unittest.TestCase): + @cpython_only def test_newempty(self): + import _testcapi co = _testcapi.code_newempty("filename", "funcname", 15) self.assertEqual(co.co_filename, "filename") self.assertEqual(co.co_name, "funcname") @@ -159,7 +161,6 @@ def test_main(verbose=None): - from test.support import run_doctest, run_unittest from test import test_code run_doctest(test_code, verbose) run_unittest(CodeTest, CodeWeakRefTest) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -1,4 +1,3 @@ -import _testcapi import codecs import io import locale @@ -1710,7 +1709,7 @@ class BasicUnicodeTest(unittest.TestCase, MixInCheckStateHandling): def test_basics(self): - s = "abc123" # all codecs should be able to encode these + s = "abc123" # all codecs should be able to encode these for encoding in all_unicode_encodings: name = codecs.lookup(encoding).name if encoding.endswith("_codec"): @@ -1722,9 +1721,9 @@ with support.check_warnings(): # unicode-internal has been deprecated (b, size) = codecs.getencoder(encoding)(s) - self.assertEqual(size, len(s), "%r != %r (encoding=%r)" % (size, len(s), encoding)) + self.assertEqual(size, len(s), "encoding=%r" % encoding) (chars, size) = codecs.getdecoder(encoding)(b) - self.assertEqual(chars, s, "%r != %r (encoding=%r)" % (chars, s, encoding)) + self.assertEqual(chars, s, "encoding=%r" % encoding) if encoding not in broken_unicode_with_streams: # check stream reader/writer @@ -1742,15 +1741,13 @@ for c in encodedresult: q.write(bytes([c])) decodedresult += reader.read() - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + self.assertEqual(decodedresult, s, "encoding=%r" % encoding) if encoding not in broken_incremental_coders: - # check incremental decoder/encoder (fetched via the Python - # and C API) and iterencode()/iterdecode() + # check incremental decoder/encoder and iterencode()/iterdecode() try: encoder = codecs.getincrementalencoder(encoding)() - cencoder = _testcapi.codec_incrementalencoder(encoding) - except LookupError: # no IncrementalEncoder + except LookupError: # no IncrementalEncoder pass else: # check incremental decoder/encoder @@ -1763,45 +1760,71 @@ for c in encodedresult: decodedresult += decoder.decode(bytes([c])) decodedresult += decoder.decode(b"", True) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) - - # check C API - encodedresult = b"" - for c in s: - encodedresult += cencoder.encode(c) - encodedresult += cencoder.encode("", True) - cdecoder = _testcapi.codec_incrementaldecoder(encoding) - decodedresult = "" - for c in encodedresult: - decodedresult += cdecoder.decode(bytes([c])) - decodedresult += cdecoder.decode(b"", True) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) # check iterencode()/iterdecode() - result = "".join(codecs.iterdecode(codecs.iterencode(s, encoding), encoding)) - self.assertEqual(result, s, "%r != %r (encoding=%r)" % (result, s, encoding)) + result = "".join(codecs.iterdecode( + codecs.iterencode(s, encoding), encoding)) + self.assertEqual(result, s, "encoding=%r" % encoding) # check iterencode()/iterdecode() with empty string - result = "".join(codecs.iterdecode(codecs.iterencode("", encoding), encoding)) + result = "".join(codecs.iterdecode( + codecs.iterencode("", encoding), encoding)) self.assertEqual(result, "") if encoding not in ("idna", "mbcs"): # check incremental decoder/encoder with errors argument try: encoder = codecs.getincrementalencoder(encoding)("ignore") - cencoder = _testcapi.codec_incrementalencoder(encoding, "ignore") - except LookupError: # no IncrementalEncoder + except LookupError: # no IncrementalEncoder pass else: encodedresult = b"".join(encoder.encode(c) for c in s) decoder = codecs.getincrementaldecoder(encoding)("ignore") - decodedresult = "".join(decoder.decode(bytes([c])) for c in encodedresult) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + decodedresult = "".join(decoder.decode(bytes([c])) + for c in encodedresult) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) + @support.cpython_only + def test_basics_capi(self): + from _testcapi import codec_incrementalencoder, codec_incrementaldecoder + s = "abc123" # all codecs should be able to encode these + for encoding in all_unicode_encodings: + if encoding not in broken_incremental_coders: + # check incremental decoder/encoder (fetched via the C API) + try: + cencoder = codec_incrementalencoder(encoding) + except LookupError: # no IncrementalEncoder + pass + else: + # check C API + encodedresult = b"" + for c in s: + encodedresult += cencoder.encode(c) + encodedresult += cencoder.encode("", True) + cdecoder = codec_incrementaldecoder(encoding) + decodedresult = "" + for c in encodedresult: + decodedresult += cdecoder.decode(bytes([c])) + decodedresult += cdecoder.decode(b"", True) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) + + if encoding not in ("idna", "mbcs"): + # check incremental decoder/encoder with errors argument + try: + cencoder = codec_incrementalencoder(encoding, "ignore") + except LookupError: # no IncrementalEncoder + pass + else: encodedresult = b"".join(cencoder.encode(c) for c in s) - cdecoder = _testcapi.codec_incrementaldecoder(encoding, "ignore") - decodedresult = "".join(cdecoder.decode(bytes([c])) for c in encodedresult) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + cdecoder = codec_incrementaldecoder(encoding, "ignore") + decodedresult = "".join(cdecoder.decode(bytes([c])) + for c in encodedresult) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) def test_seek(self): # all codecs should be able to encode these diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2046,6 +2046,7 @@ prop2 = property(fset=setter) self.assertEqual(prop2.__doc__, None) + @support.cpython_only def test_testcapi_no_segfault(self): # this segfaulted in 2.5b2 try: diff --git a/Lib/test/test_devpoll.py b/Lib/test/test_devpoll.py --- a/Lib/test/test_devpoll.py +++ b/Lib/test/test_devpoll.py @@ -3,8 +3,7 @@ # Initial tests are copied as is from "test_poll.py" import os, select, random, unittest, sys -from test.support import TESTFN, run_unittest -from _testcapi import USHRT_MAX +from test.support import TESTFN, run_unittest, cpython_only try: select.devpoll @@ -94,8 +93,18 @@ pollster.register(w) # Issue #17919 self.assertRaises(OverflowError, pollster.register, 0, -1) + self.assertRaises(OverflowError, pollster.register, 0, 1 << 64) + self.assertRaises(OverflowError, pollster.modify, 1, -1) + self.assertRaises(OverflowError, pollster.modify, 1, 1 << 64) + + @cpython_only + def test_events_mask_overflow_c_limits(self): + from _testcapi import USHRT_MAX + pollster = select.devpoll() + w, r = os.pipe() + pollster.register(w) + # Issue #17919 self.assertRaises(OverflowError, pollster.register, 0, USHRT_MAX + 1) - self.assertRaises(OverflowError, pollster.modify, 1, -1) self.assertRaises(OverflowError, pollster.modify, 1, USHRT_MAX + 1) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -832,6 +832,7 @@ self.assertIn("maximum recursion depth exceeded", str(v)) + @cpython_only def test_MemoryError(self): # PyErr_NoMemory always raises the same exception instance. # Check that the traceback is not doubled. @@ -890,6 +891,7 @@ self.assertEqual(error5.a, 1) self.assertEqual(error5.__doc__, "") + @cpython_only def test_memory_error_cleanup(self): # Issue #5437: preallocated MemoryError instances should not keep # traceback objects alive. diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -7,9 +7,9 @@ import os import struct import sys -import _testcapi import unittest -from test.support import verbose, TESTFN, unlink, run_unittest, import_module +from test.support import (verbose, TESTFN, unlink, run_unittest, import_module, + cpython_only) # Skip test if no fcntl module. fcntl = import_module('fcntl') @@ -50,6 +50,12 @@ lockdata = get_lockdata() +class BadFile: + def __init__(self, fn): + self.fn = fn + def fileno(self): + return self.fn + class TestFcntl(unittest.TestCase): def setUp(self): @@ -81,24 +87,27 @@ self.f.close() def test_fcntl_bad_file(self): - class F: - def __init__(self, fn): - self.fn = fn - def fileno(self): - return self.fn - self.assertRaises(ValueError, fcntl.fcntl, -1, fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(ValueError, fcntl.fcntl, F(-1), fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(TypeError, fcntl.fcntl, 'spam', fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(TypeError, fcntl.fcntl, F('spam'), fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(-1, fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(BadFile(-1), fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(TypeError): + fcntl.fcntl('spam', fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(TypeError): + fcntl.fcntl(BadFile('spam'), fcntl.F_SETFL, os.O_NONBLOCK) + + @cpython_only + def test_fcntl_bad_file_overflow(self): + from _testcapi import INT_MAX, INT_MIN # Issue 15989 - self.assertRaises(OverflowError, fcntl.fcntl, _testcapi.INT_MAX + 1, - fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(OverflowError, fcntl.fcntl, F(_testcapi.INT_MAX + 1), - fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(OverflowError, fcntl.fcntl, _testcapi.INT_MIN - 1, - fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(OverflowError, fcntl.fcntl, F(_testcapi.INT_MIN - 1), - fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(OverflowError): + fcntl.fcntl(INT_MAX + 1, fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(OverflowError): + fcntl.fcntl(BadFile(INT_MAX + 1), fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(OverflowError): + fcntl.fcntl(INT_MIN - 1, fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(OverflowError): + fcntl.fcntl(BadFile(INT_MIN - 1), fcntl.F_SETFL, os.O_NONBLOCK) @unittest.skipIf( platform.machine().startswith('arm') and platform.system() == 'Linux', diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -8,9 +8,8 @@ from array import array from weakref import proxy from functools import wraps -import _testcapi -from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd +from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd, cpython_only from collections import UserList from _io import FileIO as _FileIO @@ -362,7 +361,11 @@ if sys.platform == 'win32': import msvcrt self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd()) + + @cpython_only + def testInvalidFd_overflow(self): # Issue 15989 + import _testcapi self.assertRaises(TypeError, _FileIO, _testcapi.INT_MAX + 1) self.assertRaises(TypeError, _FileIO, _testcapi.INT_MIN - 1) diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -313,21 +313,31 @@ support.run_unittest(FormatTest) def test_precision(self): - INT_MAX = 2147483647 - f = 1.2 self.assertEqual(format(f, ".0f"), "1") self.assertEqual(format(f, ".3f"), "1.200") with self.assertRaises(ValueError) as cm: - format(f, ".%sf" % (INT_MAX + 1)) + format(f, ".%sf" % (sys.maxsize + 1)) self.assertEqual(str(cm.exception), "precision too big") c = complex(f) - self.assertEqual(format(f, ".0f"), "1") - self.assertEqual(format(f, ".3f"), "1.200") + self.assertEqual(format(c, ".0f"), "1+0j") + self.assertEqual(format(c, ".3f"), "1.200+0.000j") + with self.assertRaises(ValueError) as cm: + format(c, ".%sf" % (sys.maxsize + 1)) + self.assertEqual(str(cm.exception), "precision too big") + + @support.cpython_only + def test_precision_c_limits(self): + from _testcapi import INT_MAX + + f = 1.2 with self.assertRaises(ValueError) as cm: format(f, ".%sf" % (INT_MAX + 1)) - self.assertEqual(str(cm.exception), "precision too big") + + c = complex(f) + with self.assertRaises(ValueError) as cm: + format(c, ".%sf" % (INT_MAX + 1)) if __name__ == "__main__": diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -1,5 +1,7 @@ import unittest from test import support +# Skip this test if the _testcapi module isn't available. +support.import_module('_testcapi') from _testcapi import getargs_keywords, getargs_keyword_only """ diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -32,7 +32,6 @@ import unittest import warnings import weakref -import _testcapi from collections import deque, UserList from itertools import cycle, count from test import support @@ -1977,8 +1976,10 @@ os.environ.clear() os.environ.update(old_environ) - # Issue 15989 + @support.cpython_only def test_device_encoding(self): + # Issue 15989 + import _testcapi b = self.BytesIO() b.fileno = lambda: _testcapi.INT_MAX + 1 self.assertRaises(OverflowError, self.TextIOWrapper, b) diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py @@ -3,14 +3,13 @@ import os import random import select -from _testcapi import USHRT_MAX, INT_MAX, UINT_MAX try: import threading except ImportError: threading = None import time import unittest -from test.support import TESTFN, run_unittest, reap_threads +from test.support import TESTFN, run_unittest, reap_threads, cpython_only try: select.poll @@ -161,8 +160,18 @@ # Issues #15989, #17919 self.assertRaises(OverflowError, pollster.register, 0, -1) + self.assertRaises(OverflowError, pollster.register, 0, 1 << 64) + self.assertRaises(OverflowError, pollster.modify, 1, -1) + self.assertRaises(OverflowError, pollster.modify, 1, 1 << 64) + + @cpython_only + def test_poll_c_limits(self): + from _testcapi import USHRT_MAX, INT_MAX, UINT_MAX + pollster = select.poll() + pollster.register(1) + + # Issues #15989, #17919 self.assertRaises(OverflowError, pollster.register, 0, USHRT_MAX + 1) - self.assertRaises(OverflowError, pollster.modify, 1, -1) self.assertRaises(OverflowError, pollster.modify, 1, USHRT_MAX + 1) self.assertRaises(OverflowError, pollster.poll, INT_MAX + 1) self.assertRaises(OverflowError, pollster.poll, UINT_MAX + 1) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -17,7 +17,6 @@ import tempfile import unittest import warnings -import _testcapi _DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(), support.TESTFN + '-dummy-symlink') @@ -615,7 +614,12 @@ except OSError: pass + @support.cpython_only + @unittest.skipUnless(hasattr(os, 'pipe2'), "test needs os.pipe2()") + @support.requires_linux_version(2, 6, 27) + def test_pipe2_c_limits(self): # Issue 15989 + import _testcapi self.assertRaises(OverflowError, os.pipe2, _testcapi.INT_MAX + 1) self.assertRaises(OverflowError, os.pipe2, _testcapi.UINT_MAX + 1) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7,7 +7,6 @@ import socket import select import tempfile -import _testcapi import time import traceback import queue @@ -1274,7 +1273,10 @@ srv.listen(backlog) srv.close() + @support.cpython_only + def test_listen_backlog_overflow(self): # Issue 15989 + import _testcapi srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.bind((HOST, 0)) self.assertRaises(OverflowError, srv.listen, _testcapi.INT_MAX + 1) @@ -1593,6 +1595,14 @@ def _testShutdown(self): self.serv_conn.send(MSG) + self.serv_conn.shutdown(2) + + testShutdown_overflow = support.cpython_only(testShutdown) + + @support.cpython_only + def _testShutdown_overflow(self): + import _testcapi + self.serv_conn.send(MSG) # Issue 15989 self.assertRaises(OverflowError, self.serv_conn.shutdown, _testcapi.INT_MAX + 1) @@ -2361,7 +2371,12 @@ # code with these functions. # Match the definition in socketmodule.c - socklen_t_limit = min(0x7fffffff, _testcapi.INT_MAX) + try: + import _testcapi + except ImportError: + socklen_t_limit = 0x7fffffff + else: + socklen_t_limit = min(0x7fffffff, _testcapi.INT_MAX) @requireAttrs(socket, "CMSG_LEN") def testCMSG_LEN(self): @@ -3586,14 +3601,23 @@ pass end = time.time() self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.") - # Issue 15989 - if _testcapi.UINT_MAX < _testcapi.ULONG_MAX: - self.serv.setblocking(_testcapi.UINT_MAX + 1) - self.assertIsNone(self.serv.gettimeout()) def _testSetBlocking(self): pass + @support.cpython_only + def testSetBlocking_overflow(self): + # Issue 15989 + import _testcapi + if _testcapi.UINT_MAX >= _testcapi.ULONG_MAX: + self.skipTest('needs UINT_MAX < ULONG_MAX') + self.serv.setblocking(False) + self.assertEqual(self.serv.gettimeout(), 0.0) + self.serv.setblocking(_testcapi.UINT_MAX + 1) + self.assertIsNone(self.serv.gettimeout()) + + _testSetBlocking_overflow = support.cpython_only(_testSetBlocking) + @unittest.skipUnless(hasattr(socket, 'SOCK_NONBLOCK'), 'test needs socket.SOCK_NONBLOCK') @support.requires_linux_version(2, 6, 28) diff --git a/Lib/test/test_structmembers.py b/Lib/test/test_structmembers.py --- a/Lib/test/test_structmembers.py +++ b/Lib/test/test_structmembers.py @@ -1,3 +1,8 @@ +import unittest +from test import support + +# Skip this test if the _testcapi module isn't available. +support.import_module('_testcapi') from _testcapi import _test_structmembersType, \ CHAR_MAX, CHAR_MIN, UCHAR_MAX, \ SHRT_MAX, SHRT_MIN, USHRT_MAX, \ @@ -6,9 +11,6 @@ LLONG_MAX, LLONG_MIN, ULLONG_MAX, \ PY_SSIZE_T_MAX, PY_SSIZE_T_MIN -import unittest -from test import support - ts=_test_structmembersType(False, # T_BOOL 1, # T_BYTE 2, # T_UBYTE diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -610,6 +610,7 @@ ret, out, err = assert_python_ok(*args) self.assertIn(b"free PyDictObjects", err) + at test.support.cpython_only class SizeofTest(unittest.TestCase): def setUp(self): diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -1,7 +1,6 @@ import unittest import sys import os -import _testcapi from test import support # Skip this test if the _tkinter module wasn't built. @@ -13,6 +12,11 @@ from tkinter import Tcl from _tkinter import TclError +try: + from _testcapi import INT_MAX, PY_SSIZE_T_MAX +except ImportError: + INT_MAX = PY_SSIZE_T_MAX = sys.maxsize + tcl_version = _tkinter.TCL_VERSION.split('.') try: for i in range(len(tcl_version)): @@ -539,9 +543,9 @@ def setUp(self): self.interp = Tcl() - @unittest.skipUnless(_testcapi.INT_MAX < _testcapi.PY_SSIZE_T_MAX, - "needs UINT_MAX < SIZE_MAX") - @support.bigmemtest(size=_testcapi.INT_MAX + 1, memuse=5, dry_run=False) + @support.cpython_only + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @support.bigmemtest(size=INT_MAX + 1, memuse=5, dry_run=False) def test_huge_string(self, size): value = ' ' * size self.assertRaises(OverflowError, self.interp.call, 'set', '_', value) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -3,7 +3,7 @@ """ import test.support -from test.support import verbose, strip_python_stderr, import_module +from test.support import verbose, strip_python_stderr, import_module, cpython_only from test.script_helper import assert_python_ok import random @@ -773,6 +773,7 @@ for t in threads: t.join() + @cpython_only @unittest.skipIf(_testcapi is None, "need _testcapi module") def test_frame_tstate_tracing(self): # Issue #14432: Crash when a generator is created in a C thread that is diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -573,6 +573,7 @@ -(2.0 ** 100.0), 2.0 ** 100.0, ) + @support.cpython_only def test_time_t(self): from _testcapi import pytime_object_to_time_t for obj, time_t in ( @@ -588,6 +589,7 @@ for invalid in self.invalid_values: self.assertRaises(OverflowError, pytime_object_to_time_t, invalid) + @support.cpython_only def test_timeval(self): from _testcapi import pytime_object_to_timeval for obj, timeval in ( @@ -607,6 +609,7 @@ for invalid in self.invalid_values: self.assertRaises(OverflowError, pytime_object_to_timeval, invalid) + @support.cpython_only def test_timespec(self): from _testcapi import pytime_object_to_timespec for obj, timespec in ( diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1,12 +1,11 @@ """Test cases for traceback module""" -from _testcapi import traceback_print, exception_print from io import StringIO import sys import unittest import re from test.support import run_unittest, Error, captured_output -from test.support import TESTFN, unlink +from test.support import TESTFN, unlink, cpython_only import traceback @@ -173,7 +172,9 @@ class TracebackFormatTests(unittest.TestCase): + @cpython_only def test_traceback_format(self): + from _testcapi import traceback_print try: raise KeyError('blah') except KeyError: @@ -360,7 +361,9 @@ # This checks built-in reporting by the interpreter. # + @cpython_only def get_report(self, e): + from _testcapi import exception_print e = self.get_exception(e) with captured_output("stderr") as s: exception_print(e) diff --git a/Lib/test/test_ucn.py b/Lib/test/test_ucn.py --- a/Lib/test/test_ucn.py +++ b/Lib/test/test_ucn.py @@ -9,12 +9,16 @@ import unittest import unicodedata -import _testcapi from test import support from http.client import HTTPException from test.test_normalization import check_version +try: + from _testcapi import INT_MAX, PY_SSIZE_T_MAX, UINT_MAX +except ImportError: + INT_MAX = PY_SSIZE_T_MAX = UINT_MAX = 2**64 - 1 + class UnicodeNamesTest(unittest.TestCase): def checkletter(self, name, code): @@ -216,15 +220,13 @@ str, b"\\NSPACE", 'unicode-escape', 'strict' ) - @unittest.skipUnless(_testcapi.INT_MAX < _testcapi.PY_SSIZE_T_MAX, - "needs UINT_MAX < SIZE_MAX") - @support.bigmemtest(size=_testcapi.UINT_MAX + 1, - memuse=2 + 1, dry_run=False) + @support.cpython_only + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @support.bigmemtest(size=UINT_MAX + 1, memuse=2 + 1, dry_run=False) def test_issue16335(self, size): # very very long bogus character name - x = b'\\N{SPACE' + b'x' * (_testcapi.UINT_MAX + 1) + b'}' - self.assertEqual(len(x), len(b'\\N{SPACE}') + - (_testcapi.UINT_MAX + 1)) + x = b'\\N{SPACE' + b'x' * (UINT_MAX + 1) + b'}' + self.assertEqual(len(x), len(b'\\N{SPACE}') + (UINT_MAX + 1)) self.assertRaisesRegex(UnicodeError, 'unknown Unicode character name', x.decode, 'unicode-escape' diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -1108,8 +1108,13 @@ self.assertEqual('%.1s' % "a\xe9\u20ac", 'a') self.assertEqual('%.2s' % "a\xe9\u20ac", 'a\xe9') + def test_formatting_huge_precision(self): + format_string = "%.{}f".format(sys.maxsize + 1) + with self.assertRaises(ValueError): + result = format_string % 2.34 + @support.cpython_only - def test_formatting_huge_precision(self): + def test_formatting_huge_precision_c_limits(self): from _testcapi import INT_MAX format_string = "%.{}f".format(INT_MAX + 1) with self.assertRaises(ValueError): @@ -2090,6 +2095,7 @@ self.assertEqual(PyUnicode_FromFormat(b'%.%s', b'abc'), '%.%s') # Test PyUnicode_AsWideChar() + @support.cpython_only def test_aswidechar(self): from _testcapi import unicode_aswidechar support.import_module('ctypes') @@ -2127,6 +2133,7 @@ self.assertEqual(wchar, nonbmp + '\0') # Test PyUnicode_AsWideCharString() + @support.cpython_only def test_aswidecharstring(self): from _testcapi import unicode_aswidecharstring support.import_module('ctypes') @@ -2161,6 +2168,7 @@ s += "4" self.assertEqual(s, "3") + @support.cpython_only def test_encode_decimal(self): from _testcapi import unicode_encodedecimal self.assertEqual(unicode_encodedecimal('123'), @@ -2176,6 +2184,7 @@ "^'decimal' codec can't encode character", unicode_encodedecimal, "123\u20ac", "replace") + @support.cpython_only def test_transform_decimal(self): from _testcapi import unicode_transformdecimaltoascii as transform_decimal self.assertEqual(transform_decimal('123'), diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -324,6 +324,8 @@ Tests ----- +- Issue #20532: Tests which use _testcapi are now marked as CPython only. + - Issue #19920: Added tests for TarFile.list(). Based on patch by Vajrasky Kok. - Issue #19990: Added tests for the imghdr module. Based on patch by -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 09:11:10 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 7 Feb 2014 09:11:10 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320532=3A_Tests_which_use_=5Ftestcapi_now_are_ma?= =?utf-8?q?rked_as_CPython_only=2E?= Message-ID: <3fL8NG1lYpz7Lk2@mail.python.org> http://hg.python.org/cpython/rev/b4e99bec0c8a changeset: 89009:b4e99bec0c8a parent: 89006:844879389a17 parent: 89008:e5a78f7c2dcb user: Serhiy Storchaka date: Fri Feb 07 10:10:55 2014 +0200 summary: Issue #20532: Tests which use _testcapi now are marked as CPython only. files: Lib/test/string_tests.py | 25 ++++-- Lib/test/support/__init__.py | 3 +- Lib/test/test_atexit.py | 1 - Lib/test/test_capi.py | 3 +- Lib/test/test_code.py | 5 +- Lib/test/test_codecs.py | 87 +++++++++++++-------- Lib/test/test_descr.py | 1 + Lib/test/test_devpoll.py | 16 +++- Lib/test/test_exceptions.py | 2 + Lib/test/test_fcntl.py | 51 ++++++++---- Lib/test/test_fileio.py | 7 +- Lib/test/test_finalization.py | 17 +++- Lib/test/test_format.py | 17 +++- Lib/test/test_gc.py | 22 ++++- Lib/test/test_getargs2.py | 2 + Lib/test/test_inspect.py | 13 ++- Lib/test/test_io.py | 5 +- Lib/test/test_poll.py | 15 +++- Lib/test/test_posix.py | 6 +- Lib/test/test_socket.py | 36 +++++++- Lib/test/test_structmembers.py | 8 +- Lib/test/test_sys.py | 1 + Lib/test/test_tcl.py | 12 ++- Lib/test/test_threading.py | 6 +- Lib/test/test_time.py | 3 + Lib/test/test_traceback.py | 7 +- Lib/test/test_ucn.py | 18 ++-- Lib/test/test_unicode.py | 11 ++- Misc/NEWS | 2 + 29 files changed, 284 insertions(+), 118 deletions(-) diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -5,7 +5,6 @@ import unittest, string, sys, struct from test import support from collections import UserList -import _testcapi class Sequence: def __init__(self, seq='wxyz'): self.seq = seq @@ -1199,20 +1198,28 @@ # Outrageously large width or precision should raise ValueError. self.checkraises(ValueError, '%%%df' % (2**64), '__mod__', (3.2)) self.checkraises(ValueError, '%%.%df' % (2**64), '__mod__', (3.2)) - self.checkraises(OverflowError, '%*s', '__mod__', - (_testcapi.PY_SSIZE_T_MAX + 1, '')) + (sys.maxsize + 1, '')) self.checkraises(OverflowError, '%.*f', '__mod__', - (_testcapi.INT_MAX + 1, 1. / 7)) - # Issue 15989 - self.checkraises(OverflowError, '%*s', '__mod__', - (1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1), '')) - self.checkraises(OverflowError, '%.*f', '__mod__', - (_testcapi.UINT_MAX + 1, 1. / 7)) + (sys.maxsize + 1, 1. / 7)) class X(object): pass self.checkraises(TypeError, 'abc', '__mod__', X()) + @support.cpython_only + def test_formatting_c_limits(self): + from _testcapi import PY_SSIZE_T_MAX, INT_MAX, UINT_MAX + SIZE_MAX = (1 << (PY_SSIZE_T_MAX.bit_length() + 1)) - 1 + self.checkraises(OverflowError, '%*s', '__mod__', + (PY_SSIZE_T_MAX + 1, '')) + self.checkraises(OverflowError, '%.*f', '__mod__', + (INT_MAX + 1, 1. / 7)) + # Issue 15989 + self.checkraises(OverflowError, '%*s', '__mod__', + (SIZE_MAX + 1, '')) + self.checkraises(OverflowError, '%.*f', '__mod__', + (UINT_MAX + 1, 1. / 7)) + def test_floatformatting(self): # float formatting for prec in range(100): diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -25,7 +25,6 @@ import logging.handlers import struct import tempfile -import _testcapi try: import _thread, threading @@ -1373,6 +1372,7 @@ _TPFLAGS_HEAPTYPE = 1<<9 def check_sizeof(test, o, size): + import _testcapi result = sys.getsizeof(o) # add GC header size if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\ @@ -2172,4 +2172,5 @@ raise unittest.SkipTest("run_in_subinterp() cannot be used " "if tracemalloc module is tracing " "memory allocations") + import _testcapi return _testcapi.run_in_subinterp(code) diff --git a/Lib/test/test_atexit.py b/Lib/test/test_atexit.py --- a/Lib/test/test_atexit.py +++ b/Lib/test/test_atexit.py @@ -2,7 +2,6 @@ import unittest import io import atexit -import _testcapi from test import support ### helpers diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -18,7 +18,8 @@ import threading except ImportError: threading = None -import _testcapi +# Skip this test if the _testcapi module isn't available. +_testcapi = support.import_module('_testcapi') def testfunction(self): diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -104,7 +104,7 @@ import unittest import weakref -import _testcapi +from test.support import run_doctest, run_unittest, cpython_only def consts(t): @@ -126,7 +126,9 @@ class CodeTest(unittest.TestCase): + @cpython_only def test_newempty(self): + import _testcapi co = _testcapi.code_newempty("filename", "funcname", 15) self.assertEqual(co.co_filename, "filename") self.assertEqual(co.co_name, "funcname") @@ -159,7 +161,6 @@ def test_main(verbose=None): - from test.support import run_doctest, run_unittest from test import test_code run_doctest(test_code, verbose) run_unittest(CodeTest, CodeWeakRefTest) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -1,4 +1,3 @@ -import _testcapi import codecs import contextlib import io @@ -1760,7 +1759,7 @@ class BasicUnicodeTest(unittest.TestCase, MixInCheckStateHandling): def test_basics(self): - s = "abc123" # all codecs should be able to encode these + s = "abc123" # all codecs should be able to encode these for encoding in all_unicode_encodings: name = codecs.lookup(encoding).name if encoding.endswith("_codec"): @@ -1772,9 +1771,9 @@ with support.check_warnings(): # unicode-internal has been deprecated (b, size) = codecs.getencoder(encoding)(s) - self.assertEqual(size, len(s), "%r != %r (encoding=%r)" % (size, len(s), encoding)) + self.assertEqual(size, len(s), "encoding=%r" % encoding) (chars, size) = codecs.getdecoder(encoding)(b) - self.assertEqual(chars, s, "%r != %r (encoding=%r)" % (chars, s, encoding)) + self.assertEqual(chars, s, "encoding=%r" % encoding) if encoding not in broken_unicode_with_streams: # check stream reader/writer @@ -1792,15 +1791,13 @@ for c in encodedresult: q.write(bytes([c])) decodedresult += reader.read() - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + self.assertEqual(decodedresult, s, "encoding=%r" % encoding) if encoding not in broken_incremental_coders: - # check incremental decoder/encoder (fetched via the Python - # and C API) and iterencode()/iterdecode() + # check incremental decoder/encoder and iterencode()/iterdecode() try: encoder = codecs.getincrementalencoder(encoding)() - cencoder = _testcapi.codec_incrementalencoder(encoding) - except LookupError: # no IncrementalEncoder + except LookupError: # no IncrementalEncoder pass else: # check incremental decoder/encoder @@ -1813,45 +1810,71 @@ for c in encodedresult: decodedresult += decoder.decode(bytes([c])) decodedresult += decoder.decode(b"", True) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) - - # check C API - encodedresult = b"" - for c in s: - encodedresult += cencoder.encode(c) - encodedresult += cencoder.encode("", True) - cdecoder = _testcapi.codec_incrementaldecoder(encoding) - decodedresult = "" - for c in encodedresult: - decodedresult += cdecoder.decode(bytes([c])) - decodedresult += cdecoder.decode(b"", True) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) # check iterencode()/iterdecode() - result = "".join(codecs.iterdecode(codecs.iterencode(s, encoding), encoding)) - self.assertEqual(result, s, "%r != %r (encoding=%r)" % (result, s, encoding)) + result = "".join(codecs.iterdecode( + codecs.iterencode(s, encoding), encoding)) + self.assertEqual(result, s, "encoding=%r" % encoding) # check iterencode()/iterdecode() with empty string - result = "".join(codecs.iterdecode(codecs.iterencode("", encoding), encoding)) + result = "".join(codecs.iterdecode( + codecs.iterencode("", encoding), encoding)) self.assertEqual(result, "") if encoding not in ("idna", "mbcs"): # check incremental decoder/encoder with errors argument try: encoder = codecs.getincrementalencoder(encoding)("ignore") - cencoder = _testcapi.codec_incrementalencoder(encoding, "ignore") - except LookupError: # no IncrementalEncoder + except LookupError: # no IncrementalEncoder pass else: encodedresult = b"".join(encoder.encode(c) for c in s) decoder = codecs.getincrementaldecoder(encoding)("ignore") - decodedresult = "".join(decoder.decode(bytes([c])) for c in encodedresult) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + decodedresult = "".join(decoder.decode(bytes([c])) + for c in encodedresult) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) + @support.cpython_only + def test_basics_capi(self): + from _testcapi import codec_incrementalencoder, codec_incrementaldecoder + s = "abc123" # all codecs should be able to encode these + for encoding in all_unicode_encodings: + if encoding not in broken_incremental_coders: + # check incremental decoder/encoder (fetched via the C API) + try: + cencoder = codec_incrementalencoder(encoding) + except LookupError: # no IncrementalEncoder + pass + else: + # check C API + encodedresult = b"" + for c in s: + encodedresult += cencoder.encode(c) + encodedresult += cencoder.encode("", True) + cdecoder = codec_incrementaldecoder(encoding) + decodedresult = "" + for c in encodedresult: + decodedresult += cdecoder.decode(bytes([c])) + decodedresult += cdecoder.decode(b"", True) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) + + if encoding not in ("idna", "mbcs"): + # check incremental decoder/encoder with errors argument + try: + cencoder = codec_incrementalencoder(encoding, "ignore") + except LookupError: # no IncrementalEncoder + pass + else: encodedresult = b"".join(cencoder.encode(c) for c in s) - cdecoder = _testcapi.codec_incrementaldecoder(encoding, "ignore") - decodedresult = "".join(cdecoder.decode(bytes([c])) for c in encodedresult) - self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + cdecoder = codec_incrementaldecoder(encoding, "ignore") + decodedresult = "".join(cdecoder.decode(bytes([c])) + for c in encodedresult) + self.assertEqual(decodedresult, s, + "encoding=%r" % encoding) def test_seek(self): # all codecs should be able to encode these diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2050,6 +2050,7 @@ prop2 = property(fset=setter) self.assertEqual(prop2.__doc__, None) + @support.cpython_only def test_testcapi_no_segfault(self): # this segfaulted in 2.5b2 try: diff --git a/Lib/test/test_devpoll.py b/Lib/test/test_devpoll.py --- a/Lib/test/test_devpoll.py +++ b/Lib/test/test_devpoll.py @@ -7,8 +7,7 @@ import select import sys import unittest -from test.support import TESTFN, run_unittest -from _testcapi import USHRT_MAX +from test.support import TESTFN, run_unittest, cpython_only try: select.devpoll @@ -121,15 +120,24 @@ self.addCleanup(devpoll.close) self.assertEqual(os.get_inheritable(devpoll.fileno()), False) - def test_events_mask_overflow(self): pollster = select.devpoll() w, r = os.pipe() pollster.register(w) # Issue #17919 self.assertRaises(OverflowError, pollster.register, 0, -1) + self.assertRaises(OverflowError, pollster.register, 0, 1 << 64) + self.assertRaises(OverflowError, pollster.modify, 1, -1) + self.assertRaises(OverflowError, pollster.modify, 1, 1 << 64) + + @cpython_only + def test_events_mask_overflow_c_limits(self): + from _testcapi import USHRT_MAX + pollster = select.devpoll() + w, r = os.pipe() + pollster.register(w) + # Issue #17919 self.assertRaises(OverflowError, pollster.register, 0, USHRT_MAX + 1) - self.assertRaises(OverflowError, pollster.modify, 1, -1) self.assertRaises(OverflowError, pollster.modify, 1, USHRT_MAX + 1) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -832,6 +832,7 @@ self.assertIn("maximum recursion depth exceeded", str(v)) + @cpython_only def test_MemoryError(self): # PyErr_NoMemory always raises the same exception instance. # Check that the traceback is not doubled. @@ -890,6 +891,7 @@ self.assertEqual(error5.a, 1) self.assertEqual(error5.__doc__, "") + @cpython_only def test_memory_error_cleanup(self): # Issue #5437: preallocated MemoryError instances should not keep # traceback objects alive. diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -4,9 +4,9 @@ import os import struct import sys -import _testcapi import unittest -from test.support import verbose, TESTFN, unlink, run_unittest, import_module +from test.support import (verbose, TESTFN, unlink, run_unittest, import_module, + cpython_only) # Skip test if no fcntl module. fcntl = import_module('fcntl') @@ -45,6 +45,12 @@ lockdata = get_lockdata() +class BadFile: + def __init__(self, fn): + self.fn = fn + def fileno(self): + return self.fn + class TestFcntl(unittest.TestCase): def setUp(self): @@ -78,24 +84,27 @@ self.f.close() def test_fcntl_bad_file(self): - class F: - def __init__(self, fn): - self.fn = fn - def fileno(self): - return self.fn - self.assertRaises(ValueError, fcntl.fcntl, -1, fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(ValueError, fcntl.fcntl, F(-1), fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(TypeError, fcntl.fcntl, 'spam', fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(TypeError, fcntl.fcntl, F('spam'), fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(-1, fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(ValueError): + fcntl.fcntl(BadFile(-1), fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(TypeError): + fcntl.fcntl('spam', fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(TypeError): + fcntl.fcntl(BadFile('spam'), fcntl.F_SETFL, os.O_NONBLOCK) + + @cpython_only + def test_fcntl_bad_file_overflow(self): + from _testcapi import INT_MAX, INT_MIN # Issue 15989 - self.assertRaises(OverflowError, fcntl.fcntl, _testcapi.INT_MAX + 1, - fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(OverflowError, fcntl.fcntl, F(_testcapi.INT_MAX + 1), - fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(OverflowError, fcntl.fcntl, _testcapi.INT_MIN - 1, - fcntl.F_SETFL, os.O_NONBLOCK) - self.assertRaises(OverflowError, fcntl.fcntl, F(_testcapi.INT_MIN - 1), - fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(OverflowError): + fcntl.fcntl(INT_MAX + 1, fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(OverflowError): + fcntl.fcntl(BadFile(INT_MAX + 1), fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(OverflowError): + fcntl.fcntl(INT_MIN - 1, fcntl.F_SETFL, os.O_NONBLOCK) + with self.assertRaises(OverflowError): + fcntl.fcntl(BadFile(INT_MIN - 1), fcntl.F_SETFL, os.O_NONBLOCK) @unittest.skipIf( platform.machine().startswith('arm') and platform.system() == 'Linux', @@ -128,6 +137,10 @@ self.assertRaises(ValueError, fcntl.flock, -1, fcntl.LOCK_SH) self.assertRaises(TypeError, fcntl.flock, 'spam', fcntl.LOCK_SH) + + @cpython_only + def test_flock_overflow(self): + import _testcapi self.assertRaises(OverflowError, fcntl.flock, _testcapi.INT_MAX+1, fcntl.LOCK_SH) diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -8,9 +8,8 @@ from array import array from weakref import proxy from functools import wraps -import _testcapi -from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd +from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd, cpython_only from collections import UserList from _io import FileIO as _FileIO @@ -362,7 +361,11 @@ if sys.platform == 'win32': import msvcrt self.assertRaises(OSError, msvcrt.get_osfhandle, make_bad_fd()) + + @cpython_only + def testInvalidFd_overflow(self): # Issue 15989 + import _testcapi self.assertRaises(TypeError, _FileIO, _testcapi.INT_MAX + 1) self.assertRaises(TypeError, _FileIO, _testcapi.INT_MIN - 1) diff --git a/Lib/test/test_finalization.py b/Lib/test/test_finalization.py --- a/Lib/test/test_finalization.py +++ b/Lib/test/test_finalization.py @@ -7,7 +7,15 @@ import unittest import weakref -import _testcapi +try: + from _testcapi import with_tp_del +except ImportError: + def with_tp_del(cls): + class C(object): + def __new__(cls, *args, **kwargs): + raise TypeError('requires _testcapi.with_tp_del') + return C + from test import support @@ -423,11 +431,11 @@ except Exception as e: self.errors.append(e) - at _testcapi.with_tp_del + at with_tp_del class Legacy(LegacyBase): pass - at _testcapi.with_tp_del + at with_tp_del class LegacyResurrector(LegacyBase): def side_effect(self): @@ -436,11 +444,12 @@ """ self.survivors.append(self) - at _testcapi.with_tp_del + at with_tp_del class LegacySelfCycle(SelfCycleBase, LegacyBase): pass + at support.cpython_only class LegacyFinalizationTest(TestBase, unittest.TestCase): """ Test finalization of objects with a tp_del. diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -332,20 +332,29 @@ self.assertIs(text % (), text) self.assertIs(text.format(), text) - @support.cpython_only def test_precision(self): - from _testcapi import INT_MAX - f = 1.2 self.assertEqual(format(f, ".0f"), "1") self.assertEqual(format(f, ".3f"), "1.200") with self.assertRaises(ValueError) as cm: - format(f, ".%sf" % (INT_MAX + 1)) + format(f, ".%sf" % (sys.maxsize + 1)) c = complex(f) self.assertEqual(format(c, ".0f"), "1+0j") self.assertEqual(format(c, ".3f"), "1.200+0.000j") with self.assertRaises(ValueError) as cm: + format(c, ".%sf" % (sys.maxsize + 1)) + + @support.cpython_only + def test_precision_c_limits(self): + from _testcapi import INT_MAX + + f = 1.2 + with self.assertRaises(ValueError) as cm: + format(f, ".%sf" % (INT_MAX + 1)) + + c = complex(f) + with self.assertRaises(ValueError) as cm: format(c, ".%sf" % (INT_MAX + 1)) diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1,7 +1,6 @@ -import _testcapi import unittest from test.support import (verbose, refcount_test, run_unittest, - strip_python_stderr) + strip_python_stderr, cpython_only) from test.script_helper import assert_python_ok, make_script, temp_dir import sys @@ -14,6 +13,15 @@ except ImportError: threading = None +try: + from _testcapi import with_tp_del +except ImportError: + def with_tp_del(cls): + class C(object): + def __new__(cls, *args, **kwargs): + raise TypeError('requires _testcapi.with_tp_del') + return C + ### Support code ############################################################################### @@ -41,7 +49,7 @@ # gc collects it. self.wr = weakref.ref(C1055820(666), it_happened) - at _testcapi.with_tp_del + at with_tp_del class Uncollectable(object): """Create a reference cycle with multiple __del__ methods. @@ -143,10 +151,11 @@ del a self.assertNotEqual(gc.collect(), 0) + @cpython_only def test_legacy_finalizer(self): # A() is uncollectable if it is part of a cycle, make sure it shows up # in gc.garbage. - @_testcapi.with_tp_del + @with_tp_del class A: def __tp_del__(self): pass class B: @@ -168,10 +177,11 @@ self.fail("didn't find obj in garbage (finalizer)") gc.garbage.remove(obj) + @cpython_only def test_legacy_finalizer_newclass(self): # A() is uncollectable if it is part of a cycle, make sure it shows up # in gc.garbage. - @_testcapi.with_tp_del + @with_tp_del class A(object): def __tp_del__(self): pass class B(object): @@ -570,6 +580,7 @@ # would be damaged, with an empty __dict__. self.assertEqual(x, None) + @cpython_only def test_garbage_at_shutdown(self): import subprocess code = """if 1: @@ -764,6 +775,7 @@ info = v[2] self.assertEqual(info["generation"], 2) + @cpython_only def test_collect_garbage(self): self.preclean() # Each of these cause four objects to be garbage: Two diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -1,5 +1,7 @@ import unittest from test import support +# Skip this test if the _testcapi module isn't available. +support.import_module('_testcapi') from _testcapi import getargs_keywords, getargs_keyword_only try: from _testcapi import getargs_L, getargs_K 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 @@ -1,4 +1,3 @@ -import _testcapi import collections import datetime import functools @@ -21,7 +20,7 @@ except ImportError: ThreadPoolExecutor = None -from test.support import run_unittest, TESTFN, DirsOnSysPath +from test.support import run_unittest, TESTFN, DirsOnSysPath, cpython_only from test.support import MISSING_C_DOCSTRINGS from test.script_helper import assert_python_ok, assert_python_failure from test import inspect_fodder as mod @@ -604,16 +603,20 @@ self.assertFullArgSpecEquals(_pickle.Pickler(io.BytesIO()).dump, args_e=['self', 'obj'], formatted='(self, obj)') + @cpython_only @unittest.skipIf(MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") def test_getfullagrspec_builtin_func(self): + import _testcapi builtin = _testcapi.docstring_with_signature_with_defaults spec = inspect.getfullargspec(builtin) self.assertEqual(spec.defaults[0], 'avocado') + @cpython_only @unittest.skipIf(MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") def test_getfullagrspec_builtin_func_no_signature(self): + import _testcapi builtin = _testcapi.docstring_no_signature with self.assertRaises(TypeError): inspect.getfullargspec(builtin) @@ -1647,9 +1650,11 @@ ('kwargs', ..., int, "var_keyword")), ...)) + @cpython_only @unittest.skipIf(MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") def test_signature_on_builtins(self): + import _testcapi def test_unbound_method(o): """Use this to test unbound methods (things that should have a self)""" @@ -1709,9 +1714,11 @@ __call__ = type test_callable(ThisWorksNow()) + @cpython_only @unittest.skipIf(MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") def test_signature_on_decorated_builtins(self): + import _testcapi func = _testcapi.docstring_with_signature_with_defaults def decorator(func): @@ -1725,7 +1732,9 @@ self.assertEqual(inspect.signature(func), inspect.signature(decorated_func)) + @cpython_only def test_signature_on_builtins_no_signature(self): + import _testcapi with self.assertRaisesRegex(ValueError, 'no signature found for builtin'): inspect.signature(_testcapi.docstring_no_signature) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -32,7 +32,6 @@ import unittest import warnings import weakref -import _testcapi from collections import deque, UserList from itertools import cycle, count from test import support @@ -1997,8 +1996,10 @@ os.environ.clear() os.environ.update(old_environ) - # Issue 15989 + @support.cpython_only def test_device_encoding(self): + # Issue 15989 + import _testcapi b = self.BytesIO() b.fileno = lambda: _testcapi.INT_MAX + 1 self.assertRaises(OverflowError, self.TextIOWrapper, b) diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py @@ -4,14 +4,13 @@ import subprocess import random import select -from _testcapi import USHRT_MAX, INT_MAX, UINT_MAX try: import threading except ImportError: threading = None import time import unittest -from test.support import TESTFN, run_unittest, reap_threads +from test.support import TESTFN, run_unittest, reap_threads, cpython_only try: select.poll @@ -163,8 +162,18 @@ # Issues #15989, #17919 self.assertRaises(OverflowError, pollster.register, 0, -1) + self.assertRaises(OverflowError, pollster.register, 0, 1 << 64) + self.assertRaises(OverflowError, pollster.modify, 1, -1) + self.assertRaises(OverflowError, pollster.modify, 1, 1 << 64) + + @cpython_only + def test_poll_c_limits(self): + from _testcapi import USHRT_MAX, INT_MAX, UINT_MAX + pollster = select.poll() + pollster.register(1) + + # Issues #15989, #17919 self.assertRaises(OverflowError, pollster.register, 0, USHRT_MAX + 1) - self.assertRaises(OverflowError, pollster.modify, 1, -1) self.assertRaises(OverflowError, pollster.modify, 1, USHRT_MAX + 1) self.assertRaises(OverflowError, pollster.poll, INT_MAX + 1) self.assertRaises(OverflowError, pollster.poll, UINT_MAX + 1) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -17,7 +17,6 @@ import tempfile import unittest import warnings -import _testcapi _DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(), support.TESTFN + '-dummy-symlink') @@ -617,7 +616,12 @@ except OSError: pass + @support.cpython_only + @unittest.skipUnless(hasattr(os, 'pipe2'), "test needs os.pipe2()") + @support.requires_linux_version(2, 6, 27) + def test_pipe2_c_limits(self): # Issue 15989 + import _testcapi self.assertRaises(OverflowError, os.pipe2, _testcapi.INT_MAX + 1) self.assertRaises(OverflowError, os.pipe2, _testcapi.UINT_MAX + 1) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -6,7 +6,6 @@ import socket import select import tempfile -import _testcapi import time import traceback import queue @@ -1344,7 +1343,10 @@ srv.listen(backlog) srv.close() + @support.cpython_only + def test_listen_backlog_overflow(self): # Issue 15989 + import _testcapi srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.bind((HOST, 0)) self.assertRaises(OverflowError, srv.listen, _testcapi.INT_MAX + 1) @@ -1738,6 +1740,14 @@ def _testShutdown(self): self.serv_conn.send(MSG) + self.serv_conn.shutdown(2) + + testShutdown_overflow = support.cpython_only(testShutdown) + + @support.cpython_only + def _testShutdown_overflow(self): + import _testcapi + self.serv_conn.send(MSG) # Issue 15989 self.assertRaises(OverflowError, self.serv_conn.shutdown, _testcapi.INT_MAX + 1) @@ -2506,7 +2516,12 @@ # code with these functions. # Match the definition in socketmodule.c - socklen_t_limit = min(0x7fffffff, _testcapi.INT_MAX) + try: + import _testcapi + except ImportError: + socklen_t_limit = 0x7fffffff + else: + socklen_t_limit = min(0x7fffffff, _testcapi.INT_MAX) @requireAttrs(socket, "CMSG_LEN") def testCMSG_LEN(self): @@ -3731,14 +3746,23 @@ pass end = time.time() self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.") - # Issue 15989 - if _testcapi.UINT_MAX < _testcapi.ULONG_MAX: - self.serv.setblocking(_testcapi.UINT_MAX + 1) - self.assertIsNone(self.serv.gettimeout()) def _testSetBlocking(self): pass + @support.cpython_only + def testSetBlocking_overflow(self): + # Issue 15989 + import _testcapi + if _testcapi.UINT_MAX >= _testcapi.ULONG_MAX: + self.skipTest('needs UINT_MAX < ULONG_MAX') + self.serv.setblocking(False) + self.assertEqual(self.serv.gettimeout(), 0.0) + self.serv.setblocking(_testcapi.UINT_MAX + 1) + self.assertIsNone(self.serv.gettimeout()) + + _testSetBlocking_overflow = support.cpython_only(_testSetBlocking) + @unittest.skipUnless(hasattr(socket, 'SOCK_NONBLOCK'), 'test needs socket.SOCK_NONBLOCK') @support.requires_linux_version(2, 6, 28) diff --git a/Lib/test/test_structmembers.py b/Lib/test/test_structmembers.py --- a/Lib/test/test_structmembers.py +++ b/Lib/test/test_structmembers.py @@ -1,3 +1,8 @@ +import unittest +from test import support + +# Skip this test if the _testcapi module isn't available. +support.import_module('_testcapi') from _testcapi import _test_structmembersType, \ CHAR_MAX, CHAR_MIN, UCHAR_MAX, \ SHRT_MAX, SHRT_MIN, USHRT_MAX, \ @@ -6,9 +11,6 @@ LLONG_MAX, LLONG_MIN, ULLONG_MAX, \ PY_SSIZE_T_MAX, PY_SSIZE_T_MIN -import unittest -from test import support - ts=_test_structmembersType(False, # T_BOOL 1, # T_BYTE 2, # T_UBYTE diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -698,6 +698,7 @@ self.assertIn(c, range(b - 50, b + 50)) + at test.support.cpython_only class SizeofTest(unittest.TestCase): def setUp(self): diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -1,7 +1,6 @@ import unittest import sys import os -import _testcapi from test import support # Skip this test if the _tkinter module wasn't built. @@ -13,6 +12,11 @@ from tkinter import Tcl from _tkinter import TclError +try: + from _testcapi import INT_MAX, PY_SSIZE_T_MAX +except ImportError: + INT_MAX = PY_SSIZE_T_MAX = sys.maxsize + tcl_version = _tkinter.TCL_VERSION.split('.') try: for i in range(len(tcl_version)): @@ -505,9 +509,9 @@ def setUp(self): self.interp = Tcl() - @unittest.skipUnless(_testcapi.INT_MAX < _testcapi.PY_SSIZE_T_MAX, - "needs UINT_MAX < SIZE_MAX") - @support.bigmemtest(size=_testcapi.INT_MAX + 1, memuse=5, dry_run=False) + @support.cpython_only + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @support.bigmemtest(size=INT_MAX + 1, memuse=5, dry_run=False) def test_huge_string(self, size): value = ' ' * size self.assertRaises(OverflowError, self.interp.call, 'set', '_', value) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -3,7 +3,7 @@ """ import test.support -from test.support import verbose, strip_python_stderr, import_module +from test.support import verbose, strip_python_stderr, import_module, cpython_only from test.script_helper import assert_python_ok import random @@ -11,7 +11,6 @@ import sys _thread = import_module('_thread') threading = import_module('threading') -import _testcapi import time import unittest import weakref @@ -662,6 +661,7 @@ self.assertRegex(err.rstrip(), b"^sys:1: ResourceWarning: unclosed file ") + @cpython_only def test_frame_tstate_tracing(self): # Issue #14432: Crash when a generator is created in a C thread that is # destroyed while the generator is still used. The issue was that a @@ -690,6 +690,7 @@ threading.settrace(noop_trace) # Create a generator in a C thread which exits after the call + import _testcapi _testcapi.call_in_temporary_c_thread(callback) # Call the generator in a different Python thread, check that the @@ -928,6 +929,7 @@ # The thread was joined properly. self.assertEqual(os.read(r, 1), b"x") + @cpython_only def test_daemon_threads_fatal_error(self): subinterp_code = r"""if 1: import os diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -582,6 +582,7 @@ -(2.0 ** 100.0), 2.0 ** 100.0, ) + @support.cpython_only def test_time_t(self): from _testcapi import pytime_object_to_time_t for obj, time_t in ( @@ -597,6 +598,7 @@ for invalid in self.invalid_values: self.assertRaises(OverflowError, pytime_object_to_time_t, invalid) + @support.cpython_only def test_timeval(self): from _testcapi import pytime_object_to_timeval for obj, timeval in ( @@ -616,6 +618,7 @@ for invalid in self.invalid_values: self.assertRaises(OverflowError, pytime_object_to_timeval, invalid) + @support.cpython_only def test_timespec(self): from _testcapi import pytime_object_to_timespec for obj, timespec in ( diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1,12 +1,11 @@ """Test cases for traceback module""" -from _testcapi import traceback_print, exception_print from io import StringIO import sys import unittest import re from test.support import run_unittest, Error, captured_output -from test.support import TESTFN, unlink +from test.support import TESTFN, unlink, cpython_only import traceback @@ -176,7 +175,9 @@ def some_exception(self): raise KeyError('blah') + @cpython_only def check_traceback_format(self, cleanup_func=None): + from _testcapi import traceback_print try: self.some_exception() except KeyError: @@ -404,7 +405,9 @@ # This checks built-in reporting by the interpreter. # + @cpython_only def get_report(self, e): + from _testcapi import exception_print e = self.get_exception(e) with captured_output("stderr") as s: exception_print(e) diff --git a/Lib/test/test_ucn.py b/Lib/test/test_ucn.py --- a/Lib/test/test_ucn.py +++ b/Lib/test/test_ucn.py @@ -9,12 +9,16 @@ import unittest import unicodedata -import _testcapi from test import support from http.client import HTTPException from test.test_normalization import check_version +try: + from _testcapi import INT_MAX, PY_SSIZE_T_MAX, UINT_MAX +except ImportError: + INT_MAX = PY_SSIZE_T_MAX = UINT_MAX = 2**64 - 1 + class UnicodeNamesTest(unittest.TestCase): def checkletter(self, name, code): @@ -216,15 +220,13 @@ str, b"\\NSPACE", 'unicode-escape', 'strict' ) - @unittest.skipUnless(_testcapi.INT_MAX < _testcapi.PY_SSIZE_T_MAX, - "needs UINT_MAX < SIZE_MAX") - @support.bigmemtest(size=_testcapi.UINT_MAX + 1, - memuse=2 + 1, dry_run=False) + @support.cpython_only + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @support.bigmemtest(size=UINT_MAX + 1, memuse=2 + 1, dry_run=False) def test_issue16335(self, size): # very very long bogus character name - x = b'\\N{SPACE' + b'x' * (_testcapi.UINT_MAX + 1) + b'}' - self.assertEqual(len(x), len(b'\\N{SPACE}') + - (_testcapi.UINT_MAX + 1)) + x = b'\\N{SPACE' + b'x' * (UINT_MAX + 1) + b'}' + self.assertEqual(len(x), len(b'\\N{SPACE}') + (UINT_MAX + 1)) self.assertRaisesRegex(UnicodeError, 'unknown Unicode character name', x.decode, 'unicode-escape' diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -1187,8 +1187,13 @@ self.assertEqual('...%(foo)f...' % {'foo':Float.PI,'def':123}, '...3.141593...') + def test_formatting_huge_precision(self): + format_string = "%.{}f".format(sys.maxsize + 1) + with self.assertRaises(ValueError): + result = format_string % 2.34 + @support.cpython_only - def test_formatting_huge_precision(self): + def test_formatting_huge_precision_c_limits(self): from _testcapi import INT_MAX format_string = "%.{}f".format(INT_MAX + 1) with self.assertRaises(ValueError): @@ -2315,6 +2320,7 @@ b'%.%s', b'abc') # Test PyUnicode_AsWideChar() + @support.cpython_only def test_aswidechar(self): from _testcapi import unicode_aswidechar support.import_module('ctypes') @@ -2352,6 +2358,7 @@ self.assertEqual(wchar, nonbmp + '\0') # Test PyUnicode_AsWideCharString() + @support.cpython_only def test_aswidecharstring(self): from _testcapi import unicode_aswidecharstring support.import_module('ctypes') @@ -2386,6 +2393,7 @@ s += "4" self.assertEqual(s, "3") + @support.cpython_only def test_encode_decimal(self): from _testcapi import unicode_encodedecimal self.assertEqual(unicode_encodedecimal('123'), @@ -2401,6 +2409,7 @@ "^'decimal' codec can't encode character", unicode_encodedecimal, "123\u20ac", "replace") + @support.cpython_only def test_transform_decimal(self): from _testcapi import unicode_transformdecimaltoascii as transform_decimal self.assertEqual(transform_decimal('123'), diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -125,6 +125,8 @@ Tests ----- +- Issue #20532: Tests which use _testcapi now are marked as CPython only. + - Issue #19920: Added tests for TarFile.list(). Based on patch by Vajrasky Kok. - Issue #19990: Added tests for the imghdr module. Based on patch by -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 13:28:34 2014 From: python-checkins at python.org (nick.coghlan) Date: Fri, 7 Feb 2014 13:28:34 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320053=3A_Actually?= =?utf-8?q?_test_relevant_assumption?= Message-ID: <3fLG5G70cXzQPM@mail.python.org> http://hg.python.org/cpython/rev/17bea44a9fa7 changeset: 89010:17bea44a9fa7 user: Nick Coghlan date: Fri Feb 07 22:28:18 2014 +1000 summary: Issue #20053: Actually test relevant assumption files: Lib/test/test_venv.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -291,7 +291,7 @@ def test_devnull_exists_and_is_empty(self): # Fix for issue #20053 uses os.devnull to force a config file to # appear empty. Make sure that assumption is valid cross platform. - self.assertTrue(os.path.exists, os.devnull) + self.assertTrue(os.path.exists(os.devnull)) with open(os.devnull, "rb") as f: self.assertEqual(f.read(), b"") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 14:35:03 2014 From: python-checkins at python.org (nick.coghlan) Date: Fri, 7 Feb 2014 14:35:03 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_20542=3A_Temporarily?= =?utf-8?q?_skip_failing_test?= Message-ID: <3fLHYz5L9Lz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/2e7c46718b83 changeset: 89011:2e7c46718b83 user: Nick Coghlan date: Fri Feb 07 23:34:41 2014 +1000 summary: Issue 20542: Temporarily skip failing test files: Lib/test/test_codecs.py | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -124,6 +124,8 @@ "".join(codecs.iterdecode([bytes([c]) for c in encoded], self.encoding)) ) + # Temporary skip, see http://bugs.python.org/issue20542 + @unittest.skip def test_readline(self): def getreader(input): stream = io.BytesIO(input.encode(self.encoding)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 14:46:50 2014 From: python-checkins at python.org (nick.coghlan) Date: Fri, 7 Feb 2014 14:46:50 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320053=3A_Mark_as_?= =?utf-8?q?an_expected_failure_for_3=2E4?= Message-ID: <3fLHqZ5RSxz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/f3f92d55f942 changeset: 89012:f3f92d55f942 user: Nick Coghlan date: Fri Feb 07 23:46:38 2014 +1000 summary: Issue #20053: Mark as an expected failure for 3.4 files: Lib/test/test_venv.py | 14 ++++++++++++-- 1 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -28,6 +28,12 @@ skipInVenv = unittest.skipIf(sys.prefix != sys.base_prefix, 'Test not appropriate in a venv') +# os.path.exists('nul') is False: http://bugs.python.org/issue20541 +if os.devnull.lower() == 'nul': + failsOnWindows = unittest.expectedFailure +else: + def failsOnWindows(f): + return f class BaseTest(unittest.TestCase): """Base class for venv tests.""" @@ -288,9 +294,12 @@ self.run_with_capture(venv.create, self.env_dir, with_pip=False) self.assert_pip_not_installed() + @failsOnWindows def test_devnull_exists_and_is_empty(self): # Fix for issue #20053 uses os.devnull to force a config file to - # appear empty. Make sure that assumption is valid cross platform. + # appear empty. However http://bugs.python.org/issue20541 means + # that doesn't currently work properly on Windows. Once that is + # fixed, the "win_location" part of test_with_pip should be restored self.assertTrue(os.path.exists(os.devnull)) with open(os.devnull, "rb") as f: self.assertEqual(f.read(), b"") @@ -319,7 +328,8 @@ # cross-platform variation in test code behaviour win_location = ("pip", "pip.ini") posix_location = (".pip", "pip.conf") - for dirname, fname in (win_location, posix_location): + # Skips win_location due to http://bugs.python.org/issue20541 + for dirname, fname in (posix_location,): dirpath = os.path.join(home_dir, dirname) os.mkdir(dirpath) fpath = os.path.join(dirpath, fname) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 16:44:48 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 16:44:48 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2320476=3A_use_EmailMessa?= =?utf-8?q?ge_as_factory_if_non-compat32_policy_is_used=2E?= Message-ID: <3fLLRh6wpRz7LkD@mail.python.org> http://hg.python.org/cpython/rev/77082b818676 changeset: 89013:77082b818676 user: R David Murray date: Fri Feb 07 10:44:16 2014 -0500 summary: #20476: use EmailMessage as factory if non-compat32 policy is used. In 3.5 I will fix this right by adding a message_factory attribute to the policy. files: Lib/email/feedparser.py | 25 +++++++++++----- Lib/email/parser.py | 2 +- Lib/test/test_email/test_message.py | 16 ++++++++++- Misc/NEWS | 4 ++ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py --- a/Lib/email/feedparser.py +++ b/Lib/email/feedparser.py @@ -126,7 +126,7 @@ class FeedParser: """A feed-style parser of email.""" - def __init__(self, _factory=message.Message, *, policy=compat32): + def __init__(self, _factory=None, *, policy=compat32): """_factory is called with no arguments to create a new message obj The policy keyword specifies a policy object that controls a number of @@ -134,14 +134,23 @@ backward compatibility. """ - self._factory = _factory self.policy = policy - try: - _factory(policy=self.policy) - self._factory_kwds = lambda: {'policy': self.policy} - except TypeError: - # Assume this is an old-style factory - self._factory_kwds = lambda: {} + self._factory_kwds = lambda: {'policy': self.policy} + if _factory is None: + # What this should be: + #self._factory = policy.default_message_factory + # but, because we are post 3.4 feature freeze, fix with temp hack: + if self.policy is compat32: + self._factory = message.Message + else: + self._factory = message.EmailMessage + else: + self._factory = _factory + try: + _factory(policy=self.policy) + except TypeError: + # Assume this is an old-style factory + self._factory_kwds = lambda: {} self._input = BufferedSubFile() self._msgstack = [] self._parse = self._parsegen().__next__ diff --git a/Lib/email/parser.py b/Lib/email/parser.py --- a/Lib/email/parser.py +++ b/Lib/email/parser.py @@ -17,7 +17,7 @@ class Parser: - def __init__(self, _class=Message, *, policy=compat32): + def __init__(self, _class=None, *, policy=compat32): """Parser of RFC 2822 and MIME email messages. Creates an in-memory object tree representing the email message, which diff --git a/Lib/test/test_email/test_message.py b/Lib/test/test_email/test_message.py --- a/Lib/test/test_email/test_message.py +++ b/Lib/test/test_email/test_message.py @@ -1,6 +1,6 @@ import unittest import textwrap -from email import policy +from email import policy, message_from_string from email.message import EmailMessage, MIMEPart from test.test_email import TestEmailBase, parameterize @@ -20,6 +20,20 @@ with self.assertRaises(ValueError): m['To'] = 'xyz at abc' + def test_rfc2043_auto_decoded_and_emailmessage_used(self): + m = message_from_string(textwrap.dedent("""\ + Subject: Ayons asperges pour le =?utf-8?q?d=C3=A9jeuner?= + From: =?utf-8?q?Pep=C3=A9?= Le Pew + To: "Penelope Pussycat" <"penelope at example.com"> + MIME-Version: 1.0 + Content-Type: text/plain; charset="utf-8" + + sample text + """), policy=policy.default) + self.assertEqual(m['subject'], "Ayons asperges pour le d?jeuner") + self.assertEqual(m['from'], "Pep? Le Pew ") + self.assertIsInstance(m, EmailMessage) + @parameterize class TestEmailMessageBase: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,10 @@ Library ------- +- Issue #20476: If a non-compat32 policy is used with any of the email parsers, + EmailMessage is now used as the factory class. The factory class should + really come from the policy; that will get fixed in 3.5. + - Issue #19920: TarFile.list() no longer fails when outputs a listing containing non-encodable characters. Based on patch by Vajrasky Kok. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 16:55:33 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 16:55:33 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2320477=3A_add_examples_o?= =?utf-8?q?f_using_the_new_contentmanager_API=2E?= Message-ID: <3fLLh55nTnz7LjN@mail.python.org> http://hg.python.org/cpython/rev/a9d7d53d5fbd changeset: 89014:a9d7d53d5fbd user: R David Murray date: Fri Feb 07 10:55:17 2014 -0500 summary: #20477: add examples of using the new contentmanager API. files: Doc/includes/email-alternative-new-api.py | 56 +++++++ Doc/includes/email-read-alternative-new-api.py | 74 ++++++++++ Doc/library/email-examples.rst | 28 +++ 3 files changed, 158 insertions(+), 0 deletions(-) diff --git a/Doc/includes/email-alternative-new-api.py b/Doc/includes/email-alternative-new-api.py new file mode 100644 --- /dev/null +++ b/Doc/includes/email-alternative-new-api.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +import smtplib + +from email.message import EmailMessage +from email.headerregistry import Address +from email.utils import make_msgid + +# Create the base text message. +msg = EmailMessage() +msg['Subject'] = "Ayons asperges pour le d?jeuner" +msg['From'] = Address("Pep? Le Pew", "pepe at example.com") +msg['To'] = (Address("Penelope Pussycat", "penelope at example.com"), + Address("Fabrette Pussycat", "fabrette at example.com")) +msg.set_content("""\ +Salut! + +Cela ressemble ? un excellent recipie[1] d?jeuner. + +[1] http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718 + +--?ric +""") + +# Add the html version. This converts the message into a multipart/alternative +# container, with the original text message as the first part and the new html +# message as the second part. +asparagus_cid = make_msgid() +msg.add_alternative("""\ + + + +

Salut!<\p> +

Cela ressemble ? un excellent + + + +""".format(asparagus_cid=asparagus_cid[1:-1]), subtype='html') +# note that we needed to peel the <> off the msgid for use in the html. + +# Now add the related image to the html part. +with open("roasted-asparagus.jpg", 'rb') as img: + msg.get_payload()[1].add_related(img.read(), 'image', 'jpeg', + cid=asparagus_cid) + +# Make a local copy of what we are going to send. +with open('outgoing.msg', 'wb') as f: + f.write(bytes(msg)) + +# Send the message via local SMTP server. +with smtplib.SMTP('localhost') as s: + s.send_message(msg) diff --git a/Doc/includes/email-read-alternative-new-api.py b/Doc/includes/email-read-alternative-new-api.py new file mode 100644 --- /dev/null +++ b/Doc/includes/email-read-alternative-new-api.py @@ -0,0 +1,74 @@ +import os +import sys +import tempfile +import mimetypes +import webbrowser + +# Import the email modules we'll need +from email import policy +from email.parser import BytesParser + +# An imaginary module that would make this work and be safe. +from imaginary import magic_html_parser + +# In a real program you'd get the filename from the arguments. +msg = BytesParser(policy=policy.default).parse(open('outgoing.msg', 'rb')) + +# Now the header items can be accessed as a dictionary, and any non-ASCII will +# be converted to unicode: +print('To:', msg['to']) +print('From:', msg['from']) +print('Subject:', msg['subject']) + +# If we want to print a priview of the message content, we can extract whatever +# the least formatted payload is and print the first three lines. Of course, +# if the message has no plain text part printing the first three lines of html +# is probably useless, but this is just a conceptual example. +simplest = msg.get_body(preferencelist=('plain', 'html')) +print() +print(''.join(simplest.get_content().splitlines(keepends=True)[:3])) + +ans = input("View full message?") +if ans.lower()[0] == 'n': + sys.exit() + +# We can extract the richest alternative in order to display it: +richest = msg.get_body() +partfiles = {} +if richest['content-type'].maintype == 'text': + if richest['content-type'].subtype == 'plain': + for line in richest.get_content().splitlines(): + print(line) + sys.exit() + elif richest['content-type'].subtype == 'html': + body = richest + else: + print("Don't know how to display {}".format(richest.get_content_type())) + sys.exit() +elif richest['content-type'].content_type == 'multipart/related': + body = richest.get_body(preferencelist=('html')) + for part in richest.iter_attachments(): + fn = part.get_filename() + if fn: + extension = os.path.splitext(part.get_filename())[1] + else: + extension = mimetypes.guess_extension(part.get_content_type()) + with tempfile.NamedTemporaryFile(suffix=extension, delete=False) as f: + f.write(part.get_content()) + # again strip the <> to go from email form of cid to html form. + partfiles[part['content-id'][1:-1]] = f.name +else: + print("Don't know how to display {}".format(richest.get_content_type())) + sys.exit() +with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + # The magic_html_parser has to rewrite the href="cid:...." attributes to + # point to the filenames in partfiles. It also has to do a safety-sanitize + # of the html. It could be written using html.parser. + f.write(magic_html_parser(body.get_content(), partfiles)) +webbrowser.open(f.name) +os.remove(f.name) +for fn in partfiles.values(): + os.remove(fn) + +# Of course, there are lots of email messages that could break this simple +# minded program, but it will handle the most common ones. diff --git a/Doc/library/email-examples.rst b/Doc/library/email-examples.rst --- a/Doc/library/email-examples.rst +++ b/Doc/library/email-examples.rst @@ -40,6 +40,34 @@ .. literalinclude:: ../includes/email-alternative.py +Examples using the Provision API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Here is a reworking of the last example using the provisional API. To make +things a bit more interesting, we include a related image in the html part, and +we save a copy of what we are going to send to disk, as well as sending it. + +This example also shows how easy it is to include non-ASCII, and simplifies the +sending of the message using the :meth:`.send_message` method of the +:mod:`smtplib` module. + +.. literalinclude:: ../includes/email-alternative-new-api.py + +If we were instead sent the message from the last example, here is one +way we could process it: + +.. literalinclude:: ../includes/email-read-alternative-new-api.py + +Up to the prompt, the output from the above is:: + + To: Penelope Pussycat <"penelope at example.com">, Fabrette Pussycat <"fabrette at example.com"> + From: Pep? Le Pew + Subject: Ayons asperges pour le d?jeuner + + Salut! + + Cela ressemble ? un excellent recipie[1] d?jeuner. + + .. rubric:: Footnotes .. [1] Thanks to Matthew Dixon Cowles for the original inspiration and examples. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 17:57:59 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 7 Feb 2014 17:57:59 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320505=3A_add_debu?= =?utf-8?q?g_info?= Message-ID: <3fLN473D7vz7Lk9@mail.python.org> http://hg.python.org/cpython/rev/3b94a4ef244e changeset: 89015:3b94a4ef244e user: Victor Stinner date: Fri Feb 07 17:53:13 2014 +0100 summary: Issue #20505: add debug info files: Lib/asyncio/base_events.py | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -634,7 +634,17 @@ else: logger.log(level, 'poll took %.3f seconds', t1-t0) else: + t0 = self.time() event_list = self._selector.select(timeout) + dt = self.time() - t0 + if timeout and not event_list and dt < timeout: + print("%s.select(%.3f ms) took %.3f ms (granularity=%.3f ms, resolution=%.3f ms)" + % (self._selector.__class__.__name__, + timeout * 1e3, + dt * 1e3, + self._granularity * 1e3, + self._selector.resolution * 1e3), + file=sys.__stderr__) self._process_events(event_list) # Handle 'later' callbacks that are ready. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 19:03:17 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 7 Feb 2014 19:03:17 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_doc=3A_mention_tha?= =?utf-8?q?t_asyncio_is_not_thread-safe?= Message-ID: <3fLPWT6CFRz7LkC@mail.python.org> http://hg.python.org/cpython/rev/86bc4194f986 changeset: 89016:86bc4194f986 user: Victor Stinner date: Fri Feb 07 19:03:05 2014 +0100 summary: asyncio doc: mention that asyncio is not thread-safe files: Doc/library/asyncio-dev.rst | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -23,6 +23,12 @@ loop.call_soon_threadsafe(asyncio.async, coro_func()) +Most asyncio objects are not thread safe. You should only worry if you access +objects outside the event loop. For example, to cancel a future, don't call +directly its :meth:`Future.cancel` method, but:: + + loop.call_soon_threadsafe(fut.cancel) + To handle signals and to execute subprocesses, the event loop must be run in the main thread. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 19:06:36 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 19:06:36 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE5MDYzOiB0aGUg?= =?utf-8?q?unicode-in-set=5Fpayload_problem_isn=27t_getting_fixed_in_3=2E4?= =?utf-8?q?=2E?= Message-ID: <3fLPbJ3cWWz7LkV@mail.python.org> http://hg.python.org/cpython/rev/4daf3cec9419 changeset: 89017:4daf3cec9419 branch: 3.3 parent: 89008:e5a78f7c2dcb user: R David Murray date: Fri Feb 07 13:03:08 2014 -0500 summary: #19063: the unicode-in-set_payload problem isn't getting fixed in 3.4. files: Lib/email/message.py | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/email/message.py b/Lib/email/message.py --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -277,8 +277,6 @@ """ if hasattr(payload, 'encode'): if charset is None: - # We should check for ASCII-only here, but we can't do that - # for backward compatibility reasons. Fixed in 3.4. self._payload = payload return if not isinstance(charset, Charset): @@ -326,8 +324,9 @@ try: cte(self) except TypeError: - # This if is for backward compatibility and will be removed - # in 3.4 when the ascii check is added to set_payload. + # This 'if' is for backward compatibility, it allows unicode + # through even though that won't work correctly if the + # message is serialized. payload = self._payload if payload: try: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 19:06:37 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 19:06:37 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2320531=3A_Revert_e20f98a?= =?utf-8?q?8ed71=2C_the_3=2E4_version_of_the_=2319063_fix=2E?= Message-ID: <3fLPbK6gYXz7Lm3@mail.python.org> http://hg.python.org/cpython/rev/f942f1eddfea changeset: 89018:f942f1eddfea parent: 89016:86bc4194f986 user: R David Murray date: Fri Feb 07 12:40:37 2014 -0500 summary: #20531: Revert e20f98a8ed71, the 3.4 version of the #19063 fix. files: Doc/library/email.message.rst | 8 +- Lib/email/charset.py | 29 +++-- Lib/email/message.py | 22 +--- Lib/test/test_email/test_contentmanager.py | 5 +- Lib/test/test_email/test_email.py | 52 ++------- 5 files changed, 38 insertions(+), 78 deletions(-) diff --git a/Doc/library/email.message.rst b/Doc/library/email.message.rst --- a/Doc/library/email.message.rst +++ b/Doc/library/email.message.rst @@ -196,13 +196,7 @@ Set the entire message object's payload to *payload*. It is the client's responsibility to ensure the payload invariants. Optional *charset* sets - the message's character set; see :meth:`set_charset` for details. If - *payload* is a string containing non-ASCII characters, *charset* is - required. - - .. versionchanged:: 3.4 - Previous to 3.4 *charset* was not required when *payload* was a - non-ASCII string, but omitting it produced nonsense results. + the message's default character set; see :meth:`set_charset` for details. .. method:: set_charset(charset) diff --git a/Lib/email/charset.py b/Lib/email/charset.py --- a/Lib/email/charset.py +++ b/Lib/email/charset.py @@ -378,19 +378,18 @@ return None def body_encode(self, string): - """Body-encode a string, converting it first to bytes if needed. + """Body-encode a string by converting it first to bytes. The type of encoding (base64 or quoted-printable) will be based on - self.body_encoding. If body_encoding is None, we perform no CTE - encoding (the CTE will be either 7bit or 8bit), we just encode the - binary representation to ascii using the surrogateescape error handler, - which will enable the Generators to produce the correct output. + self.body_encoding. If body_encoding is None, we assume the + output charset is a 7bit encoding, so re-encoding the decoded + string using the ascii codec produces the correct string version + of the content. """ - if not string: - return string - if isinstance(string, str): - string = string.encode(self.output_charset) + # 7bit/8bit encodings return the string unchanged (module conversions) if self.body_encoding is BASE64: + if isinstance(string, str): + string = string.encode(self.output_charset) return email.base64mime.body_encode(string) elif self.body_encoding is QP: # quopromime.body_encode takes a string, but operates on it as if @@ -399,7 +398,15 @@ # character set, then, we must turn it into pseudo bytes via the # latin1 charset, which will encode any byte as a single code point # between 0 and 255, which is what body_encode is expecting. - string = string.decode('latin1') + # + # Note that this clause doesn't handle the case of a _payload that + # is already bytes. It never did, and the semantics of _payload + # being bytes has never been nailed down, so fixing that is a + # longer term TODO. + if isinstance(string, str): + string = string.encode(self.output_charset).decode('latin1') return email.quoprimime.body_encode(string) else: - return string.decode('ascii', 'surrogateescape') + if isinstance(string, str): + string = string.encode(self.output_charset).decode('ascii') + return string diff --git a/Lib/email/message.py b/Lib/email/message.py --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -301,23 +301,9 @@ Optional charset sets the message's default character set. See set_charset() for details. """ - if hasattr(payload, 'encode'): - if charset is None: - try: - payload.encode('ascii', 'surrogateescape') - except UnicodeError: - raise TypeError("charset argument must be specified" - " when non-ASCII characters are used in the" - " payload") from None - self._payload = payload - return - if not isinstance(charset, Charset): - charset = Charset(charset) - payload = payload.encode(charset.output_charset) - if hasattr(payload, 'decode'): - self._payload = payload.decode('ascii', 'surrogateescape') - else: - self._payload = payload + if isinstance(payload, bytes): + payload = payload.decode('ascii', 'surrogateescape') + self._payload = payload if charset is not None: self.set_charset(charset) @@ -356,7 +342,7 @@ try: cte(self) except TypeError: - self._payload = charset.body_encode(self.get_payload(decode=True)) + self._payload = charset.body_encode(self._payload) self.add_header('Content-Transfer-Encoding', cte) def get_charset(self): diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -208,11 +208,12 @@ "Bas?c t?xt.\n") def test_get_text_plain_utf8_base64_recoverable_bad_CTE_data(self): - m = self._bytes_msg(textwrap.dedent("""\ + m = self._str_msg(textwrap.dedent("""\ Content-Type: text/plain; charset="utf8" Content-Transfer-Encoding: base64 - QmFzw6xjIHTDq3h0Lgo""").encode('ascii') + b'\xFF=\n') + QmFzw6xjIHTDq3h0Lgo\xFF= + """)) self.assertEqual(raw_data_manager.get_content(m, errors='ignore'), "Bas?c t?xt.\n") diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -92,44 +92,6 @@ msg.set_payload('This is a string payload', charset) self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1') - def test_set_payload_with_8bit_data_and_charset(self): - data = b'\xd0\x90\xd0\x91\xd0\x92' - charset = Charset('utf-8') - msg = Message() - msg.set_payload(data, charset) - self.assertEqual(msg['content-transfer-encoding'], 'base64') - self.assertEqual(msg.get_payload(decode=True), data) - self.assertEqual(msg.get_payload(), '0JDQkdCS\n') - - def test_set_payload_with_non_ascii_and_charset_body_encoding_none(self): - data = b'\xd0\x90\xd0\x91\xd0\x92' - charset = Charset('utf-8') - charset.body_encoding = None # Disable base64 encoding - msg = Message() - msg.set_payload(data.decode('utf-8'), charset) - self.assertEqual(msg['content-transfer-encoding'], '8bit') - self.assertEqual(msg.get_payload(decode=True), data) - - def test_set_payload_with_8bit_data_and_charset_body_encoding_none(self): - data = b'\xd0\x90\xd0\x91\xd0\x92' - charset = Charset('utf-8') - charset.body_encoding = None # Disable base64 encoding - msg = Message() - msg.set_payload(data, charset) - self.assertEqual(msg['content-transfer-encoding'], '8bit') - self.assertEqual(msg.get_payload(decode=True), data) - - def test_set_payload_to_list(self): - msg = Message() - msg.set_payload([]) - self.assertEqual(msg.get_payload(), []) - - def test_set_payload_with_non_ascii_and_no_charset_raises(self): - data = b'\xd0\x90\xd0\x91\xd0\x92'.decode('utf-8') - msg = Message() - with self.assertRaises(TypeError): - msg.set_payload(data) - def test_get_charsets(self): eq = self.assertEqual @@ -596,10 +558,20 @@ self.assertIsInstance(msg.defects[0], errors.InvalidBase64CharactersDefect) + def test_broken_unicode_payload(self): + # This test improves coverage but is not a compliance test. + # The behavior in this situation is currently undefined by the API. + x = 'this is a br\xf6ken thing to do' + msg = Message() + msg['content-type'] = 'text/plain' + msg['content-transfer-encoding'] = '8bit' + msg.set_payload(x) + self.assertEqual(msg.get_payload(decode=True), + bytes(x, 'raw-unicode-escape')) + def test_questionable_bytes_payload(self): # This test improves coverage but is not a compliance test, - # since it involves poking inside the black box in a way - # that actually breaks the model invariants. + # since it involves poking inside the black box. x = 'this is a qu?stionable thing to do'.encode('utf-8') msg = Message() msg['content-type'] = 'text/plain; charset="utf-8"' -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 19:06:39 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 19:06:39 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2320531=3A_Apply_the_3=2E?= =?utf-8?q?3_version_of_the_=2319063_fix=2E?= Message-ID: <3fLPbM2HnYz7Lm9@mail.python.org> http://hg.python.org/cpython/rev/ef8aaace85ca changeset: 89019:ef8aaace85ca user: R David Murray date: Fri Feb 07 12:46:17 2014 -0500 summary: #20531: Apply the 3.3 version of the #19063 fix. So passing unicode to set_payload works again (but still doesn't do what you want when the message is serialized). files: Lib/email/charset.py | 11 ++---- Lib/email/message.py | 26 +++++++++++++-- Lib/test/test_email/test_email.py | 32 +++++++++++++++++++ Misc/NEWS | 4 ++ 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/Lib/email/charset.py b/Lib/email/charset.py --- a/Lib/email/charset.py +++ b/Lib/email/charset.py @@ -386,7 +386,8 @@ string using the ascii codec produces the correct string version of the content. """ - # 7bit/8bit encodings return the string unchanged (module conversions) + if not string: + return string if self.body_encoding is BASE64: if isinstance(string, str): string = string.encode(self.output_charset) @@ -398,13 +399,9 @@ # character set, then, we must turn it into pseudo bytes via the # latin1 charset, which will encode any byte as a single code point # between 0 and 255, which is what body_encode is expecting. - # - # Note that this clause doesn't handle the case of a _payload that - # is already bytes. It never did, and the semantics of _payload - # being bytes has never been nailed down, so fixing that is a - # longer term TODO. if isinstance(string, str): - string = string.encode(self.output_charset).decode('latin1') + string = string.encode(self.output_charset) + string = string.decode('latin1') return email.quoprimime.body_encode(string) else: if isinstance(string, str): diff --git a/Lib/email/message.py b/Lib/email/message.py --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -301,9 +301,19 @@ Optional charset sets the message's default character set. See set_charset() for details. """ - if isinstance(payload, bytes): - payload = payload.decode('ascii', 'surrogateescape') - self._payload = payload + if hasattr(payload, 'encode'): + if charset is None: + # We should check for ASCII-only here, but we can't do that + # for backward compatibility reasons. Fixed in 3.4. + self._payload = payload + return + if not isinstance(charset, Charset): + charset = Charset(charset) + payload = payload.encode(charset.output_charset) + if hasattr(payload, 'decode'): + self._payload = payload.decode('ascii', 'surrogateescape') + else: + self._payload = payload if charset is not None: self.set_charset(charset) @@ -342,7 +352,15 @@ try: cte(self) except TypeError: - self._payload = charset.body_encode(self._payload) + # This if is for backward compatibility and will be removed + # in 3.4 when the ascii check is added to set_payload. + payload = self._payload + if payload: + try: + payload = payload.encode('ascii', 'surrogateescape') + except UnicodeError: + payload = payload.encode(charset.output_charset) + self._payload = charset.body_encode(payload) self.add_header('Content-Transfer-Encoding', cte) def get_charset(self): diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -92,6 +92,38 @@ msg.set_payload('This is a string payload', charset) self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1') + def test_set_payload_with_8bit_data_and_charset(self): + data = b'\xd0\x90\xd0\x91\xd0\x92' + charset = Charset('utf-8') + msg = Message() + msg.set_payload(data, charset) + self.assertEqual(msg['content-transfer-encoding'], 'base64') + self.assertEqual(msg.get_payload(decode=True), data) + self.assertEqual(msg.get_payload(), '0JDQkdCS\n') + + def test_set_payload_with_non_ascii_and_charset_body_encoding_none(self): + data = b'\xd0\x90\xd0\x91\xd0\x92' + charset = Charset('utf-8') + charset.body_encoding = None # Disable base64 encoding + msg = Message() + msg.set_payload(data.decode('utf-8'), charset) + self.assertEqual(msg['content-transfer-encoding'], '8bit') + self.assertEqual(msg.get_payload(decode=True), data) + + def test_set_payload_with_8bit_data_and_charset_body_encoding_none(self): + data = b'\xd0\x90\xd0\x91\xd0\x92' + charset = Charset('utf-8') + charset.body_encoding = None # Disable base64 encoding + msg = Message() + msg.set_payload(data, charset) + self.assertEqual(msg['content-transfer-encoding'], '8bit') + self.assertEqual(msg.get_payload(decode=True), data) + + def test_set_payload_to_list(self): + msg = Message() + msg.set_payload([]) + self.assertEqual(msg.get_payload(), []) + def test_get_charsets(self): eq = self.assertEqual diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,10 @@ Library ------- +- Issue #20531: Revert 3.4 version of fix for #19063, and apply the 3.3 + version. That is, do *not* raise an error if unicode is passed to + email.message.Message.set_payload. + - Issue #20476: If a non-compat32 policy is used with any of the email parsers, EmailMessage is now used as the factory class. The factory class should really come from the policy; that will get fixed in 3.5. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 19:06:40 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 19:06:40 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge=3A_=2319063=3A_the_unicode-in-set=5Fpayload_proble?= =?utf-8?q?m_isn=27t_getting_fixed_in_3=2E4=2E?= Message-ID: <3fLPbN3nkjz7LmK@mail.python.org> http://hg.python.org/cpython/rev/aab7258a31d3 changeset: 89020:aab7258a31d3 parent: 89019:ef8aaace85ca parent: 89017:4daf3cec9419 user: R David Murray date: Fri Feb 07 13:04:18 2014 -0500 summary: Merge: #19063: the unicode-in-set_payload problem isn't getting fixed in 3.4. files: Lib/email/message.py | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/email/message.py b/Lib/email/message.py --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -303,8 +303,6 @@ """ if hasattr(payload, 'encode'): if charset is None: - # We should check for ASCII-only here, but we can't do that - # for backward compatibility reasons. Fixed in 3.4. self._payload = payload return if not isinstance(charset, Charset): @@ -352,8 +350,9 @@ try: cte(self) except TypeError: - # This if is for backward compatibility and will be removed - # in 3.4 when the ascii check is added to set_payload. + # This 'if' is for backward compatibility, it allows unicode + # through even though that won't work correctly if the + # message is serialized. payload = self._payload if payload: try: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 19:45:45 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 19:45:45 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2320013=3A_don=27t_raise_?= =?utf-8?q?socket_error_when_selected_mailbox_deleted=2E?= Message-ID: <3fLQST0Lc3z7Ljv@mail.python.org> http://hg.python.org/cpython/rev/a24f7d195b8f changeset: 89021:a24f7d195b8f user: R David Murray date: Fri Feb 07 13:44:57 2014 -0500 summary: #20013: don't raise socket error when selected mailbox deleted. I'm checking this in without a test because not much of this code is tested and I don't have time to work up the necessary extensions to the existing test framework. The patch itself was tested by the person who reported the bug. files: Lib/imaplib.py | 5 +++++ Misc/NEWS | 4 ++++ 2 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Lib/imaplib.py b/Lib/imaplib.py --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -1063,6 +1063,11 @@ del self.tagged_commands[tag] return result + # If we've seen a BYE at this point, the socket will be + # closed, so report the BYE now. + + self._check_bye() + # Some have reported "unexpected response" exceptions. # Note that ignoring them here causes loops. # Instead, send me details of the unexpected response and diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,10 @@ Library ------- +- Issue #20013: Some imap servers disconnect if the current mailbox is + deleted, and imaplib did not handle that case gracefully. Now it + handles the 'bye' correctly. + - Issue #20531: Revert 3.4 version of fix for #19063, and apply the 3.3 version. That is, do *not* raise an error if unicode is passed to email.message.Message.set_payload. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 19:51:55 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 19:51:55 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzIwMDEzOiBkb24n?= =?utf-8?q?t_raise_socket_error_when_selected_mailbox_deleted=2E?= Message-ID: <3fLQbb2HJcz7LjN@mail.python.org> http://hg.python.org/cpython/rev/aecc0a4be052 changeset: 89022:aecc0a4be052 branch: 3.3 parent: 89017:4daf3cec9419 user: R David Murray date: Fri Feb 07 13:47:40 2014 -0500 summary: #20013: don't raise socket error when selected mailbox deleted. I'm checking this in without a test because not much of this code is tested and I don't have time to work up the necessary extensions to the existing test framework. The patch itself was tested by the person who reported the bug. files: Lib/imaplib.py | 5 +++++ Misc/NEWS | 4 ++++ 2 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Lib/imaplib.py b/Lib/imaplib.py --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -1063,6 +1063,11 @@ del self.tagged_commands[tag] return result + # If we've seen a BYE at this point, the socket will be + # closed, so report the BYE now. + + self._check_bye() + # Some have reported "unexpected response" exceptions. # Note that ignoring them here causes loops. # Instead, send me details of the unexpected response and diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,10 @@ Library ------- +- Issue #20013: Some imap servers disconnect if the current mailbox is + deleted, and imaplib did not handle that case gracefully. Now it + handles the 'bye' correctly. + - Issue #19920: TarFile.list() no longer fails when outputs a listing containing non-encodable characters. Based on patch by Vajrasky Kok. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 19:51:56 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 19:51:56 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Null_merge_of_commit_accidentally_made_to_default_branch?= =?utf-8?q?_first=2E?= Message-ID: <3fLQbc3sqzz7LkV@mail.python.org> http://hg.python.org/cpython/rev/2e59d3dbf737 changeset: 89023:2e59d3dbf737 parent: 89021:a24f7d195b8f parent: 89022:aecc0a4be052 user: R David Murray date: Fri Feb 07 13:48:46 2014 -0500 summary: Null merge of commit accidentally made to default branch first. files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 19:51:57 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 19:51:57 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzIwMDEzOiBkb24n?= =?utf-8?q?t_raise_socket_error_when_selected_mailbox_deleted=2E?= Message-ID: <3fLQbd5n2sz7Llc@mail.python.org> http://hg.python.org/cpython/rev/16af3de2240f changeset: 89024:16af3de2240f branch: 2.7 parent: 89007:75c75d90f3a4 user: R David Murray date: Fri Feb 07 13:51:22 2014 -0500 summary: #20013: don't raise socket error when selected mailbox deleted. I'm checking this in without a test because not much of this code is tested and I don't have time to work up the necessary extensions to the existing test framework. The patch itself was tested by the person who reported the bug. files: Lib/imaplib.py | 5 +++++ Misc/NEWS | 4 ++++ 2 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Lib/imaplib.py b/Lib/imaplib.py --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -1002,6 +1002,11 @@ del self.tagged_commands[tag] return result + # If we've seen a BYE at this point, the socket will be + # closed, so report the BYE now. + + self._check_bye() + # Some have reported "unexpected response" exceptions. # Note that ignoring them here causes loops. # Instead, send me details of the unexpected response and diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -42,6 +42,10 @@ Library ------- +- Issue #20013: Some imap servers disconnect if the current mailbox is + deleted, and imaplib did not handle that case gracefully. Now it + handles the 'bye' correctly. + - Issue #20426: When passing the re.DEBUG flag, re.compile() displays the debug output every time it is called, regardless of the compilation cache. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 21:05:03 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 21:05:03 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE3MzY5OiBJbXBy?= =?utf-8?q?ove_handling_of_broken_RFC2231_values_in_get=5Ffilename=2E?= Message-ID: <3fLSCz0wtwz7Ljv@mail.python.org> http://hg.python.org/cpython/rev/63f8ea0eeb6d changeset: 89025:63f8ea0eeb6d branch: 3.3 parent: 89022:aecc0a4be052 user: R David Murray date: Fri Feb 07 15:02:19 2014 -0500 summary: #17369: Improve handling of broken RFC2231 values in get_filename. This fixes a regression relative to python2. files: Lib/email/utils.py | 4 +++ Lib/test/test_email/test_email.py | 20 +++++++++++++++++++ Misc/NEWS | 4 +++ 3 files changed, 28 insertions(+), 0 deletions(-) diff --git a/Lib/email/utils.py b/Lib/email/utils.py --- a/Lib/email/utils.py +++ b/Lib/email/utils.py @@ -337,6 +337,10 @@ # object. We do not want bytes() normal utf-8 decoder, we want a straight # interpretation of the string as character bytes. charset, language, text = value + if charset is None: + # Issue 17369: if charset/lang is None, decode_rfc2231 couldn't parse + # the value, so use the fallback_charset. + charset = fallback_charset rawbytes = bytes(text, 'raw-unicode-escape') try: return str(rawbytes, charset, errors) diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -5018,6 +5018,26 @@ self.assertNotIsInstance(param, tuple) self.assertEqual(param, "Frank's Document") + def test_rfc2231_missing_tick(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0*="'This%20is%20broken"; +''' + msg = email.message_from_string(m) + self.assertEqual( + msg.get_filename(), + "'This is broken") + + def test_rfc2231_missing_tick_with_encoded_non_ascii(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0*="'This%20is%E2broken"; +''' + msg = email.message_from_string(m) + self.assertEqual( + msg.get_filename(), + "'This is\ufffdbroken") + # test_headerregistry.TestContentTypeHeader.rfc2231_single_quote_in_value_with_charset_and_lang def test_rfc2231_tick_attack_extended(self): eq = self.assertEqual diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,10 @@ Library ------- +- Issue #17369: get_filename was raising an exception if the filename + parameter's RFC2231 encoding was broken in certain ways. This was + a regression relative to python2. + - Issue #20013: Some imap servers disconnect if the current mailbox is deleted, and imaplib did not handle that case gracefully. Now it handles the 'bye' correctly. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 21:05:04 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 7 Feb 2014 21:05:04 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge=3A_=2317369=3A_Improve_handling_of_broken_RFC2231_?= =?utf-8?q?values_in_get=5Ffilename=2E?= Message-ID: <3fLSD02qnWz7LmD@mail.python.org> http://hg.python.org/cpython/rev/e0a90b1c4cdf changeset: 89026:e0a90b1c4cdf parent: 89023:2e59d3dbf737 parent: 89025:63f8ea0eeb6d user: R David Murray date: Fri Feb 07 15:04:26 2014 -0500 summary: Merge: #17369: Improve handling of broken RFC2231 values in get_filename. files: Lib/email/utils.py | 4 +++ Lib/test/test_email/test_email.py | 20 +++++++++++++++++++ Misc/NEWS | 4 +++ 3 files changed, 28 insertions(+), 0 deletions(-) diff --git a/Lib/email/utils.py b/Lib/email/utils.py --- a/Lib/email/utils.py +++ b/Lib/email/utils.py @@ -347,6 +347,10 @@ # object. We do not want bytes() normal utf-8 decoder, we want a straight # interpretation of the string as character bytes. charset, language, text = value + if charset is None: + # Issue 17369: if charset/lang is None, decode_rfc2231 couldn't parse + # the value, so use the fallback_charset. + charset = fallback_charset rawbytes = bytes(text, 'raw-unicode-escape') try: return str(rawbytes, charset, errors) diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -5052,6 +5052,26 @@ self.assertNotIsInstance(param, tuple) self.assertEqual(param, "Frank's Document") + def test_rfc2231_missing_tick(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0*="'This%20is%20broken"; +''' + msg = email.message_from_string(m) + self.assertEqual( + msg.get_filename(), + "'This is broken") + + def test_rfc2231_missing_tick_with_encoded_non_ascii(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0*="'This%20is%E2broken"; +''' + msg = email.message_from_string(m) + self.assertEqual( + msg.get_filename(), + "'This is\ufffdbroken") + # test_headerregistry.TestContentTypeHeader.rfc2231_single_quote_in_value_with_charset_and_lang def test_rfc2231_tick_attack_extended(self): eq = self.assertEqual diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,10 @@ Library ------- +- Issue #17369: get_filename was raising an exception if the filename + parameter's RFC2231 encoding was broken in certain ways. This was + a regression relative to python2. + - Issue #20013: Some imap servers disconnect if the current mailbox is deleted, and imaplib did not handle that case gracefully. Now it handles the 'bye' correctly. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Feb 7 23:37:01 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 7 Feb 2014 23:37:01 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320505=3A_Remove_r?= =?utf-8?q?esolution_and_=5Fgranularity_from_selectors_and_asyncio?= Message-ID: <3fLWbK35C3z7LkV@mail.python.org> http://hg.python.org/cpython/rev/d853955491a2 changeset: 89027:d853955491a2 user: Victor Stinner date: Fri Feb 07 23:34:58 2014 +0100 summary: Issue #20505: Remove resolution and _granularity from selectors and asyncio * Remove selectors.BaseSelector.resolution attribute * Remove asyncio.BaseEventLoop._granularity attribute files: Doc/library/asyncio-eventloop.rst | 13 ------ Doc/library/selectors.rst | 4 - Lib/asyncio/base_events.py | 13 +----- Lib/asyncio/proactor_events.py | 1 - Lib/asyncio/selector_events.py | 1 - Lib/selectors.py | 21 ---------- Lib/test/test_asyncio/test_base_events.py | 3 +- Lib/test/test_asyncio/test_events.py | 23 +++------- Lib/test/test_selectors.py | 5 -- 9 files changed, 10 insertions(+), 74 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -118,19 +118,6 @@ implementation; ideally it is a monotonic clock. This will generally be a different clock than :func:`time.time`. -The granularity of the event loop depends on the resolution of the -:meth:`~BaseEventLoop.time` method and the resolution of the selector. It is -usually between 1 ms and 16 ms. For example, a granularity of 1 ms means that -in the best case, the difference between the expected delay and the real -elapsed time is between -1 ms and +1 ms: a call scheduled in 1 nanosecond may -be called in 1 ms, and a call scheduled in 100 ms may be called in 99 ms. - -The granularity is the best difference in theory. In practice, it depends on -the system load and the the time taken by tasks executed by the event loop. -For example, if a task blocks the event loop for 1 second, all tasks scheduled -in this second will be delayed. The :ref:`Handle correctly blocking functions -` section explains how to avoid such issue. - .. method:: BaseEventLoop.call_later(delay, callback, *args) diff --git a/Doc/library/selectors.rst b/Doc/library/selectors.rst --- a/Doc/library/selectors.rst +++ b/Doc/library/selectors.rst @@ -98,10 +98,6 @@ :class:`BaseSelector` and its concrete implementations support the :term:`context manager` protocol. - .. attribute:: resolution - - Resolution of the selector in seconds. - .. method:: register(fileobj, events, data=None) Register a file object for selection, monitoring it for I/O events. diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -96,7 +96,6 @@ self._default_executor = None self._internal_fds = 0 self._running = False - self._granularity = time.get_clock_info('monotonic').resolution def _make_socket_transport(self, sock, protocol, waiter=None, *, extra=None, server=None): @@ -634,21 +633,11 @@ else: logger.log(level, 'poll took %.3f seconds', t1-t0) else: - t0 = self.time() event_list = self._selector.select(timeout) - dt = self.time() - t0 - if timeout and not event_list and dt < timeout: - print("%s.select(%.3f ms) took %.3f ms (granularity=%.3f ms, resolution=%.3f ms)" - % (self._selector.__class__.__name__, - timeout * 1e3, - dt * 1e3, - self._granularity * 1e3, - self._selector.resolution * 1e3), - file=sys.__stderr__) self._process_events(event_list) # Handle 'later' callbacks that are ready. - now = self.time() + self._granularity + now = self.time() while self._scheduled: handle = self._scheduled[0] if handle._when > now: diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -365,7 +365,6 @@ self._selector = proactor # convenient alias self._self_reading_future = None self._accept_futures = {} # socket file descriptor => Future - self._granularity = max(proactor.resolution, self._granularity) proactor.set_loop(self) self._make_self_pipe() diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -36,7 +36,6 @@ selector = selectors.DefaultSelector() logger.debug('Using selector: %s', selector.__class__.__name__) self._selector = selector - self._granularity = max(selector.resolution, self._granularity) self._make_self_pipe() def _make_socket_transport(self, sock, protocol, waiter=None, *, diff --git a/Lib/selectors.py b/Lib/selectors.py --- a/Lib/selectors.py +++ b/Lib/selectors.py @@ -83,11 +83,6 @@ performant implementation on the current platform. """ - @abstractproperty - def resolution(self): - """Resolution of the selector in seconds""" - return None - @abstractmethod def register(self, fileobj, events, data=None): """Register a file object. @@ -289,10 +284,6 @@ self._readers = set() self._writers = set() - @property - def resolution(self): - return 1e-6 - def register(self, fileobj, events, data=None): key = super().register(fileobj, events, data) if events & EVENT_READ: @@ -345,10 +336,6 @@ super().__init__() self._poll = select.poll() - @property - def resolution(self): - return 1e-3 - def register(self, fileobj, events, data=None): key = super().register(fileobj, events, data) poll_events = 0 @@ -400,10 +387,6 @@ super().__init__() self._epoll = select.epoll() - @property - def resolution(self): - return 1e-3 - def fileno(self): return self._epoll.fileno() @@ -468,10 +451,6 @@ super().__init__() self._kqueue = select.kqueue() - @property - def resolution(self): - return 1e-9 - def fileno(self): return self._kqueue.fileno() diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -124,7 +124,8 @@ self.loop.run_forever() dt = self.loop.time() - t0 - self.assertGreaterEqual(dt, delay - self.loop._granularity, dt) + # 50 ms: maximum granularity of the event loop + self.assertGreaterEqual(dt, delay - 0.050, dt) # tolerate a difference of +800 ms because some Python buildbots # are really slow self.assertLessEqual(dt, 0.9, dt) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1170,28 +1170,19 @@ orig_run_once = self.loop._run_once self.loop._run_once_counter = 0 self.loop._run_once = _run_once - calls = [] @asyncio.coroutine def wait(): loop = self.loop - calls.append(loop._run_once_counter) - yield from asyncio.sleep(loop._granularity * 10, loop=loop) - calls.append(loop._run_once_counter) - yield from asyncio.sleep(loop._granularity / 10, loop=loop) - calls.append(loop._run_once_counter) + yield from asyncio.sleep(1e-2, loop=loop) + yield from asyncio.sleep(1e-4, loop=loop) self.loop.run_until_complete(wait()) - calls.append(self.loop._run_once_counter) - self.assertEqual(calls, [1, 3, 5, 6]) - - def test_granularity(self): - granularity = self.loop._granularity - self.assertGreater(granularity, 0.0) - # Worst expected granularity: 1 ms on Linux (limited by poll/epoll - # resolution), 15.6 ms on Windows (limited by time.monotonic - # resolution) - self.assertLess(granularity, 0.050) + # The ideal number of call is 6, but on some platforms, the selector + # may sleep at little bit less than timeout depending on the resolution + # of the clock used by the kernel. Tolerate 2 useless calls on these + # platforms. + self.assertLessEqual(self.loop._run_once_counter, 8) class SubprocessTestsMixin: diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py --- a/Lib/test/test_selectors.py +++ b/Lib/test/test_selectors.py @@ -363,11 +363,6 @@ self.assertFalse(s.select(2)) self.assertLess(time() - t, 2.5) - def test_resolution(self): - s = self.SELECTOR() - self.assertIsInstance(s.resolution, (int, float)) - self.assertGreater(s.resolution, 0.0) - class ScalableSelectorMixIn: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 01:11:25 2014 From: python-checkins at python.org (guido.van.rossum) Date: Sat, 8 Feb 2014 01:11:25 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_More_asyncio_news=2E?= Message-ID: <3fLYhF11ftz7LjY@mail.python.org> http://hg.python.org/cpython/rev/b541ecd32115 changeset: 89028:b541ecd32115 user: Guido van Rossum date: Fri Feb 07 16:11:17 2014 -0800 summary: More asyncio news. files: Misc/NEWS | 16 +++++++++++----- 1 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -77,12 +77,18 @@ select.epoll.poll(), selectors.PollSelector.poll() and selectors.EpollSelector.poll(). For example, a timeout of one microsecond (1e-6) is now rounded to one millisecondi (1e-3), instead of being rounded to - zero. - -- asyncio: Some refactoring; add write flow control to unix pipes; - support wait_for(f, None); don't log broken/disconnected pipes; use + zero. However, the granularity property and asyncio's resolution feature + were removed again. + +- asyncio: Some refactoring; various fixes; add write flow control to + unix pipes; Future.set_exception() instantiates the exception + argument if it is a class; improved proactor pipe transport; support + wait_for(f, None); don't log broken/disconnected pipes; use ValueError instead of assert for forbidden subprocess_{shell,exec} - arguments. (More to follow -- a convenience API for subprocesses.) + arguments; added a convenience API for subprocess management; added + StreamReader.at_eof(); properly handle duplicate coroutines/futures + in gather(), wait(), as_completed(); use a bytearray for buffering + in StreamReader; and more. - Issue #20288: fix handling of invalid numeric charrefs in HTMLParser. -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Feb 8 09:52:57 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 08 Feb 2014 09:52:57 +0100 Subject: [Python-checkins] Daily reference leaks (b541ecd32115): sum=4 Message-ID: results for b541ecd32115 on branch "default" -------------------------------------------- test_site leaked [0, 0, 2] references, sum=2 test_site leaked [0, 0, 2] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogRqbXLp', '-x'] From python-checkins at python.org Sat Feb 8 10:44:49 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 8 Feb 2014 10:44:49 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320478=3A_avoid_sp?= =?utf-8?q?ecial_casing_Counter_in_statistics?= Message-ID: <3fLpPs1k0gz7Ljn@mail.python.org> http://hg.python.org/cpython/rev/78d0b7472697 changeset: 89029:78d0b7472697 user: Nick Coghlan date: Sat Feb 08 19:44:16 2014 +1000 summary: Issue #20478: avoid special casing Counter in statistics Passing Counter objects to the Counter constructor is special cased, going through iter() firsts ensures they are handled the same way as any other iterable. (Committing on Steven's behalf as I don't believe his SSH key is registered yet) files: Lib/statistics.py | 4 +--- Lib/test/test_statistics.py | 8 ++++++++ Misc/NEWS | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Lib/statistics.py b/Lib/statistics.py --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -268,9 +268,7 @@ def _counts(data): # Generate a table of sorted (value, frequency) pairs. - if data is None: - raise TypeError('None is not iterable') - table = collections.Counter(data).most_common() + table = collections.Counter(iter(data)).most_common() if not table: return table # Extract the values with the highest frequency. diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -1355,6 +1355,14 @@ # collections.Counter, which accepts None and returns an empty dict. self.assertRaises(TypeError, self.func, None) + def test_counter_data(self): + # Test that a Counter is treated like any other iterable. + data = collections.Counter([1, 1, 1, 2]) + # Since the keys of the counter are treated as data points, not the + # counts, this should raise. + self.assertRaises(statistics.StatisticsError, self.func, data) + + # === Tests for variances and standard deviations === diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,9 @@ Library ------- +- Issue #20478: the statistics module now treats collections.Counter inputs + like any other iterable. + - Issue #17369: get_filename was raising an exception if the filename parameter's RFC2231 encoding was broken in certain ways. This was a regression relative to python2. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 10:48:00 2014 From: python-checkins at python.org (terry.reedy) Date: Sat, 8 Feb 2014 10:48:00 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320167=3A_Suppress?= =?utf-8?q?_3=2E4_specific_=27Exception_ignored=27_messages=2E?= Message-ID: <3fLpTX6dVWz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/f9f2c57f7d00 changeset: 89030:f9f2c57f7d00 user: Terry Jan Reedy date: Sat Feb 08 04:47:29 2014 -0500 summary: Issue #20167: Suppress 3.4 specific 'Exception ignored' messages. Original patch by Tal Einat. files: Lib/idlelib/MultiCall.py | 27 +++++++++++++++++++++++---- 1 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/MultiCall.py b/Lib/idlelib/MultiCall.py --- a/Lib/idlelib/MultiCall.py +++ b/Lib/idlelib/MultiCall.py @@ -57,6 +57,13 @@ for number in range(len(_modifiers)) for name in _modifiers[number]]) +# In 3.4, if no shell window is ever open, the underlying Tk widget is +# destroyed before .__del__ methods here are called. The following +# is used to selectively ignore shutdown exceptions to avoid +# 'Exception ignored' messages. See http://bugs.python.org/issue20167 +APPLICATION_GONE = '''\ +can't invoke "bind" command: application has been destroyed''' + # A binder is a class which binds functions to one type of event. It has two # methods: bind and unbind, which get a function and a parsed sequence, as # returned by _parse_sequence(). There are two types of binders: @@ -98,7 +105,12 @@ def __del__(self): if self.handlerid: - self.widget.unbind(self.widgetinst, self.sequence, self.handlerid) + try: + self.widget.unbind(self.widgetinst, self.sequence, + self.handlerid) + except tkinter.TclError as e: + if e.args[0] == APPLICATION_GONE: + pass # An int in range(1 << len(_modifiers)) represents a combination of modifiers # (if the least significent bit is on, _modifiers[0] is on, and so on). @@ -227,7 +239,11 @@ def __del__(self): for seq, id in self.handlerids: - self.widget.unbind(self.widgetinst, seq, id) + try: + self.widget.unbind(self.widgetinst, seq, id) + except tkinter.TclError as e: + if e.args[0] == APPLICATION_GONE: + break # define the list of event types to be handled by MultiEvent. the order is # compatible with the definition of event type constants. @@ -390,8 +406,11 @@ func, triplets = self.__eventinfo[virtual] if func: for triplet in triplets: - self.__binders[triplet[1]].unbind(triplet, func) - + try: + self.__binders[triplet[1]].unbind(triplet, func) + except tkinter.TclError as e: + if e.args[0] == APPLICATION_GONE: + break _multicall_dict[widget] = MultiCall return MultiCall -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 11:14:09 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 8 Feb 2014 11:14:09 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_=2320481=3A_Disallow?= =?utf-8?q?_mixed_type_input_in_statistics?= Message-ID: <3fLq3j5gmhz7LjN@mail.python.org> http://hg.python.org/cpython/rev/5db74cd953ab changeset: 89031:5db74cd953ab user: Nick Coghlan date: Sat Feb 08 19:58:04 2014 +1000 summary: Close #20481: Disallow mixed type input in statistics The most appropriate coercion rules are not yet clear, so simply disallowing mixed type input for 3.4. (Committed on Steven's behalf) files: Doc/library/statistics.rst | 10 +++ Lib/statistics.py | 67 +++++++++--------------- Lib/test/test_statistics.py | 59 +++++++++------------ Misc/NEWS | 6 ++ 4 files changed, 66 insertions(+), 76 deletions(-) diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst --- a/Doc/library/statistics.rst +++ b/Doc/library/statistics.rst @@ -20,6 +20,16 @@ This module provides functions for calculating mathematical statistics of numeric (:class:`Real`-valued) data. +.. note:: + + Unless explicitly noted otherwise, these functions support :class:`int`, + :class:`float`, :class:`decimal.Decimal` and :class:`fractions.Fraction`. + Behaviour with other types (whether in the numeric tower or not) is + currently unsupported. Mixed types are also undefined and + implementation-dependent. If your input data consists of mixed types, + you may be able to use :func:`map` to ensure a consistent result, e.g. + ``map(float, input_data)``. + Averages and measures of central location ----------------------------------------- diff --git a/Lib/statistics.py b/Lib/statistics.py --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -144,19 +144,31 @@ >>> _sum(data) Decimal('0.6963') + Mixed types are currently treated as an error, except that int is + allowed. """ + # We fail as soon as we reach a value that is not an int or the type of + # the first value which is not an int. E.g. _sum([int, int, float, int]) + # is okay, but sum([int, int, float, Fraction]) is not. + allowed_types = set([int, type(start)]) n, d = _exact_ratio(start) - T = type(start) partials = {d: n} # map {denominator: sum of numerators} # Micro-optimizations. - coerce_types = _coerce_types exact_ratio = _exact_ratio partials_get = partials.get - # Add numerators for each denominator, and track the "current" type. + # Add numerators for each denominator. for x in data: - T = _coerce_types(T, type(x)) + _check_type(type(x), allowed_types) n, d = exact_ratio(x) partials[d] = partials_get(d, 0) + n + # Find the expected result type. If allowed_types has only one item, it + # will be int; if it has two, use the one which isn't int. + assert len(allowed_types) in (1, 2) + if len(allowed_types) == 1: + assert allowed_types.pop() is int + T = int + else: + T = (allowed_types - set([int])).pop() if None in partials: assert issubclass(T, (float, Decimal)) assert not math.isfinite(partials[None]) @@ -172,6 +184,15 @@ return T(total) +def _check_type(T, allowed): + if T not in allowed: + if len(allowed) == 1: + allowed.add(T) + else: + types = ', '.join([t.__name__ for t in allowed] + [T.__name__]) + raise TypeError("unsupported mixed types: %s" % types) + + def _exact_ratio(x): """Convert Real number x exactly to (numerator, denominator) pair. @@ -228,44 +249,6 @@ return (num, den) -def _coerce_types(T1, T2): - """Coerce types T1 and T2 to a common type. - - >>> _coerce_types(int, float) - - - Coercion is performed according to this table, where "N/A" means - that a TypeError exception is raised. - - +----------+-----------+-----------+-----------+----------+ - | | int | Fraction | Decimal | float | - +----------+-----------+-----------+-----------+----------+ - | int | int | Fraction | Decimal | float | - | Fraction | Fraction | Fraction | N/A | float | - | Decimal | Decimal | N/A | Decimal | float | - | float | float | float | float | float | - +----------+-----------+-----------+-----------+----------+ - - Subclasses trump their parent class; two subclasses of the same - base class will be coerced to the second of the two. - - """ - # Get the common/fast cases out of the way first. - if T1 is T2: return T1 - if T1 is int: return T2 - if T2 is int: return T1 - # Subclasses trump their parent class. - if issubclass(T2, T1): return T2 - if issubclass(T1, T2): return T1 - # Floats trump everything else. - if issubclass(T2, float): return T2 - if issubclass(T1, float): return T1 - # Subclasses of the same base class give priority to the second. - if T1.__base__ is T2.__base__: return T2 - # Otherwise, just give up. - raise TypeError('cannot coerce types %r and %r' % (T1, T2)) - - def _counts(data): # Generate a table of sorted (value, frequency) pairs. table = collections.Counter(iter(data)).most_common() diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -687,6 +687,26 @@ self.assertRaises(ValueError, statistics._decimal_to_ratio, d) +class CheckTypeTest(unittest.TestCase): + # Test _check_type private function. + + def test_allowed(self): + # Test that a type which should be allowed is allowed. + allowed = set([int, float]) + statistics._check_type(int, allowed) + statistics._check_type(float, allowed) + + def test_not_allowed(self): + # Test that a type which should not be allowed raises. + allowed = set([int, float]) + self.assertRaises(TypeError, statistics._check_type, Decimal, allowed) + + def test_add_to_allowed(self): + # Test that a second type will be added to the allowed set. + allowed = set([int]) + statistics._check_type(float, allowed) + self.assertEqual(allowed, set([int, float])) + # === Tests for public functions === @@ -881,40 +901,11 @@ self.assertRaises(TypeError, self.func, [1, 2, 3, b'999']) def test_mixed_sum(self): - # Mixed sums are allowed. - - # Careful here: order matters. Can't mix Fraction and Decimal directly, - # only after they're converted to float. - data = [1, 2, Fraction(1, 2), 3.0, Decimal("0.25")] - self.assertEqual(self.func(data), 6.75) - - -class SumInternalsTest(NumericTestCase): - # Test internals of the sum function. - - def test_ignore_instance_float_method(self): - # Test that __float__ methods on data instances are ignored. - - # Python typically calls __dunder__ methods on the class, not the - # instance. The ``sum`` implementation calls __float__ directly. To - # better match the behaviour of Python, we call it only on the class, - # not the instance. This test will fail if somebody "fixes" that code. - - # Create a fake __float__ method. - def __float__(self): - raise AssertionError('test fails') - - # Inject it into an instance. - class MyNumber(Fraction): - pass - x = MyNumber(3) - x.__float__ = types.MethodType(__float__, x) - - # Check it works as expected. - self.assertRaises(AssertionError, x.__float__) - self.assertEqual(float(x), 3.0) - # And now test the function. - self.assertEqual(statistics._sum([1.0, 2.0, x, 4.0]), 10.0) + # Mixed input types are not (currently) allowed. + # Check that mixed data types fail. + self.assertRaises(TypeError, self.func, [1, 2.0, Fraction(1, 2)]) + # And so does mixed start argument. + self.assertRaises(TypeError, self.func, [1, 2.0], Decimal(1)) class SumTortureTest(NumericTestCase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,12 @@ Library ------- +- Issue #20481: For at least Python 3.4, the statistics module will require + that all inputs for a single operation be of a single consistent type, or + else a mixed of ints and a single other consistent type. This avoids + some interoperability issues that arose with the previous approach of + coercing to a suitable common type. + - Issue #20478: the statistics module now treats collections.Counter inputs like any other iterable. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 13:09:12 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 13:09:12 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTM4?= =?utf-8?q?=3A_UTF-7_incremental_decoder_produced_inconsistant_string_when?= Message-ID: <3fLscS0ftxz7Ljn@mail.python.org> http://hg.python.org/cpython/rev/8d40d9cee409 changeset: 89032:8d40d9cee409 branch: 3.3 parent: 89025:63f8ea0eeb6d user: Serhiy Storchaka date: Sat Feb 08 14:01:29 2014 +0200 summary: Issue #20538: UTF-7 incremental decoder produced inconsistant string when input was truncated in BASE64 section. files: Lib/test/test_codecs.py | 39 ++++++++++++++++++++++++---- Misc/NEWS | 3 ++ Objects/unicodeobject.c | 10 ++++++- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -852,13 +852,40 @@ def test_partial(self): self.check_partial( - "a+-b", + 'a+-b\x00c\x80d\u0100e\U00010000f', [ - "a", - "a", - "a+", - "a+-", - "a+-b", + 'a', + 'a', + 'a+', + 'a+-', + 'a+-b', + 'a+-b', + 'a+-b', + 'a+-b', + 'a+-b', + 'a+-b\x00', + 'a+-b\x00c', + 'a+-b\x00c', + 'a+-b\x00c', + 'a+-b\x00c', + 'a+-b\x00c', + 'a+-b\x00c\x80', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d\u0100', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e\U00010000', + 'a+-b\x00c\x80d\u0100e\U00010000f', ] ) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #20538: UTF-7 incremental decoder produced inconsistant string when + input was truncated in BASE64 section. + - Issue #17825: Cursor "^" is correctly positioned for SyntaxError and IndentationError. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -4474,8 +4474,16 @@ /* return state */ if (consumed) { if (inShift) { + *consumed = startinpos; + if (outpos != shiftOutStart && + PyUnicode_MAX_CHAR_VALUE(unicode) > 127) { + PyObject *result = PyUnicode_FromKindAndData( + PyUnicode_KIND(unicode), PyUnicode_DATA(unicode), + shiftOutStart); + Py_DECREF(unicode); + unicode = result; + } outpos = shiftOutStart; /* back off output */ - *consumed = startinpos; } else { *consumed = s-starts; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 13:09:13 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 13:09:13 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320538=3A_UTF-7_incremental_decoder_produced_inc?= =?utf-8?q?onsistant_string_when?= Message-ID: <3fLscT2R4hz7LmZ@mail.python.org> http://hg.python.org/cpython/rev/e988661e458c changeset: 89033:e988661e458c parent: 89031:5db74cd953ab parent: 89032:8d40d9cee409 user: Serhiy Storchaka date: Sat Feb 08 14:06:33 2014 +0200 summary: Issue #20538: UTF-7 incremental decoder produced inconsistant string when input was truncated in BASE64 section. files: Lib/test/test_codecs.py | 41 +++++++++++++++++++++++----- Misc/NEWS | 3 ++ Objects/unicodeobject.c | 10 ++++++- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -124,8 +124,6 @@ "".join(codecs.iterdecode([bytes([c]) for c in encoded], self.encoding)) ) - # Temporary skip, see http://bugs.python.org/issue20542 - @unittest.skip def test_readline(self): def getreader(input): stream = io.BytesIO(input.encode(self.encoding)) @@ -899,13 +897,40 @@ def test_partial(self): self.check_partial( - "a+-b", + 'a+-b\x00c\x80d\u0100e\U00010000f', [ - "a", - "a", - "a+", - "a+-", - "a+-b", + 'a', + 'a', + 'a+', + 'a+-', + 'a+-b', + 'a+-b', + 'a+-b', + 'a+-b', + 'a+-b', + 'a+-b\x00', + 'a+-b\x00c', + 'a+-b\x00c', + 'a+-b\x00c', + 'a+-b\x00c', + 'a+-b\x00c', + 'a+-b\x00c\x80', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d', + 'a+-b\x00c\x80d\u0100', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e', + 'a+-b\x00c\x80d\u0100e\U00010000', + 'a+-b\x00c\x80d\u0100e\U00010000f', ] ) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #20538: UTF-7 incremental decoder produced inconsistant string when + input was truncated in BASE64 section. + - Issue #20404: io.TextIOWrapper (and hence the open() builtin) now uses the internal codec marking system added for issue #19619 to throw LookupError for known non-text encodings at stream construction time. The existing diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -4459,8 +4459,16 @@ /* return state */ if (consumed) { if (inShift) { + *consumed = startinpos; + if (writer.pos != shiftOutStart && writer.maxchar > 127) { + PyObject *result = PyUnicode_FromKindAndData( + writer.kind, writer.data, shiftOutStart); + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + _PyUnicodeWriter_Dealloc(&writer); + return result; + } writer.pos = shiftOutStart; /* back off output */ - *consumed = startinpos; } else { *consumed = s-starts; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:07:58 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 14:07:58 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNTQ2?= =?utf-8?q?=3A_Use_specific_asserts_in_int_tests=2E?= Message-ID: <3fLtwG1hDTz7Ljn@mail.python.org> http://hg.python.org/cpython/rev/e47c46ede37e changeset: 89034:e47c46ede37e branch: 2.7 parent: 89024:16af3de2240f user: Serhiy Storchaka date: Sat Feb 08 14:28:20 2014 +0200 summary: Issue #20546: Use specific asserts in int tests. files: Lib/test/test_long.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -90,7 +90,7 @@ # The sign of the number is also random. def getran(self, ndigits): - self.assertTrue(ndigits > 0) + self.assertGreater(ndigits, 0) nbits_hi = ndigits * SHIFT nbits_lo = nbits_hi - SHIFT + 1 answer = 0L @@ -588,7 +588,7 @@ pass x = long2(1L<<100) y = int(x) - self.assertTrue(type(y) is long, + self.assertIs(type(y), long, "overflowing int conversion must return long not long subtype") # long -> Py_ssize_t conversion -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:07:59 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 14:07:59 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTQ2?= =?utf-8?q?=3A_Use_specific_asserts_in_int_tests=2E?= Message-ID: <3fLtwH44Zlz7LmZ@mail.python.org> http://hg.python.org/cpython/rev/265446b8b48c changeset: 89035:265446b8b48c branch: 3.3 parent: 89032:8d40d9cee409 user: Serhiy Storchaka date: Sat Feb 08 14:28:33 2014 +0200 summary: Issue #20546: Use specific asserts in int tests. files: Lib/test/test_long.py | 48 +++++++++++++++--------------- 1 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -130,7 +130,7 @@ # The sign of the number is also random. def getran(self, ndigits): - self.assertTrue(ndigits > 0) + self.assertGreater(ndigits, 0) nbits_hi = ndigits * SHIFT nbits_lo = nbits_hi - SHIFT + 1 answer = 0 @@ -865,21 +865,21 @@ def test_small_ints(self): for i in range(-5, 257): - self.assertTrue(i is i + 0) - self.assertTrue(i is i * 1) - self.assertTrue(i is i - 0) - self.assertTrue(i is i // 1) - self.assertTrue(i is i & -1) - self.assertTrue(i is i | 0) - self.assertTrue(i is i ^ 0) - self.assertTrue(i is ~~i) - self.assertTrue(i is i**1) - self.assertTrue(i is int(str(i))) - self.assertTrue(i is i<<2>>2, str(i)) + self.assertIs(i, i + 0) + self.assertIs(i, i * 1) + self.assertIs(i, i - 0) + self.assertIs(i, i // 1) + self.assertIs(i, i & -1) + self.assertIs(i, i | 0) + self.assertIs(i, i ^ 0) + self.assertIs(i, ~~i) + self.assertIs(i, i**1) + self.assertIs(i, int(str(i))) + self.assertIs(i, i<<2>>2, str(i)) # corner cases i = 1 << 70 - self.assertTrue(i - i is 0) - self.assertTrue(0 * i is 0) + self.assertIs(i - i, 0) + self.assertIs(0 * i, 0) def test_bit_length(self): tiny = 1e-10 @@ -926,7 +926,7 @@ got = round(k+offset, -1) expected = v+offset self.assertEqual(got, expected) - self.assertTrue(type(got) is int) + self.assertIs(type(got), int) # larger second argument self.assertEqual(round(-150, -2), -200) @@ -965,7 +965,7 @@ got = round(10**k + 324678, -3) expect = 10**k + 325000 self.assertEqual(got, expect) - self.assertTrue(type(got) is int) + self.assertIs(type(got), int) # nonnegative second argument: round(x, n) should just return x for n in range(5): @@ -973,7 +973,7 @@ x = random.randrange(-10000, 10000) got = round(x, n) self.assertEqual(got, x) - self.assertTrue(type(got) is int) + self.assertIs(type(got), int) for huge_n in 2**31-1, 2**31, 2**63-1, 2**63, 2**100, 10**100: self.assertEqual(round(8979323, huge_n), 8979323) @@ -982,7 +982,7 @@ x = random.randrange(-10000, 10000) got = round(x) self.assertEqual(got, x) - self.assertTrue(type(got) is int) + self.assertIs(type(got), int) # bad second argument bad_exponents = ('brian', 2.0, 0j, None) @@ -1187,15 +1187,15 @@ class myint(int): pass - self.assertTrue(type(myint.from_bytes(b'\x00', 'big')) is myint) + self.assertIs(type(myint.from_bytes(b'\x00', 'big')), myint) self.assertEqual(myint.from_bytes(b'\x01', 'big'), 1) - self.assertTrue( - type(myint.from_bytes(b'\x00', 'big', signed=False)) is myint) + self.assertIs( + type(myint.from_bytes(b'\x00', 'big', signed=False)), myint) self.assertEqual(myint.from_bytes(b'\x01', 'big', signed=False), 1) - self.assertTrue(type(myint.from_bytes(b'\x00', 'little')) is myint) + self.assertIs(type(myint.from_bytes(b'\x00', 'little')), myint) self.assertEqual(myint.from_bytes(b'\x01', 'little'), 1) - self.assertTrue(type(myint.from_bytes( - b'\x00', 'little', signed=False)) is myint) + self.assertIs(type(myint.from_bytes( + b'\x00', 'little', signed=False)), myint) self.assertEqual(myint.from_bytes(b'\x01', 'little', signed=False), 1) self.assertEqual( int.from_bytes([255, 0, 0], 'big', signed=True), -65536) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:08:00 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 14:08:00 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320546=3A_Use_specific_asserts_in_int_tests=2E?= Message-ID: <3fLtwJ6SmKz7LnD@mail.python.org> http://hg.python.org/cpython/rev/79ddfbea25d7 changeset: 89036:79ddfbea25d7 parent: 89033:e988661e458c parent: 89035:265446b8b48c user: Serhiy Storchaka date: Sat Feb 08 14:29:36 2014 +0200 summary: Issue #20546: Use specific asserts in int tests. files: Lib/test/test_long.py | 48 +++++++++++++++--------------- 1 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -130,7 +130,7 @@ # The sign of the number is also random. def getran(self, ndigits): - self.assertTrue(ndigits > 0) + self.assertGreater(ndigits, 0) nbits_hi = ndigits * SHIFT nbits_lo = nbits_hi - SHIFT + 1 answer = 0 @@ -865,21 +865,21 @@ def test_small_ints(self): for i in range(-5, 257): - self.assertTrue(i is i + 0) - self.assertTrue(i is i * 1) - self.assertTrue(i is i - 0) - self.assertTrue(i is i // 1) - self.assertTrue(i is i & -1) - self.assertTrue(i is i | 0) - self.assertTrue(i is i ^ 0) - self.assertTrue(i is ~~i) - self.assertTrue(i is i**1) - self.assertTrue(i is int(str(i))) - self.assertTrue(i is i<<2>>2, str(i)) + self.assertIs(i, i + 0) + self.assertIs(i, i * 1) + self.assertIs(i, i - 0) + self.assertIs(i, i // 1) + self.assertIs(i, i & -1) + self.assertIs(i, i | 0) + self.assertIs(i, i ^ 0) + self.assertIs(i, ~~i) + self.assertIs(i, i**1) + self.assertIs(i, int(str(i))) + self.assertIs(i, i<<2>>2, str(i)) # corner cases i = 1 << 70 - self.assertTrue(i - i is 0) - self.assertTrue(0 * i is 0) + self.assertIs(i - i, 0) + self.assertIs(0 * i, 0) def test_bit_length(self): tiny = 1e-10 @@ -926,7 +926,7 @@ got = round(k+offset, -1) expected = v+offset self.assertEqual(got, expected) - self.assertTrue(type(got) is int) + self.assertIs(type(got), int) # larger second argument self.assertEqual(round(-150, -2), -200) @@ -965,7 +965,7 @@ got = round(10**k + 324678, -3) expect = 10**k + 325000 self.assertEqual(got, expect) - self.assertTrue(type(got) is int) + self.assertIs(type(got), int) # nonnegative second argument: round(x, n) should just return x for n in range(5): @@ -973,7 +973,7 @@ x = random.randrange(-10000, 10000) got = round(x, n) self.assertEqual(got, x) - self.assertTrue(type(got) is int) + self.assertIs(type(got), int) for huge_n in 2**31-1, 2**31, 2**63-1, 2**63, 2**100, 10**100: self.assertEqual(round(8979323, huge_n), 8979323) @@ -982,7 +982,7 @@ x = random.randrange(-10000, 10000) got = round(x) self.assertEqual(got, x) - self.assertTrue(type(got) is int) + self.assertIs(type(got), int) # bad second argument bad_exponents = ('brian', 2.0, 0j, None) @@ -1187,15 +1187,15 @@ class myint(int): pass - self.assertTrue(type(myint.from_bytes(b'\x00', 'big')) is myint) + self.assertIs(type(myint.from_bytes(b'\x00', 'big')), myint) self.assertEqual(myint.from_bytes(b'\x01', 'big'), 1) - self.assertTrue( - type(myint.from_bytes(b'\x00', 'big', signed=False)) is myint) + self.assertIs( + type(myint.from_bytes(b'\x00', 'big', signed=False)), myint) self.assertEqual(myint.from_bytes(b'\x01', 'big', signed=False), 1) - self.assertTrue(type(myint.from_bytes(b'\x00', 'little')) is myint) + self.assertIs(type(myint.from_bytes(b'\x00', 'little')), myint) self.assertEqual(myint.from_bytes(b'\x01', 'little'), 1) - self.assertTrue(type(myint.from_bytes( - b'\x00', 'little', signed=False)) is myint) + self.assertIs(type(myint.from_bytes( + b'\x00', 'little', signed=False)), myint) self.assertEqual(myint.from_bytes(b'\x01', 'little', signed=False), 1) self.assertEqual( int.from_bytes([255, 0, 0], 'big', signed=True), -65536) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:08:02 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 14:08:02 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNTU1?= =?utf-8?q?=3A_Use_specific_asserts_in_urllib=2C_httplib=2C_ftplib=2C_cgi?= =?utf-8?q?=2C_wsgiref?= Message-ID: <3fLtwL1vRKz7LnY@mail.python.org> http://hg.python.org/cpython/rev/b21397ca58c7 changeset: 89037:b21397ca58c7 branch: 2.7 parent: 89034:e47c46ede37e user: Serhiy Storchaka date: Sat Feb 08 14:49:55 2014 +0200 summary: Issue #20555: Use specific asserts in urllib, httplib, ftplib, cgi, wsgiref tests. files: Lib/test/test_cgi.py | 2 +- Lib/test/test_ftplib.py | 6 ++-- Lib/test/test_httplib.py | 6 ++-- Lib/test/test_httpservers.py | 2 +- Lib/test/test_urllib.py | 4 +- Lib/test/test_urllib2.py | 7 +++-- Lib/test/test_urllib2net.py | 26 ++++++++++++------------ Lib/test/test_wsgiref.py | 4 +- 8 files changed, 29 insertions(+), 28 deletions(-) diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -232,7 +232,7 @@ # if we're not chunking properly, readline is only called twice # (by read_binary); if we are chunking properly, it will be called 5 times # as long as the chunksize is 1 << 16. - self.assertTrue(f.numcalls > 2) + self.assertGreater(f.numcalls, 2) def test_fieldstorage_invalid(self): fs = cgi.FieldStorage() diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -740,7 +740,7 @@ def testTimeoutDefault(self): # default -- use global socket timeout - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: ftp = ftplib.FTP(HOST) @@ -752,13 +752,13 @@ def testTimeoutNone(self): # no timeout -- do not use global socket timeout - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: ftp = ftplib.FTP(HOST, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(ftp.sock.gettimeout() is None) + self.assertIsNone(ftp.sock.gettimeout()) self.evt.wait() ftp.close() diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -123,7 +123,7 @@ conn.sock = FakeSocket(None) conn.putrequest('GET','/') conn.putheader('Content-length',42) - self.assertTrue('Content-length: 42' in conn._buffer) + self.assertIn('Content-length: 42', conn._buffer) def test_ipv6host_header(self): # Default host header on IPv6 transaction should wrapped by [] if @@ -466,7 +466,7 @@ HTTPConnection and into the socket. ''' # default -- use global socket timeout - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT) @@ -477,7 +477,7 @@ httpConn.close() # no timeout -- do not use global socket default - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT, diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -114,7 +114,7 @@ def verify_http_server_response(self, response): match = self.HTTPResponseMatch.search(response) - self.assertTrue(match is not None) + self.assertIsNotNone(match) def test_http_1_1(self): result = self.send_typical_request('GET / HTTP/1.1\r\n\r\n') diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -878,7 +878,7 @@ # def testTimeoutNone(self): # # global default timeout is ignored # import socket -# self.assertTrue(socket.getdefaulttimeout() is None) +# self.assertIsNone(socket.getdefaulttimeout()) # socket.setdefaulttimeout(30) # try: # ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) @@ -890,7 +890,7 @@ # def testTimeoutDefault(self): # # global default timeout is used # import socket -# self.assertTrue(socket.getdefaulttimeout() is None) +# self.assertIsNone(socket.getdefaulttimeout()) # socket.setdefaulttimeout(30) # try: # ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -591,8 +591,8 @@ self.assertIsInstance(args[0], Request) # response from opener.open is None, because there's no # handler that defines http_open to handle it - self.assertTrue(args[1] is None or - isinstance(args[1], MockResponse)) + if args[1] is not None: + self.assertIsInstance(args[1], MockResponse) def sanepathname2url(path): @@ -924,7 +924,8 @@ MockHeaders({"location": to_url})) except urllib2.HTTPError: # 307 in response to POST requires user OK - self.assertTrue(code == 307 and data is not None) + self.assertEqual(code, 307) + self.assertIsNotNone(data) self.assertEqual(o.req.get_full_url(), to_url) try: self.assertEqual(o.req.get_method(), "GET") diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -80,11 +80,11 @@ # delve deep into response to fetch socket._socketobject response = _urlopen_with_retry("http://www.python.org/") abused_fileobject = response.fp - self.assertTrue(abused_fileobject.__class__ is socket._fileobject) + self.assertIs(abused_fileobject.__class__, socket._fileobject) httpresponse = abused_fileobject._sock - self.assertTrue(httpresponse.__class__ is httplib.HTTPResponse) + self.assertIs(httpresponse.__class__, httplib.HTTPResponse) fileobject = httpresponse.fp - self.assertTrue(fileobject.__class__ is socket._fileobject) + self.assertIs(fileobject.__class__, socket._fileobject) self.assertTrue(not fileobject.closed) response.close() @@ -250,14 +250,14 @@ class TimeoutTest(unittest.TestCase): def test_http_basic(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) url = "http://www.python.org" with test_support.transient_internet(url, timeout=None): u = _urlopen_with_retry(url) - self.assertTrue(u.fp._sock.fp._sock.gettimeout() is None) + self.assertIsNone(u.fp._sock.fp._sock.gettimeout()) def test_http_default_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) url = "http://www.python.org" with test_support.transient_internet(url): socket.setdefaulttimeout(60) @@ -268,7 +268,7 @@ self.assertEqual(u.fp._sock.fp._sock.gettimeout(), 60) def test_http_no_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) url = "http://www.python.org" with test_support.transient_internet(url): socket.setdefaulttimeout(60) @@ -276,7 +276,7 @@ u = _urlopen_with_retry(url, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(u.fp._sock.fp._sock.gettimeout() is None) + self.assertIsNone(u.fp._sock.fp._sock.gettimeout()) def test_http_timeout(self): url = "http://www.python.org" @@ -287,13 +287,13 @@ FTP_HOST = "ftp://ftp.mirror.nl/pub/gnu/" def test_ftp_basic(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) with test_support.transient_internet(self.FTP_HOST, timeout=None): u = _urlopen_with_retry(self.FTP_HOST) - self.assertTrue(u.fp.fp._sock.gettimeout() is None) + self.assertIsNone(u.fp.fp._sock.gettimeout()) def test_ftp_default_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) with test_support.transient_internet(self.FTP_HOST): socket.setdefaulttimeout(60) try: @@ -303,14 +303,14 @@ self.assertEqual(u.fp.fp._sock.gettimeout(), 60) def test_ftp_no_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout(),) with test_support.transient_internet(self.FTP_HOST): socket.setdefaulttimeout(60) try: u = _urlopen_with_retry(self.FTP_HOST, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(u.fp.fp._sock.gettimeout() is None) + self.assertIsNone(u.fp.fp._sock.gettimeout()) def test_ftp_timeout(self): with test_support.transient_internet(self.FTP_HOST): diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -155,7 +155,7 @@ # Check existing value env = {key:alt} util.setup_testing_defaults(env) - self.assertTrue(env[key] is alt) + self.assertIs(env[key], alt) def checkCrossDefault(self,key,value,**kw): util.setup_testing_defaults(kw) @@ -302,7 +302,7 @@ self.assertEqual(Headers(test[:]).keys(), ['x']) self.assertEqual(Headers(test[:]).values(), ['y']) self.assertEqual(Headers(test[:]).items(), test) - self.assertFalse(Headers(test).items() is test) # must be copy! + self.assertIsNot(Headers(test).items(), test) # must be copy! h=Headers([]) del h['foo'] # should not raise an error -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:08:03 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 14:08:03 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTU1?= =?utf-8?q?=3A_Use_specific_asserts_in_urllib=2C_httplib=2C_ftplib=2C_cgi?= =?utf-8?q?=2C_wsgiref?= Message-ID: <3fLtwM4xWHz7LmZ@mail.python.org> http://hg.python.org/cpython/rev/b6f8d5148a05 changeset: 89038:b6f8d5148a05 branch: 3.3 parent: 89035:265446b8b48c user: Serhiy Storchaka date: Sat Feb 08 14:50:08 2014 +0200 summary: Issue #20555: Use specific asserts in urllib, httplib, ftplib, cgi, wsgiref tests. files: Lib/test/test_cgi.py | 2 +- Lib/test/test_ftplib.py | 6 +++--- Lib/test/test_httplib.py | 6 +++--- Lib/test/test_httpservers.py | 2 +- Lib/test/test_urllib.py | 4 ++-- Lib/test/test_urllib2.py | 7 ++++--- Lib/test/test_urllib2net.py | 22 +++++++++++----------- Lib/test/test_wsgiref.py | 4 ++-- 8 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -227,7 +227,7 @@ # if we're not chunking properly, readline is only called twice # (by read_binary); if we are chunking properly, it will be called 5 times # as long as the chunksize is 1 << 16. - self.assertTrue(f.numcalls > 2) + self.assertGreater(f.numcalls, 2) f.close() def test_fieldstorage_multipart(self): diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -965,7 +965,7 @@ def testTimeoutDefault(self): # default -- use global socket timeout - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: ftp = ftplib.FTP(HOST) @@ -977,13 +977,13 @@ def testTimeoutNone(self): # no timeout -- do not use global socket timeout - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: ftp = ftplib.FTP(HOST, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(ftp.sock.gettimeout() is None) + self.assertIsNone(ftp.sock.gettimeout()) self.evt.wait() ftp.close() diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -132,7 +132,7 @@ conn.sock = FakeSocket(None) conn.putrequest('GET','/') conn.putheader('Content-length', 42) - self.assertTrue(b'Content-length: 42' in conn._buffer) + self.assertIn(b'Content-length: 42', conn._buffer) def test_ipv6host_header(self): # Default host header on IPv6 transaction should wrapped by [] if @@ -699,7 +699,7 @@ # and into the socket. # default -- use global socket timeout - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: httpConn = client.HTTPConnection(HOST, TimeoutTest.PORT) @@ -710,7 +710,7 @@ httpConn.close() # no timeout -- do not use global socket default - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: httpConn = client.HTTPConnection(HOST, TimeoutTest.PORT, diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -531,7 +531,7 @@ def verify_http_server_response(self, response): match = self.HTTPResponseMatch.search(response) - self.assertTrue(match is not None) + self.assertIsNotNone(match) def test_http_1_1(self): result = self.send_typical_request(b'GET / HTTP/1.1\r\n\r\n') diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1264,7 +1264,7 @@ # def testTimeoutNone(self): # # global default timeout is ignored # import socket -# self.assertTrue(socket.getdefaulttimeout() is None) +# self.assertIsNone(socket.getdefaulttimeout()) # socket.setdefaulttimeout(30) # try: # ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) @@ -1276,7 +1276,7 @@ # def testTimeoutDefault(self): # # global default timeout is used # import socket -# self.assertTrue(socket.getdefaulttimeout() is None) +# self.assertIsNone(socket.getdefaulttimeout()) # socket.setdefaulttimeout(30) # try: # ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -594,8 +594,8 @@ self.assertIsInstance(args[0], Request) # response from opener.open is None, because there's no # handler that defines http_open to handle it - self.assertTrue(args[1] is None or - isinstance(args[1], MockResponse)) + if args[1] is not None: + self.assertIsInstance(args[1], MockResponse) def test_method_deprecations(self): req = Request("http://www.example.com") @@ -1000,7 +1000,8 @@ MockHeaders({"location": to_url})) except urllib.error.HTTPError: # 307 in response to POST requires user OK - self.assertTrue(code == 307 and data is not None) + self.assertEqual(code, 307) + self.assertIsNotNone(data) self.assertEqual(o.req.get_full_url(), to_url) try: self.assertEqual(o.req.get_method(), "GET") diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -85,7 +85,7 @@ with support.transient_internet(url): response = _urlopen_with_retry(url) sock = response.fp - self.assertTrue(not sock.closed) + self.assertFalse(sock.closed) response.close() self.assertTrue(sock.closed) @@ -252,15 +252,15 @@ class TimeoutTest(unittest.TestCase): def test_http_basic(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) url = "http://www.python.org" with support.transient_internet(url, timeout=None): u = _urlopen_with_retry(url) self.addCleanup(u.close) - self.assertTrue(u.fp.raw._sock.gettimeout() is None) + self.assertIsNone(u.fp.raw._sock.gettimeout()) def test_http_default_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) url = "http://www.python.org" with support.transient_internet(url): socket.setdefaulttimeout(60) @@ -272,7 +272,7 @@ self.assertEqual(u.fp.raw._sock.gettimeout(), 60) def test_http_no_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) url = "http://www.python.org" with support.transient_internet(url): socket.setdefaulttimeout(60) @@ -281,7 +281,7 @@ self.addCleanup(u.close) finally: socket.setdefaulttimeout(None) - self.assertTrue(u.fp.raw._sock.gettimeout() is None) + self.assertIsNone(u.fp.raw._sock.gettimeout()) def test_http_timeout(self): url = "http://www.python.org" @@ -293,14 +293,14 @@ FTP_HOST = "ftp://ftp.mirror.nl/pub/gnu/" def test_ftp_basic(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST, timeout=None): u = _urlopen_with_retry(self.FTP_HOST) self.addCleanup(u.close) - self.assertTrue(u.fp.fp.raw._sock.gettimeout() is None) + self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) def test_ftp_default_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): socket.setdefaulttimeout(60) try: @@ -311,7 +311,7 @@ self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60) def test_ftp_no_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): socket.setdefaulttimeout(60) try: @@ -319,7 +319,7 @@ self.addCleanup(u.close) finally: socket.setdefaulttimeout(None) - self.assertTrue(u.fp.fp.raw._sock.gettimeout() is None) + self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) def test_ftp_timeout(self): with support.transient_internet(self.FTP_HOST): diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -196,7 +196,7 @@ # Check existing value env = {key:alt} util.setup_testing_defaults(env) - self.assertTrue(env[key] is alt) + self.assertIs(env[key], alt) def checkCrossDefault(self,key,value,**kw): util.setup_testing_defaults(kw) @@ -343,7 +343,7 @@ self.assertEqual(Headers(test[:]).keys(), ['x']) self.assertEqual(Headers(test[:]).values(), ['y']) self.assertEqual(Headers(test[:]).items(), test) - self.assertFalse(Headers(test).items() is test) # must be copy! + self.assertIsNot(Headers(test).items(), test) # must be copy! h=Headers([]) del h['foo'] # should not raise an error -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:08:05 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 14:08:05 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320555=3A_Use_specific_asserts_in_urllib=2C_http?= =?utf-8?q?lib=2C_ftplib=2C_cgi=2C_wsgiref?= Message-ID: <3fLtwP14S7z7Lnf@mail.python.org> http://hg.python.org/cpython/rev/868675190b34 changeset: 89039:868675190b34 parent: 89036:79ddfbea25d7 parent: 89038:b6f8d5148a05 user: Serhiy Storchaka date: Sat Feb 08 14:51:10 2014 +0200 summary: Issue #20555: Use specific asserts in urllib, httplib, ftplib, cgi, wsgiref tests. files: Lib/test/test_cgi.py | 2 +- Lib/test/test_ftplib.py | 6 +++--- Lib/test/test_httplib.py | 6 +++--- Lib/test/test_httpservers.py | 2 +- Lib/test/test_urllib.py | 4 ++-- Lib/test/test_urllib2.py | 7 ++++--- Lib/test/test_urllib2net.py | 22 +++++++++++----------- Lib/test/test_wsgiref.py | 4 ++-- 8 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -227,7 +227,7 @@ # if we're not chunking properly, readline is only called twice # (by read_binary); if we are chunking properly, it will be called 5 times # as long as the chunksize is 1 << 16. - self.assertTrue(f.numcalls > 2) + self.assertGreater(f.numcalls, 2) f.close() def test_fieldstorage_multipart(self): diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -1001,7 +1001,7 @@ def testTimeoutDefault(self): # default -- use global socket timeout - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: ftp = ftplib.FTP(HOST) @@ -1013,13 +1013,13 @@ def testTimeoutNone(self): # no timeout -- do not use global socket timeout - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: ftp = ftplib.FTP(HOST, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(ftp.sock.gettimeout() is None) + self.assertIsNone(ftp.sock.gettimeout()) self.evt.wait() ftp.close() diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -134,7 +134,7 @@ conn.sock = FakeSocket(None) conn.putrequest('GET','/') conn.putheader('Content-length', 42) - self.assertTrue(b'Content-length: 42' in conn._buffer) + self.assertIn(b'Content-length: 42', conn._buffer) def test_ipv6host_header(self): # Default host header on IPv6 transaction should wrapped by [] if @@ -723,7 +723,7 @@ # and into the socket. # default -- use global socket timeout - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: httpConn = client.HTTPConnection(HOST, TimeoutTest.PORT) @@ -734,7 +734,7 @@ httpConn.close() # no timeout -- do not use global socket default - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: httpConn = client.HTTPConnection(HOST, TimeoutTest.PORT, diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -552,7 +552,7 @@ def verify_http_server_response(self, response): match = self.HTTPResponseMatch.search(response) - self.assertTrue(match is not None) + self.assertIsNotNone(match) def test_http_1_1(self): result = self.send_typical_request(b'GET / HTTP/1.1\r\n\r\n') diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1340,7 +1340,7 @@ # def testTimeoutNone(self): # # global default timeout is ignored # import socket -# self.assertTrue(socket.getdefaulttimeout() is None) +# self.assertIsNone(socket.getdefaulttimeout()) # socket.setdefaulttimeout(30) # try: # ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) @@ -1352,7 +1352,7 @@ # def testTimeoutDefault(self): # # global default timeout is used # import socket -# self.assertTrue(socket.getdefaulttimeout() is None) +# self.assertIsNone(socket.getdefaulttimeout()) # socket.setdefaulttimeout(30) # try: # ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -603,8 +603,8 @@ self.assertIsInstance(args[0], Request) # response from opener.open is None, because there's no # handler that defines http_open to handle it - self.assertTrue(args[1] is None or - isinstance(args[1], MockResponse)) + if args[1] is not None: + self.assertIsInstance(args[1], MockResponse) def sanepathname2url(path): try: @@ -1018,7 +1018,8 @@ MockHeaders({"location": to_url})) except urllib.error.HTTPError: # 307 in response to POST requires user OK - self.assertTrue(code == 307 and data is not None) + self.assertEqual(code, 307) + self.assertIsNotNone(data) self.assertEqual(o.req.get_full_url(), to_url) try: self.assertEqual(o.req.get_method(), "GET") diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -87,7 +87,7 @@ with support.transient_internet(url): response = _urlopen_with_retry(url) sock = response.fp - self.assertTrue(not sock.closed) + self.assertFalse(sock.closed) response.close() self.assertTrue(sock.closed) @@ -262,15 +262,15 @@ class TimeoutTest(unittest.TestCase): def test_http_basic(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) url = "http://www.python.org" with support.transient_internet(url, timeout=None): u = _urlopen_with_retry(url) self.addCleanup(u.close) - self.assertTrue(u.fp.raw._sock.gettimeout() is None) + self.assertIsNone(u.fp.raw._sock.gettimeout()) def test_http_default_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) url = "http://www.python.org" with support.transient_internet(url): socket.setdefaulttimeout(60) @@ -282,7 +282,7 @@ self.assertEqual(u.fp.raw._sock.gettimeout(), 60) def test_http_no_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) url = "http://www.python.org" with support.transient_internet(url): socket.setdefaulttimeout(60) @@ -291,7 +291,7 @@ self.addCleanup(u.close) finally: socket.setdefaulttimeout(None) - self.assertTrue(u.fp.raw._sock.gettimeout() is None) + self.assertIsNone(u.fp.raw._sock.gettimeout()) def test_http_timeout(self): url = "http://www.python.org" @@ -303,14 +303,14 @@ FTP_HOST = "ftp://ftp.mirror.nl/pub/gnu/" def test_ftp_basic(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST, timeout=None): u = _urlopen_with_retry(self.FTP_HOST) self.addCleanup(u.close) - self.assertTrue(u.fp.fp.raw._sock.gettimeout() is None) + self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) def test_ftp_default_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): socket.setdefaulttimeout(60) try: @@ -321,7 +321,7 @@ self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60) def test_ftp_no_timeout(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): socket.setdefaulttimeout(60) try: @@ -329,7 +329,7 @@ self.addCleanup(u.close) finally: socket.setdefaulttimeout(None) - self.assertTrue(u.fp.fp.raw._sock.gettimeout() is None) + self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) def test_ftp_timeout(self): with support.transient_internet(self.FTP_HOST): diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -196,7 +196,7 @@ # Check existing value env = {key:alt} util.setup_testing_defaults(env) - self.assertTrue(env[key] is alt) + self.assertIs(env[key], alt) def checkCrossDefault(self,key,value,**kw): util.setup_testing_defaults(kw) @@ -343,7 +343,7 @@ self.assertEqual(Headers(test[:]).keys(), ['x']) self.assertEqual(Headers(test[:]).values(), ['y']) self.assertEqual(Headers(test[:]).items(), test) - self.assertFalse(Headers(test).items() is test) # must be copy! + self.assertIsNot(Headers(test).items(), test) # must be copy! h=Headers([]) del h['foo'] # should not raise an error -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:08:06 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 14:08:06 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNTQ5?= =?utf-8?q?=3A_Use_specific_asserts_in_mailbox=2C_smtplib_and_poplib_tests?= =?utf-8?q?=2E?= Message-ID: <3fLtwQ46xnz7Lmq@mail.python.org> http://hg.python.org/cpython/rev/1c3fcba31708 changeset: 89040:1c3fcba31708 branch: 2.7 parent: 89037:b21397ca58c7 user: Serhiy Storchaka date: Sat Feb 08 15:05:53 2014 +0200 summary: Issue #20549: Use specific asserts in mailbox, smtplib and poplib tests. files: Lib/test/test_mailbox.py | 64 ++++++++++++++-------------- Lib/test/test_poplib.py | 6 +- Lib/test/test_smtplib.py | 6 +- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -138,7 +138,7 @@ msg = self._box.get(key0) self.assertEqual(msg['from'], 'foo') self.assertEqual(msg.get_payload(), '0\n') - self.assertIs(self._box.get('foo'), None) + self.assertIsNone(self._box.get('foo')) self.assertFalse(self._box.get('foo', False)) self._box.close() self._box = self._factory(self._path, factory=rfc822.Message) @@ -249,8 +249,7 @@ count = 0 for value in returned_values: self.assertEqual(value['from'], 'foo') - self.assertTrue(int(value.get_payload()) < repetitions, - (value.get_payload(), repetitions)) + self.assertLess(int(value.get_payload()), repetitions) count += 1 self.assertEqual(len(values), count) @@ -664,7 +663,7 @@ "tmp")), "File in wrong location: '%s'" % head) match = pattern.match(tail) - self.assertTrue(match is not None, "Invalid file name: '%s'" % tail) + self.assertIsNotNone(match, "Invalid file name: '%s'" % tail) groups = match.groups() if previous_groups is not None: self.assertGreaterEqual(int(groups[0]), int(previous_groups[0]), @@ -674,22 +673,22 @@ self.assertGreaterEqual(int(groups[1]), int(previous_groups[1]), "Non-monotonic milliseconds: '%s' before '%s'" % (previous_groups[1], groups[1])) - self.assertTrue(int(groups[2]) == pid, + self.assertEqual(int(groups[2]), pid, "Process ID mismatch: '%s' should be '%s'" % (groups[2], pid)) - self.assertTrue(int(groups[3]) == int(previous_groups[3]) + 1, + self.assertEqual(int(groups[3]), int(previous_groups[3]) + 1, "Non-sequential counter: '%s' before '%s'" % (previous_groups[3], groups[3])) - self.assertTrue(groups[4] == hostname, + self.assertEqual(groups[4], hostname, "Host name mismatch: '%s' should be '%s'" % (groups[4], hostname)) previous_groups = groups tmp_file.write(_sample_message) tmp_file.seek(0) - self.assertTrue(tmp_file.read() == _sample_message) + self.assertEqual(tmp_file.read(), _sample_message) tmp_file.close() file_count = len(os.listdir(os.path.join(self._path, "tmp"))) - self.assertTrue(file_count == repetitions, + self.assertEqual(file_count, repetitions, "Wrong file count: '%s' should be '%s'" % (file_count, repetitions)) @@ -1240,7 +1239,7 @@ self.assertIsInstance(msg, self._factory) self.assertEqual(msg.keys(), []) self.assertFalse(msg.is_multipart()) - self.assertEqual(msg.get_payload(), None) + self.assertIsNone(msg.get_payload()) def test_initialize_incorrectly(self): # Initialize with invalid argument @@ -1313,7 +1312,7 @@ # Use get_date() and set_date() msg = mailbox.MaildirMessage(_sample_message) diff = msg.get_date() - time.time() - self.assertTrue(abs(diff) < 60, diff) + self.assertLess(abs(diff), 60, diff) msg.set_date(0.0) self.assertEqual(msg.get_date(), 0.0) @@ -1388,8 +1387,9 @@ # Check contents of "From " line if sender is None: sender = "MAILER-DAEMON" - self.assertTrue(re.match(sender + r" \w{3} \w{3} [\d ]\d [\d ]\d:\d{2}:" - r"\d{2} \d{4}", msg.get_from())) + self.assertIsNotNone(re.match( + sender + r" \w{3} \w{3} [\d ]\d [\d ]\d:\d{2}:\d{2} \d{4}", + msg.get_from())) class TestMboxMessage(_TestMboxMMDFMessage, TestMessage): @@ -1463,7 +1463,7 @@ msg = mailbox.BabylMessage(_sample_message) visible = msg.get_visible() self.assertEqual(visible.keys(), []) - self.assertIs(visible.get_payload(), None) + self.assertIsNone(visible.get_payload()) visible['User-Agent'] = 'FooBar 1.0' visible['X-Whatever'] = 'Blah' self.assertEqual(msg.get_visible().keys(), []) @@ -1472,10 +1472,10 @@ self.assertEqual(visible.keys(), ['User-Agent', 'X-Whatever']) self.assertEqual(visible['User-Agent'], 'FooBar 1.0') self.assertEqual(visible['X-Whatever'], 'Blah') - self.assertIs(visible.get_payload(), None) + self.assertIsNone(visible.get_payload()) msg.update_visible() self.assertEqual(visible.keys(), ['User-Agent', 'X-Whatever']) - self.assertIs(visible.get_payload(), None) + self.assertIsNone(visible.get_payload()) visible = msg.get_visible() self.assertEqual(visible.keys(), ['User-Agent', 'Date', 'From', 'To', 'Subject']) @@ -1971,43 +1971,43 @@ # Make sure the boxes attribute actually gets set. self.mbox = mailbox.Maildir(test_support.TESTFN) #self.assertTrue(hasattr(self.mbox, "boxes")) - #self.assertTrue(len(self.mbox.boxes) == 0) - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + #self.assertEqual(len(self.mbox.boxes), 0) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_nonempty_maildir_cur(self): self.createMessage("cur") self.mbox = mailbox.Maildir(test_support.TESTFN) - #self.assertTrue(len(self.mbox.boxes) == 1) + #self.assertEqual(len(self.mbox.boxes), 1) msg = self.mbox.next() - self.assertIsNot(msg, None) + self.assertIsNotNone(msg) msg.fp.close() - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_nonempty_maildir_new(self): self.createMessage("new") self.mbox = mailbox.Maildir(test_support.TESTFN) - #self.assertTrue(len(self.mbox.boxes) == 1) + #self.assertEqual(len(self.mbox.boxes), 1) msg = self.mbox.next() - self.assertIsNot(msg, None) + self.assertIsNotNone(msg) msg.fp.close() - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_nonempty_maildir_both(self): self.createMessage("cur") self.createMessage("new") self.mbox = mailbox.Maildir(test_support.TESTFN) - #self.assertTrue(len(self.mbox.boxes) == 2) + #self.assertEqual(len(self.mbox.boxes), 2) msg = self.mbox.next() - self.assertIsNot(msg, None) + self.assertIsNotNone(msg) msg.fp.close() msg = self.mbox.next() - self.assertIsNot(msg, None) + self.assertIsNotNone(msg) msg.fp.close() - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_unix_mbox(self): ### should be better! diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -308,7 +308,7 @@ serv.close() def testTimeoutDefault(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: pop = poplib.POP3(HOST, self.port) @@ -318,13 +318,13 @@ pop.sock.close() def testTimeoutNone(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: pop = poplib.POP3(HOST, self.port, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(pop.sock.gettimeout() is None) + self.assertIsNone(pop.sock.gettimeout()) pop.sock.close() def testTimeoutValue(self): diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -77,7 +77,7 @@ smtp.close() def testTimeoutDefault(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: smtp = smtplib.SMTP(HOST, self.port) @@ -87,13 +87,13 @@ smtp.close() def testTimeoutNone(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: smtp = smtplib.SMTP(HOST, self.port, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(smtp.sock.gettimeout() is None) + self.assertIsNone(smtp.sock.gettimeout()) smtp.close() def testTimeoutValue(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:08:08 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 14:08:08 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTQ5?= =?utf-8?q?=3A_Use_specific_asserts_in_mailbox=2C_smtplib_and_poplib_tests?= =?utf-8?q?=2E?= Message-ID: <3fLtwS08sZz7Lmn@mail.python.org> http://hg.python.org/cpython/rev/bd1d6916b689 changeset: 89041:bd1d6916b689 branch: 3.3 parent: 89038:b6f8d5148a05 user: Serhiy Storchaka date: Sat Feb 08 15:06:08 2014 +0200 summary: Issue #20549: Use specific asserts in mailbox, smtplib and poplib tests. files: Lib/test/test_mailbox.py | 47 ++++++++++++++------------- Lib/test/test_poplib.py | 6 +- Lib/test/test_smtplib.py | 6 +- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -233,7 +233,7 @@ msg = self._box.get(key0) self.assertEqual(msg['from'], 'foo') self.assertEqual(msg.get_payload(), '0\n') - self.assertIs(self._box.get('foo'), None) + self.assertIsNone(self._box.get('foo')) self.assertIs(self._box.get('foo', False), False) self._box.close() self._box = self._factory(self._path) @@ -760,7 +760,7 @@ "tmp")), "File in wrong location: '%s'" % head) match = pattern.match(tail) - self.assertIsNot(match, None, "Invalid file name: '%s'" % tail) + self.assertIsNotNone(match, "Invalid file name: '%s'" % tail) groups = match.groups() if previous_groups is not None: self.assertGreaterEqual(int(groups[0]), int(previous_groups[0]), @@ -1394,7 +1394,7 @@ self.assertIsInstance(msg, self._factory) self.assertEqual(msg.keys(), []) self.assertFalse(msg.is_multipart()) - self.assertEqual(msg.get_payload(), None) + self.assertIsNone(msg.get_payload()) def test_initialize_incorrectly(self): # Initialize with invalid argument @@ -1405,7 +1405,7 @@ eMM = email.message_from_string(_sample_message) msg = self._factory(_sample_message) for attr in eMM.__dict__: - self.assertTrue(attr in msg.__dict__, + self.assertIn(attr, msg.__dict__, '{} attribute does not exist'.format(attr)) def test_become_message(self): @@ -1547,8 +1547,9 @@ # Check contents of "From " line if sender is None: sender = "MAILER-DAEMON" - self.assertTrue(re.match(sender + r" \w{3} \w{3} [\d ]\d [\d ]\d:\d{2}:" - r"\d{2} \d{4}", msg.get_from()) is not None) + self.assertIsNotNone(re.match( + sender + r" \w{3} \w{3} [\d ]\d [\d ]\d:\d{2}:\d{2} \d{4}", + msg.get_from())) class TestMboxMessage(_TestMboxMMDFMessage, TestMessage): @@ -1622,19 +1623,19 @@ msg = mailbox.BabylMessage(_sample_message) visible = msg.get_visible() self.assertEqual(visible.keys(), []) - self.assertIs(visible.get_payload(), None) + self.assertIsNone(visible.get_payload()) visible['User-Agent'] = 'FooBar 1.0' visible['X-Whatever'] = 'Blah' self.assertEqual(msg.get_visible().keys(), []) msg.set_visible(visible) visible = msg.get_visible() - self.assertTrue(visible.keys() == ['User-Agent', 'X-Whatever']) - self.assertTrue(visible['User-Agent'] == 'FooBar 1.0') + self.assertEqual(visible.keys(), ['User-Agent', 'X-Whatever']) + self.assertEqual(visible['User-Agent'], 'FooBar 1.0') self.assertEqual(visible['X-Whatever'], 'Blah') - self.assertIs(visible.get_payload(), None) + self.assertIsNone(visible.get_payload()) msg.update_visible() self.assertEqual(visible.keys(), ['User-Agent', 'X-Whatever']) - self.assertIs(visible.get_payload(), None) + self.assertIsNone(visible.get_payload()) visible = msg.get_visible() self.assertEqual(visible.keys(), ['User-Agent', 'Date', 'From', 'To', 'Subject']) @@ -2156,34 +2157,34 @@ self.mbox = mailbox.Maildir(support.TESTFN) #self.assertTrue(hasattr(self.mbox, "boxes")) #self.assertEqual(len(self.mbox.boxes), 0) - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_nonempty_maildir_cur(self): self.createMessage("cur") self.mbox = mailbox.Maildir(support.TESTFN) #self.assertEqual(len(self.mbox.boxes), 1) - self.assertIsNot(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNotNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_nonempty_maildir_new(self): self.createMessage("new") self.mbox = mailbox.Maildir(support.TESTFN) #self.assertEqual(len(self.mbox.boxes), 1) - self.assertIsNot(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNotNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_nonempty_maildir_both(self): self.createMessage("cur") self.createMessage("new") self.mbox = mailbox.Maildir(support.TESTFN) #self.assertEqual(len(self.mbox.boxes), 2) - self.assertIsNot(self.mbox.next(), None) - self.assertIsNot(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNotNone(self.mbox.next()) + self.assertIsNotNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) ## End: tests from the original module (for backward compatibility). diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -350,7 +350,7 @@ serv.close() def testTimeoutDefault(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: pop = poplib.POP3(HOST, self.port) @@ -360,13 +360,13 @@ pop.sock.close() def testTimeoutNone(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: pop = poplib.POP3(HOST, self.port, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(pop.sock.gettimeout() is None) + self.assertIsNone(pop.sock.gettimeout()) pop.sock.close() def testTimeoutValue(self): diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -96,7 +96,7 @@ def testTimeoutDefault(self): mock_socket.reply_with(b"220 Hola mundo") - self.assertTrue(mock_socket.getdefaulttimeout() is None) + self.assertIsNone(mock_socket.getdefaulttimeout()) mock_socket.setdefaulttimeout(30) self.assertEqual(mock_socket.getdefaulttimeout(), 30) try: @@ -108,13 +108,13 @@ def testTimeoutNone(self): mock_socket.reply_with(b"220 Hola mundo") - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: smtp = smtplib.SMTP(HOST, self.port, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(smtp.sock.gettimeout() is None) + self.assertIsNone(smtp.sock.gettimeout()) smtp.close() def testTimeoutValue(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:08:09 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 14:08:09 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320549=3A_Use_specific_asserts_in_mailbox=2C_smt?= =?utf-8?q?plib_and_poplib_tests=2E?= Message-ID: <3fLtwT2hvbz7LmZ@mail.python.org> http://hg.python.org/cpython/rev/2e5e66f9e228 changeset: 89042:2e5e66f9e228 parent: 89039:868675190b34 parent: 89041:bd1d6916b689 user: Serhiy Storchaka date: Sat Feb 08 15:06:43 2014 +0200 summary: Issue #20549: Use specific asserts in mailbox, smtplib and poplib tests. files: Lib/test/test_mailbox.py | 47 ++++++++++++++------------- Lib/test/test_poplib.py | 6 +- Lib/test/test_smtplib.py | 6 +- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -233,7 +233,7 @@ msg = self._box.get(key0) self.assertEqual(msg['from'], 'foo') self.assertEqual(msg.get_payload(), '0\n') - self.assertIs(self._box.get('foo'), None) + self.assertIsNone(self._box.get('foo')) self.assertIs(self._box.get('foo', False), False) self._box.close() self._box = self._factory(self._path) @@ -760,7 +760,7 @@ "tmp")), "File in wrong location: '%s'" % head) match = pattern.match(tail) - self.assertIsNot(match, None, "Invalid file name: '%s'" % tail) + self.assertIsNotNone(match, "Invalid file name: '%s'" % tail) groups = match.groups() if previous_groups is not None: self.assertGreaterEqual(int(groups[0]), int(previous_groups[0]), @@ -1394,7 +1394,7 @@ self.assertIsInstance(msg, self._factory) self.assertEqual(msg.keys(), []) self.assertFalse(msg.is_multipart()) - self.assertEqual(msg.get_payload(), None) + self.assertIsNone(msg.get_payload()) def test_initialize_incorrectly(self): # Initialize with invalid argument @@ -1405,7 +1405,7 @@ eMM = email.message_from_string(_sample_message) msg = self._factory(_sample_message) for attr in eMM.__dict__: - self.assertTrue(attr in msg.__dict__, + self.assertIn(attr, msg.__dict__, '{} attribute does not exist'.format(attr)) def test_become_message(self): @@ -1547,8 +1547,9 @@ # Check contents of "From " line if sender is None: sender = "MAILER-DAEMON" - self.assertTrue(re.match(sender + r" \w{3} \w{3} [\d ]\d [\d ]\d:\d{2}:" - r"\d{2} \d{4}", msg.get_from()) is not None) + self.assertIsNotNone(re.match( + sender + r" \w{3} \w{3} [\d ]\d [\d ]\d:\d{2}:\d{2} \d{4}", + msg.get_from())) class TestMboxMessage(_TestMboxMMDFMessage, TestMessage): @@ -1622,19 +1623,19 @@ msg = mailbox.BabylMessage(_sample_message) visible = msg.get_visible() self.assertEqual(visible.keys(), []) - self.assertIs(visible.get_payload(), None) + self.assertIsNone(visible.get_payload()) visible['User-Agent'] = 'FooBar 1.0' visible['X-Whatever'] = 'Blah' self.assertEqual(msg.get_visible().keys(), []) msg.set_visible(visible) visible = msg.get_visible() - self.assertTrue(visible.keys() == ['User-Agent', 'X-Whatever']) - self.assertTrue(visible['User-Agent'] == 'FooBar 1.0') + self.assertEqual(visible.keys(), ['User-Agent', 'X-Whatever']) + self.assertEqual(visible['User-Agent'], 'FooBar 1.0') self.assertEqual(visible['X-Whatever'], 'Blah') - self.assertIs(visible.get_payload(), None) + self.assertIsNone(visible.get_payload()) msg.update_visible() self.assertEqual(visible.keys(), ['User-Agent', 'X-Whatever']) - self.assertIs(visible.get_payload(), None) + self.assertIsNone(visible.get_payload()) visible = msg.get_visible() self.assertEqual(visible.keys(), ['User-Agent', 'Date', 'From', 'To', 'Subject']) @@ -2156,34 +2157,34 @@ self.mbox = mailbox.Maildir(support.TESTFN) #self.assertTrue(hasattr(self.mbox, "boxes")) #self.assertEqual(len(self.mbox.boxes), 0) - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_nonempty_maildir_cur(self): self.createMessage("cur") self.mbox = mailbox.Maildir(support.TESTFN) #self.assertEqual(len(self.mbox.boxes), 1) - self.assertIsNot(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNotNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_nonempty_maildir_new(self): self.createMessage("new") self.mbox = mailbox.Maildir(support.TESTFN) #self.assertEqual(len(self.mbox.boxes), 1) - self.assertIsNot(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNotNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) def test_nonempty_maildir_both(self): self.createMessage("cur") self.createMessage("new") self.mbox = mailbox.Maildir(support.TESTFN) #self.assertEqual(len(self.mbox.boxes), 2) - self.assertIsNot(self.mbox.next(), None) - self.assertIsNot(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) - self.assertIs(self.mbox.next(), None) + self.assertIsNotNone(self.mbox.next()) + self.assertIsNotNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) + self.assertIsNone(self.mbox.next()) ## End: tests from the original module (for backward compatibility). diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -468,7 +468,7 @@ serv.close() def testTimeoutDefault(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: pop = poplib.POP3(HOST, self.port) @@ -478,13 +478,13 @@ pop.sock.close() def testTimeoutNone(self): - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: pop = poplib.POP3(HOST, self.port, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(pop.sock.gettimeout() is None) + self.assertIsNone(pop.sock.gettimeout()) pop.sock.close() def testTimeoutValue(self): diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -96,7 +96,7 @@ def testTimeoutDefault(self): mock_socket.reply_with(b"220 Hola mundo") - self.assertTrue(mock_socket.getdefaulttimeout() is None) + self.assertIsNone(mock_socket.getdefaulttimeout()) mock_socket.setdefaulttimeout(30) self.assertEqual(mock_socket.getdefaulttimeout(), 30) try: @@ -108,13 +108,13 @@ def testTimeoutNone(self): mock_socket.reply_with(b"220 Hola mundo") - self.assertTrue(socket.getdefaulttimeout() is None) + self.assertIsNone(socket.getdefaulttimeout()) socket.setdefaulttimeout(30) try: smtp = smtplib.SMTP(HOST, self.port, timeout=None) finally: socket.setdefaulttimeout(None) - self.assertTrue(smtp.sock.gettimeout() is None) + self.assertIsNone(smtp.sock.gettimeout()) smtp.close() def testTimeoutValue(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:21:14 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 8 Feb 2014 14:21:14 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzE4ODA1?= =?utf-8?q?=3A_better_netmask_validation_in_ipaddress?= Message-ID: <3fLvCZ0CThz7Lk9@mail.python.org> http://hg.python.org/cpython/rev/ca5ea7c24370 changeset: 89043:ca5ea7c24370 branch: 3.3 parent: 89041:bd1d6916b689 user: Nick Coghlan date: Sat Feb 08 23:17:36 2014 +1000 summary: Issue #18805: better netmask validation in ipaddress files: Lib/ipaddress.py | 148 ++++++++++++++---------- Lib/test/test_ipaddress.py | 73 ++++++++--- Misc/NEWS | 3 + 3 files changed, 139 insertions(+), 85 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -456,8 +456,8 @@ raise AddressValueError(msg % (address, address_len, expected_len, self._version)) - def _ip_int_from_prefix(self, prefixlen=None): - """Turn the prefix length netmask into a int for comparison. + def _ip_int_from_prefix(self, prefixlen): + """Turn the prefix length into a bitwise netmask Args: prefixlen: An integer, the prefix length. @@ -466,36 +466,92 @@ An integer. """ - if prefixlen is None: - prefixlen = self._prefixlen return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen) - def _prefix_from_ip_int(self, ip_int, mask=32): - """Return prefix length from the decimal netmask. + def _prefix_from_ip_int(self, ip_int): + """Return prefix length from the bitwise netmask. Args: - ip_int: An integer, the IP address. - mask: The netmask. Defaults to 32. + ip_int: An integer, the netmask in axpanded bitwise format Returns: An integer, the prefix length. + Raises: + ValueError: If the input intermingles zeroes & ones """ - return mask - _count_righthand_zero_bits(ip_int, mask) + trailing_zeroes = _count_righthand_zero_bits(ip_int, + self._max_prefixlen) + prefixlen = self._max_prefixlen - trailing_zeroes + leading_ones = ip_int >> trailing_zeroes + all_ones = (1 << prefixlen) - 1 + if leading_ones != all_ones: + byteslen = self._max_prefixlen // 8 + details = ip_int.to_bytes(byteslen, 'big') + msg = 'Netmask pattern %r mixes zeroes & ones' + raise ValueError(msg % details) + return prefixlen - def _ip_string_from_prefix(self, prefixlen=None): - """Turn a prefix length into a dotted decimal string. + def _report_invalid_netmask(self, netmask_str): + msg = '%r is not a valid netmask' % netmask_str + raise NetmaskValueError(msg) from None + + def _prefix_from_prefix_string(self, prefixlen_str): + """Return prefix length from a numeric string Args: - prefixlen: An integer, the netmask prefix length. + prefixlen_str: The string to be converted Returns: - A string, the dotted decimal netmask string. + An integer, the prefix length. + Raises: + NetmaskValueError: If the input is not a valid netmask """ - if not prefixlen: - prefixlen = self._prefixlen - return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen)) + # int allows a leading +/- as well as surrounding whitespace, + # so we ensure that isn't the case + if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str): + self._report_invalid_netmask(prefixlen_str) + try: + prefixlen = int(prefixlen_str) + except ValueError: + self._report_invalid_netmask(prefixlen_str) + if not (0 <= prefixlen <= self._max_prefixlen): + self._report_invalid_netmask(prefixlen_str) + return prefixlen + + def _prefix_from_ip_string(self, ip_str): + """Turn a netmask/hostmask string into a prefix length + + Args: + ip_str: The netmask/hostmask to be converted + + Returns: + An integer, the prefix length. + + Raises: + NetmaskValueError: If the input is not a valid netmask/hostmask + """ + # Parse the netmask/hostmask like an IP address. + try: + ip_int = self._ip_int_from_string(ip_str) + except AddressValueError: + self._report_invalid_netmask(ip_str) + + # Try matching a netmask (this would be /1*0*/ as a bitwise regexp). + # Note that the two ambiguous cases (all-ones and all-zeroes) are + # treated as netmasks. + try: + return self._prefix_from_ip_int(ip_int) + except ValueError: + pass + + # Invert the bits, and try matching a /0+1+/ hostmask instead. + ip_int ^= self._ALL_ONES + try: + return self._prefix_from_ip_int(ip_int) + except ValueError: + self._report_invalid_netmask(ip_str) class _BaseAddress(_IPAddressBase): @@ -504,7 +560,6 @@ This IP class contains the version independent methods which are used by single IP addresses. - """ def __init__(self, address): @@ -873,7 +928,7 @@ raise ValueError('prefix length diff must be > 0') new_prefixlen = self._prefixlen + prefixlen_diff - if not self._is_valid_netmask(str(new_prefixlen)): + if new_prefixlen > self._max_prefixlen: raise ValueError( 'prefix length diff %d is invalid for netblock %s' % ( new_prefixlen, self)) @@ -1428,33 +1483,16 @@ self.network_address = IPv4Address(self._ip_int_from_string(addr[0])) if len(addr) == 2: - mask = addr[1].split('.') - - if len(mask) == 4: - # We have dotted decimal netmask. - if self._is_valid_netmask(addr[1]): - self.netmask = IPv4Address(self._ip_int_from_string( - addr[1])) - elif self._is_hostmask(addr[1]): - self.netmask = IPv4Address( - self._ip_int_from_string(addr[1]) ^ self._ALL_ONES) - else: - raise NetmaskValueError('%r is not a valid netmask' - % addr[1]) - - self._prefixlen = self._prefix_from_ip_int(int(self.netmask)) - else: - # We have a netmask in prefix length form. - if not self._is_valid_netmask(addr[1]): - raise NetmaskValueError('%r is not a valid netmask' - % addr[1]) - self._prefixlen = int(addr[1]) - self.netmask = IPv4Address(self._ip_int_from_prefix( - self._prefixlen)) + try: + # Check for a netmask in prefix length form + self._prefixlen = self._prefix_from_prefix_string(addr[1]) + except NetmaskValueError: + # Check for a netmask or hostmask in dotted-quad form. + # This may raise NetmaskValueError. + self._prefixlen = self._prefix_from_ip_string(addr[1]) else: self._prefixlen = self._max_prefixlen - self.netmask = IPv4Address(self._ip_int_from_prefix( - self._prefixlen)) + self.netmask = IPv4Address(self._ip_int_from_prefix(self._prefixlen)) if strict: if (IPv4Address(int(self.network_address) & int(self.netmask)) != @@ -2042,11 +2080,8 @@ self.network_address = IPv6Address(self._ip_int_from_string(addr[0])) if len(addr) == 2: - if self._is_valid_netmask(addr[1]): - self._prefixlen = int(addr[1]) - else: - raise NetmaskValueError('%r is not a valid netmask' - % addr[1]) + # This may raise NetmaskValueError + self._prefixlen = self._prefix_from_prefix_string(addr[1]) else: self._prefixlen = self._max_prefixlen @@ -2061,23 +2096,6 @@ if self._prefixlen == (self._max_prefixlen - 1): self.hosts = self.__iter__ - def _is_valid_netmask(self, prefixlen): - """Verify that the netmask/prefixlen is valid. - - Args: - prefixlen: A string, the netmask in prefix length format. - - Returns: - A boolean, True if the prefix represents a valid IPv6 - netmask. - - """ - try: - prefixlen = int(prefixlen) - except ValueError: - return False - return 0 <= prefixlen <= self._max_prefixlen - @property def is_site_local(self): """Test if the address is reserved for site-local. diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -398,18 +398,47 @@ assertBadAddress("::1.2.3.4", "Only decimal digits") assertBadAddress("1.2.3.256", re.escape("256 (> 255)")) + def test_valid_netmask(self): + self.assertEqual(str(self.factory('192.0.2.0/255.255.255.0')), + '192.0.2.0/24') + for i in range(0, 33): + # Generate and re-parse the CIDR format (trivial). + net_str = '0.0.0.0/%d' % i + net = self.factory(net_str) + self.assertEqual(str(net), net_str) + # Generate and re-parse the expanded netmask. + self.assertEqual( + str(self.factory('0.0.0.0/%s' % net.netmask)), net_str) + # Zero prefix is treated as decimal. + self.assertEqual(str(self.factory('0.0.0.0/0%d' % i)), net_str) + # Generate and re-parse the expanded hostmask. The ambiguous + # cases (/0 and /32) are treated as netmasks. + if i in (32, 0): + net_str = '0.0.0.0/%d' % (32 - i) + self.assertEqual( + str(self.factory('0.0.0.0/%s' % net.hostmask)), net_str) + def test_netmask_errors(self): def assertBadNetmask(addr, netmask): - msg = "%r is not a valid netmask" - with self.assertNetmaskError(msg % netmask): + msg = "%r is not a valid netmask" % netmask + with self.assertNetmaskError(re.escape(msg)): self.factory("%s/%s" % (addr, netmask)) assertBadNetmask("1.2.3.4", "") + assertBadNetmask("1.2.3.4", "-1") + assertBadNetmask("1.2.3.4", "+1") + assertBadNetmask("1.2.3.4", " 1 ") + assertBadNetmask("1.2.3.4", "0x1") assertBadNetmask("1.2.3.4", "33") assertBadNetmask("1.2.3.4", "254.254.255.256") + assertBadNetmask("1.2.3.4", "1.a.2.3") assertBadNetmask("1.1.1.1", "254.xyz.2.3") assertBadNetmask("1.1.1.1", "240.255.0.0") + assertBadNetmask("1.1.1.1", "255.254.128.0") + assertBadNetmask("1.1.1.1", "0.1.127.255") assertBadNetmask("1.1.1.1", "pudding") + assertBadNetmask("1.1.1.1", "::") + class InterfaceTestCase_v4(BaseTestCase, NetmaskTestMixin_v4): factory = ipaddress.IPv4Interface @@ -438,17 +467,34 @@ assertBadAddress("10/8", "At least 3 parts") assertBadAddress("1234:axy::b", "Only hex digits") + def test_valid_netmask(self): + # We only support CIDR for IPv6, because expanded netmasks are not + # standard notation. + self.assertEqual(str(self.factory('2001:db8::/32')), '2001:db8::/32') + for i in range(0, 129): + # Generate and re-parse the CIDR format (trivial). + net_str = '::/%d' % i + self.assertEqual(str(self.factory(net_str)), net_str) + # Zero prefix is treated as decimal. + self.assertEqual(str(self.factory('::/0%d' % i)), net_str) + def test_netmask_errors(self): def assertBadNetmask(addr, netmask): - msg = "%r is not a valid netmask" - with self.assertNetmaskError(msg % netmask): + msg = "%r is not a valid netmask" % netmask + with self.assertNetmaskError(re.escape(msg)): self.factory("%s/%s" % (addr, netmask)) assertBadNetmask("::1", "") assertBadNetmask("::1", "::1") assertBadNetmask("::1", "1::") + assertBadNetmask("::1", "-1") + assertBadNetmask("::1", "+1") + assertBadNetmask("::1", " 1 ") + assertBadNetmask("::1", "0x1") assertBadNetmask("::1", "129") + assertBadNetmask("::1", "1.2.3.4") assertBadNetmask("::1", "pudding") + assertBadNetmask("::", "::") class InterfaceTestCase_v6(BaseTestCase, NetmaskTestMixin_v6): factory = ipaddress.IPv6Interface @@ -694,16 +740,14 @@ def testZeroNetmask(self): ipv4_zero_netmask = ipaddress.IPv4Interface('1.2.3.4/0') self.assertEqual(int(ipv4_zero_netmask.network.netmask), 0) - self.assertTrue(ipv4_zero_netmask.network._is_valid_netmask( - str(0))) + self.assertEqual(ipv4_zero_netmask._prefix_from_prefix_string('0'), 0) self.assertTrue(ipv4_zero_netmask._is_valid_netmask('0')) self.assertTrue(ipv4_zero_netmask._is_valid_netmask('0.0.0.0')) self.assertFalse(ipv4_zero_netmask._is_valid_netmask('invalid')) ipv6_zero_netmask = ipaddress.IPv6Interface('::1/0') self.assertEqual(int(ipv6_zero_netmask.network.netmask), 0) - self.assertTrue(ipv6_zero_netmask.network._is_valid_netmask( - str(0))) + self.assertEqual(ipv6_zero_netmask._prefix_from_prefix_string('0'), 0) def testIPv4NetAndHostmasks(self): net = self.ipv4_network @@ -719,7 +763,7 @@ self.assertFalse(net._is_hostmask('1.2.3.4')) net = ipaddress.IPv4Network('127.0.0.0/0.0.0.255') - self.assertEqual(24, net.prefixlen) + self.assertEqual(net.prefixlen, 24) def testGetBroadcast(self): self.assertEqual(int(self.ipv4_network.broadcast_address), 16909311) @@ -1271,11 +1315,6 @@ self.assertEqual(ipaddress.IPv6Interface('::1:0:0:0:0').packed, b'\x00' * 6 + b'\x00\x01' + b'\x00' * 8) - def testIpStrFromPrefixlen(self): - ipv4 = ipaddress.IPv4Interface('1.2.3.4/24') - self.assertEqual(ipv4._ip_string_from_prefix(), '255.255.255.0') - self.assertEqual(ipv4._ip_string_from_prefix(28), '255.255.255.240') - def testIpType(self): ipv4net = ipaddress.ip_network('1.2.3.4') ipv4addr = ipaddress.ip_address('1.2.3.4') @@ -1467,14 +1506,8 @@ def testIPBases(self): net = self.ipv4_network self.assertEqual('1.2.3.0/24', net.compressed) - self.assertEqual( - net._ip_int_from_prefix(24), - net._ip_int_from_prefix(None)) net = self.ipv6_network self.assertRaises(ValueError, net._string_from_ip_int, 2**128 + 1) - self.assertEqual( - self.ipv6_address._string_from_ip_int(self.ipv6_address._ip), - self.ipv6_address._string_from_ip_int(None)) def testIPv6NetworkHelpers(self): net = self.ipv6_network diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -48,6 +48,9 @@ Library ------- +- Issue #18805: the netmask/hostmask parsing in ipaddress now more reliably + filters out illegal values + - Issue #17369: get_filename was raising an exception if the filename parameter's RFC2231 encoding was broken in certain ways. This was a regression relative to python2. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:21:15 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 8 Feb 2014 14:21:15 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge_fix_for_=2318805_from_3=2E3?= Message-ID: <3fLvCb3vSTz7LkD@mail.python.org> http://hg.python.org/cpython/rev/90dfb26869a7 changeset: 89044:90dfb26869a7 parent: 89042:2e5e66f9e228 parent: 89043:ca5ea7c24370 user: Nick Coghlan date: Sat Feb 08 23:20:58 2014 +1000 summary: Merge fix for #18805 from 3.3 files: Lib/ipaddress.py | 148 ++++++++++++++---------- Lib/test/test_ipaddress.py | 73 ++++++++--- Misc/NEWS | 3 + 3 files changed, 139 insertions(+), 85 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -456,8 +456,8 @@ raise AddressValueError(msg % (address, address_len, expected_len, self._version)) - def _ip_int_from_prefix(self, prefixlen=None): - """Turn the prefix length netmask into a int for comparison. + def _ip_int_from_prefix(self, prefixlen): + """Turn the prefix length into a bitwise netmask Args: prefixlen: An integer, the prefix length. @@ -466,36 +466,92 @@ An integer. """ - if prefixlen is None: - prefixlen = self._prefixlen return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen) - def _prefix_from_ip_int(self, ip_int, mask=32): - """Return prefix length from the decimal netmask. + def _prefix_from_ip_int(self, ip_int): + """Return prefix length from the bitwise netmask. Args: - ip_int: An integer, the IP address. - mask: The netmask. Defaults to 32. + ip_int: An integer, the netmask in axpanded bitwise format Returns: An integer, the prefix length. + Raises: + ValueError: If the input intermingles zeroes & ones """ - return mask - _count_righthand_zero_bits(ip_int, mask) + trailing_zeroes = _count_righthand_zero_bits(ip_int, + self._max_prefixlen) + prefixlen = self._max_prefixlen - trailing_zeroes + leading_ones = ip_int >> trailing_zeroes + all_ones = (1 << prefixlen) - 1 + if leading_ones != all_ones: + byteslen = self._max_prefixlen // 8 + details = ip_int.to_bytes(byteslen, 'big') + msg = 'Netmask pattern %r mixes zeroes & ones' + raise ValueError(msg % details) + return prefixlen - def _ip_string_from_prefix(self, prefixlen=None): - """Turn a prefix length into a dotted decimal string. + def _report_invalid_netmask(self, netmask_str): + msg = '%r is not a valid netmask' % netmask_str + raise NetmaskValueError(msg) from None + + def _prefix_from_prefix_string(self, prefixlen_str): + """Return prefix length from a numeric string Args: - prefixlen: An integer, the netmask prefix length. + prefixlen_str: The string to be converted Returns: - A string, the dotted decimal netmask string. + An integer, the prefix length. + Raises: + NetmaskValueError: If the input is not a valid netmask """ - if not prefixlen: - prefixlen = self._prefixlen - return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen)) + # int allows a leading +/- as well as surrounding whitespace, + # so we ensure that isn't the case + if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str): + self._report_invalid_netmask(prefixlen_str) + try: + prefixlen = int(prefixlen_str) + except ValueError: + self._report_invalid_netmask(prefixlen_str) + if not (0 <= prefixlen <= self._max_prefixlen): + self._report_invalid_netmask(prefixlen_str) + return prefixlen + + def _prefix_from_ip_string(self, ip_str): + """Turn a netmask/hostmask string into a prefix length + + Args: + ip_str: The netmask/hostmask to be converted + + Returns: + An integer, the prefix length. + + Raises: + NetmaskValueError: If the input is not a valid netmask/hostmask + """ + # Parse the netmask/hostmask like an IP address. + try: + ip_int = self._ip_int_from_string(ip_str) + except AddressValueError: + self._report_invalid_netmask(ip_str) + + # Try matching a netmask (this would be /1*0*/ as a bitwise regexp). + # Note that the two ambiguous cases (all-ones and all-zeroes) are + # treated as netmasks. + try: + return self._prefix_from_ip_int(ip_int) + except ValueError: + pass + + # Invert the bits, and try matching a /0+1+/ hostmask instead. + ip_int ^= self._ALL_ONES + try: + return self._prefix_from_ip_int(ip_int) + except ValueError: + self._report_invalid_netmask(ip_str) class _BaseAddress(_IPAddressBase): @@ -504,7 +560,6 @@ This IP class contains the version independent methods which are used by single IP addresses. - """ def __init__(self, address): @@ -873,7 +928,7 @@ raise ValueError('prefix length diff must be > 0') new_prefixlen = self._prefixlen + prefixlen_diff - if not self._is_valid_netmask(str(new_prefixlen)): + if new_prefixlen > self._max_prefixlen: raise ValueError( 'prefix length diff %d is invalid for netblock %s' % ( new_prefixlen, self)) @@ -1451,33 +1506,16 @@ self.network_address = IPv4Address(self._ip_int_from_string(addr[0])) if len(addr) == 2: - mask = addr[1].split('.') - - if len(mask) == 4: - # We have dotted decimal netmask. - if self._is_valid_netmask(addr[1]): - self.netmask = IPv4Address(self._ip_int_from_string( - addr[1])) - elif self._is_hostmask(addr[1]): - self.netmask = IPv4Address( - self._ip_int_from_string(addr[1]) ^ self._ALL_ONES) - else: - raise NetmaskValueError('%r is not a valid netmask' - % addr[1]) - - self._prefixlen = self._prefix_from_ip_int(int(self.netmask)) - else: - # We have a netmask in prefix length form. - if not self._is_valid_netmask(addr[1]): - raise NetmaskValueError('%r is not a valid netmask' - % addr[1]) - self._prefixlen = int(addr[1]) - self.netmask = IPv4Address(self._ip_int_from_prefix( - self._prefixlen)) + try: + # Check for a netmask in prefix length form + self._prefixlen = self._prefix_from_prefix_string(addr[1]) + except NetmaskValueError: + # Check for a netmask or hostmask in dotted-quad form. + # This may raise NetmaskValueError. + self._prefixlen = self._prefix_from_ip_string(addr[1]) else: self._prefixlen = self._max_prefixlen - self.netmask = IPv4Address(self._ip_int_from_prefix( - self._prefixlen)) + self.netmask = IPv4Address(self._ip_int_from_prefix(self._prefixlen)) if strict: if (IPv4Address(int(self.network_address) & int(self.netmask)) != @@ -2101,11 +2139,8 @@ self.network_address = IPv6Address(self._ip_int_from_string(addr[0])) if len(addr) == 2: - if self._is_valid_netmask(addr[1]): - self._prefixlen = int(addr[1]) - else: - raise NetmaskValueError('%r is not a valid netmask' - % addr[1]) + # This may raise NetmaskValueError + self._prefixlen = self._prefix_from_prefix_string(addr[1]) else: self._prefixlen = self._max_prefixlen @@ -2120,23 +2155,6 @@ if self._prefixlen == (self._max_prefixlen - 1): self.hosts = self.__iter__ - def _is_valid_netmask(self, prefixlen): - """Verify that the netmask/prefixlen is valid. - - Args: - prefixlen: A string, the netmask in prefix length format. - - Returns: - A boolean, True if the prefix represents a valid IPv6 - netmask. - - """ - try: - prefixlen = int(prefixlen) - except ValueError: - return False - return 0 <= prefixlen <= self._max_prefixlen - @property def is_site_local(self): """Test if the address is reserved for site-local. diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -398,18 +398,47 @@ assertBadAddress("::1.2.3.4", "Only decimal digits") assertBadAddress("1.2.3.256", re.escape("256 (> 255)")) + def test_valid_netmask(self): + self.assertEqual(str(self.factory('192.0.2.0/255.255.255.0')), + '192.0.2.0/24') + for i in range(0, 33): + # Generate and re-parse the CIDR format (trivial). + net_str = '0.0.0.0/%d' % i + net = self.factory(net_str) + self.assertEqual(str(net), net_str) + # Generate and re-parse the expanded netmask. + self.assertEqual( + str(self.factory('0.0.0.0/%s' % net.netmask)), net_str) + # Zero prefix is treated as decimal. + self.assertEqual(str(self.factory('0.0.0.0/0%d' % i)), net_str) + # Generate and re-parse the expanded hostmask. The ambiguous + # cases (/0 and /32) are treated as netmasks. + if i in (32, 0): + net_str = '0.0.0.0/%d' % (32 - i) + self.assertEqual( + str(self.factory('0.0.0.0/%s' % net.hostmask)), net_str) + def test_netmask_errors(self): def assertBadNetmask(addr, netmask): - msg = "%r is not a valid netmask" - with self.assertNetmaskError(msg % netmask): + msg = "%r is not a valid netmask" % netmask + with self.assertNetmaskError(re.escape(msg)): self.factory("%s/%s" % (addr, netmask)) assertBadNetmask("1.2.3.4", "") + assertBadNetmask("1.2.3.4", "-1") + assertBadNetmask("1.2.3.4", "+1") + assertBadNetmask("1.2.3.4", " 1 ") + assertBadNetmask("1.2.3.4", "0x1") assertBadNetmask("1.2.3.4", "33") assertBadNetmask("1.2.3.4", "254.254.255.256") + assertBadNetmask("1.2.3.4", "1.a.2.3") assertBadNetmask("1.1.1.1", "254.xyz.2.3") assertBadNetmask("1.1.1.1", "240.255.0.0") + assertBadNetmask("1.1.1.1", "255.254.128.0") + assertBadNetmask("1.1.1.1", "0.1.127.255") assertBadNetmask("1.1.1.1", "pudding") + assertBadNetmask("1.1.1.1", "::") + class InterfaceTestCase_v4(BaseTestCase, NetmaskTestMixin_v4): factory = ipaddress.IPv4Interface @@ -438,17 +467,34 @@ assertBadAddress("10/8", "At least 3 parts") assertBadAddress("1234:axy::b", "Only hex digits") + def test_valid_netmask(self): + # We only support CIDR for IPv6, because expanded netmasks are not + # standard notation. + self.assertEqual(str(self.factory('2001:db8::/32')), '2001:db8::/32') + for i in range(0, 129): + # Generate and re-parse the CIDR format (trivial). + net_str = '::/%d' % i + self.assertEqual(str(self.factory(net_str)), net_str) + # Zero prefix is treated as decimal. + self.assertEqual(str(self.factory('::/0%d' % i)), net_str) + def test_netmask_errors(self): def assertBadNetmask(addr, netmask): - msg = "%r is not a valid netmask" - with self.assertNetmaskError(msg % netmask): + msg = "%r is not a valid netmask" % netmask + with self.assertNetmaskError(re.escape(msg)): self.factory("%s/%s" % (addr, netmask)) assertBadNetmask("::1", "") assertBadNetmask("::1", "::1") assertBadNetmask("::1", "1::") + assertBadNetmask("::1", "-1") + assertBadNetmask("::1", "+1") + assertBadNetmask("::1", " 1 ") + assertBadNetmask("::1", "0x1") assertBadNetmask("::1", "129") + assertBadNetmask("::1", "1.2.3.4") assertBadNetmask("::1", "pudding") + assertBadNetmask("::", "::") class InterfaceTestCase_v6(BaseTestCase, NetmaskTestMixin_v6): factory = ipaddress.IPv6Interface @@ -694,16 +740,14 @@ def testZeroNetmask(self): ipv4_zero_netmask = ipaddress.IPv4Interface('1.2.3.4/0') self.assertEqual(int(ipv4_zero_netmask.network.netmask), 0) - self.assertTrue(ipv4_zero_netmask.network._is_valid_netmask( - str(0))) + self.assertEqual(ipv4_zero_netmask._prefix_from_prefix_string('0'), 0) self.assertTrue(ipv4_zero_netmask._is_valid_netmask('0')) self.assertTrue(ipv4_zero_netmask._is_valid_netmask('0.0.0.0')) self.assertFalse(ipv4_zero_netmask._is_valid_netmask('invalid')) ipv6_zero_netmask = ipaddress.IPv6Interface('::1/0') self.assertEqual(int(ipv6_zero_netmask.network.netmask), 0) - self.assertTrue(ipv6_zero_netmask.network._is_valid_netmask( - str(0))) + self.assertEqual(ipv6_zero_netmask._prefix_from_prefix_string('0'), 0) def testIPv4NetAndHostmasks(self): net = self.ipv4_network @@ -719,7 +763,7 @@ self.assertFalse(net._is_hostmask('1.2.3.4')) net = ipaddress.IPv4Network('127.0.0.0/0.0.0.255') - self.assertEqual(24, net.prefixlen) + self.assertEqual(net.prefixlen, 24) def testGetBroadcast(self): self.assertEqual(int(self.ipv4_network.broadcast_address), 16909311) @@ -1271,11 +1315,6 @@ self.assertEqual(ipaddress.IPv6Interface('::1:0:0:0:0').packed, b'\x00' * 6 + b'\x00\x01' + b'\x00' * 8) - def testIpStrFromPrefixlen(self): - ipv4 = ipaddress.IPv4Interface('1.2.3.4/24') - self.assertEqual(ipv4._ip_string_from_prefix(), '255.255.255.0') - self.assertEqual(ipv4._ip_string_from_prefix(28), '255.255.255.240') - def testIpType(self): ipv4net = ipaddress.ip_network('1.2.3.4') ipv4addr = ipaddress.ip_address('1.2.3.4') @@ -1479,14 +1518,8 @@ def testIPBases(self): net = self.ipv4_network self.assertEqual('1.2.3.0/24', net.compressed) - self.assertEqual( - net._ip_int_from_prefix(24), - net._ip_int_from_prefix(None)) net = self.ipv6_network self.assertRaises(ValueError, net._string_from_ip_int, 2**128 + 1) - self.assertEqual( - self.ipv6_address._string_from_ip_int(self.ipv6_address._ip), - self.ipv6_address._string_from_ip_int(None)) def testIPv6NetworkHelpers(self): net = self.ipv6_network diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,9 @@ Library ------- +- Issue #18805: the netmask/hostmask parsing in ipaddress now more reliably + filters out illegal values + - Issue #20481: For at least Python 3.4, the statistics module will require that all inputs for a single operation be of a single consistent type, or else a mixed of ints and a single other consistent type. This avoids -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:40:38 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 8 Feb 2014 14:40:38 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogRml4ICMxODgwNSBO?= =?utf-8?q?EWS_entry?= Message-ID: <3fLvdy0xZgz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/19ca11099f07 changeset: 89045:19ca11099f07 branch: 3.3 parent: 89043:ca5ea7c24370 user: Nick Coghlan date: Sat Feb 08 23:39:54 2014 +1000 summary: Fix #18805 NEWS entry files: Misc/NEWS | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -49,7 +49,7 @@ ------- - Issue #18805: the netmask/hostmask parsing in ipaddress now more reliably - filters out illegal values + filters out illegal values and correctly allows any valid prefix length. - Issue #17369: get_filename was raising an exception if the filename parameter's RFC2231 encoding was broken in certain ways. This was -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:40:39 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 8 Feb 2014 14:40:39 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge_=2318805_NEWS_fix_from_3=2E3?= Message-ID: <3fLvdz2vM0z7Ln4@mail.python.org> http://hg.python.org/cpython/rev/e0b1c937e57c changeset: 89046:e0b1c937e57c parent: 89044:90dfb26869a7 parent: 89045:19ca11099f07 user: Nick Coghlan date: Sat Feb 08 23:40:23 2014 +1000 summary: Merge #18805 NEWS fix from 3.3 files: Misc/NEWS | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -28,7 +28,7 @@ ------- - Issue #18805: the netmask/hostmask parsing in ipaddress now more reliably - filters out illegal values + filters out illegal values and correctly allows any valid prefix length. - Issue #20481: For at least Python 3.4, the statistics module will require that all inputs for a single operation be of a single consistent type, or -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 14:55:27 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 8 Feb 2014 14:55:27 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_=2320536=3A_correctl?= =?utf-8?q?y_handle_Decimal_exponents_in_statistics?= Message-ID: <3fLvz3181Qz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/0f9309f8c755 changeset: 89047:0f9309f8c755 user: Nick Coghlan date: Sat Feb 08 23:55:14 2014 +1000 summary: Close #20536: correctly handle Decimal exponents in statistics files: Lib/statistics.py | 6 +++- Lib/test/test_statistics.py | 38 +++++++++++++++++++++++++ Misc/NEWS | 3 + 3 files changed, 46 insertions(+), 1 deletions(-) diff --git a/Lib/statistics.py b/Lib/statistics.py --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -243,9 +243,13 @@ num = 0 for digit in digits: num = num*10 + digit + if exp < 0: + den = 10**-exp + else: + num *= 10**exp + den = 1 if sign: num = -num - den = 10**-exp return (num, den) diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -686,6 +686,38 @@ for d in (Decimal('NAN'), Decimal('sNAN'), Decimal('INF')): self.assertRaises(ValueError, statistics._decimal_to_ratio, d) + def test_sign(self): + # Test sign is calculated correctly. + numbers = [Decimal("9.8765e12"), Decimal("9.8765e-12")] + for d in numbers: + # First test positive decimals. + assert d > 0 + num, den = statistics._decimal_to_ratio(d) + self.assertGreaterEqual(num, 0) + self.assertGreater(den, 0) + # Then test negative decimals. + num, den = statistics._decimal_to_ratio(-d) + self.assertLessEqual(num, 0) + self.assertGreater(den, 0) + + def test_negative_exponent(self): + # Test result when the exponent is negative. + t = statistics._decimal_to_ratio(Decimal("0.1234")) + self.assertEqual(t, (1234, 10000)) + + def test_positive_exponent(self): + # Test results when the exponent is positive. + t = statistics._decimal_to_ratio(Decimal("1.234e7")) + self.assertEqual(t, (12340000, 1)) + + def test_regression_20536(self): + # Regression test for issue 20536. + # See http://bugs.python.org/issue20536 + t = statistics._decimal_to_ratio(Decimal("1e2")) + self.assertEqual(t, (100, 1)) + t = statistics._decimal_to_ratio(Decimal("1.47e5")) + self.assertEqual(t, (147000, 1)) + class CheckTypeTest(unittest.TestCase): # Test _check_type private function. @@ -1074,6 +1106,12 @@ actual = self.func(data*2) self.assertApproxEqual(actual, expected) + def test_regression_20561(self): + # Regression test for issue 20561. + # See http://bugs.python.org/issue20561 + d = Decimal('1e4') + self.assertEqual(statistics.mean([d]), d) + class TestMedian(NumericTestCase, AverageMixin): # Common tests for median and all median.* functions. diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,9 @@ Library ------- +- Issue #20536: the statistics module now correctly handle Decimal instances + with positive exponents + - Issue #18805: the netmask/hostmask parsing in ipaddress now more reliably filters out illegal values and correctly allows any valid prefix length. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 15:05:55 2014 From: python-checkins at python.org (terry.reedy) Date: Sat, 8 Feb 2014 15:05:55 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNDA2?= =?utf-8?q?=3A_Use_Python_application_icons_for_Idle_window_title_bars=2E?= Message-ID: <3fLwC71fyNz7LkD@mail.python.org> http://hg.python.org/cpython/rev/bda1739215b4 changeset: 89048:bda1739215b4 branch: 3.3 parent: 89045:19ca11099f07 user: Terry Jan Reedy date: Sat Feb 08 09:02:26 2014 -0500 summary: Issue #20406: Use Python application icons for Idle window title bars. Patch mostly by Serhiy Storchaka. files: Lib/idlelib/Icons/idle.ico | Bin Lib/idlelib/Icons/idle_16.gif | Bin Lib/idlelib/Icons/idle_16.png | Bin Lib/idlelib/Icons/idle_32.gif | Bin Lib/idlelib/Icons/idle_32.png | Bin Lib/idlelib/Icons/idle_48.gif | Bin Lib/idlelib/Icons/idle_48.png | Bin Lib/idlelib/PyShell.py | 14 +++++++++++++- Misc/NEWS | 3 +++ 9 files changed, 16 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/Icons/idle.ico b/Lib/idlelib/Icons/idle.ico new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3357aef14888c501bcd7bfe02393760306a18d06 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_16.gif b/Lib/idlelib/Icons/idle_16.gif new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9f001b1d79cf697bb81bde59a5bc2f8341f74ba6 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_16.png b/Lib/idlelib/Icons/idle_16.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6abde0af90cb6bc9dfc71cc4d8fd17abc3c7bf53 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_32.gif b/Lib/idlelib/Icons/idle_32.gif new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..af5b2d52cce8951a47ac1604b9332b9032c0ce2d GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_32.png b/Lib/idlelib/Icons/idle_32.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..41b70dbc37766a9f229f4e0b88ddc2f13d22e3db GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_48.gif b/Lib/idlelib/Icons/idle_48.gif new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fc5304f31eed32d5f7769e471c3817b58edc10e4 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_48.png b/Lib/idlelib/Icons/idle_48.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e5fa9280e21f86b0e5e750b02279b93e36009589 GIT binary patch [stripped] diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -16,7 +16,7 @@ import linecache from code import InteractiveInterpreter -from platform import python_version +from platform import python_version, system try: from tkinter import * @@ -1524,6 +1524,18 @@ # start editor and/or shell windows: root = Tk(className="Idle") + # set application icon + icondir = os.path.join(os.path.dirname(__file__), 'Icons') + if system() == 'Windows': + iconfile = os.path.join(icondir, 'idle.ico') + root.wm_iconbitmap(default=iconfile) + elif TkVersion >= 8.5: + ext = '.png' if TkVersion >= 8.6 else '.gif' + iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) + for size in (16, 32, 48)] + icons = [PhotoImage(file=iconfile) for iconfile in iconfiles] + root.wm_iconphoto(True, *icons) + fixwordbreaks(root) root.withdraw() flist = PyShellFileList(root) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -316,6 +316,9 @@ IDLE ---- +- Issue #20406: Use Python application icons for Idle window title bars. + Patch mostly by Serhiy Storchaka. + - Update the python.gif icon for the Idle classbrowser and pathbowser from the old green snake to the new new blue and yellow snakes. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 15:05:56 2014 From: python-checkins at python.org (terry.reedy) Date: Sat, 8 Feb 2014 15:05:56 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320406=3A_Use_Python_application_icons_for_Idle_?= =?utf-8?q?window_title_bars=2E?= Message-ID: <3fLwC84gbXz7Lnl@mail.python.org> http://hg.python.org/cpython/rev/3aa6fd1dc2c9 changeset: 89049:3aa6fd1dc2c9 parent: 89047:0f9309f8c755 parent: 89048:bda1739215b4 user: Terry Jan Reedy date: Sat Feb 08 09:05:20 2014 -0500 summary: Issue #20406: Use Python application icons for Idle window title bars. Patch mostly by Serhiy Storchaka. files: Lib/idlelib/Icons/idle.ico | Bin Lib/idlelib/Icons/idle_16.gif | Bin Lib/idlelib/Icons/idle_16.png | Bin Lib/idlelib/Icons/idle_32.gif | Bin Lib/idlelib/Icons/idle_32.png | Bin Lib/idlelib/Icons/idle_48.gif | Bin Lib/idlelib/Icons/idle_48.png | Bin Lib/idlelib/PyShell.py | 14 +++++++++++++- Misc/NEWS | 3 +++ 9 files changed, 16 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/Icons/idle.ico b/Lib/idlelib/Icons/idle.ico new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3357aef14888c501bcd7bfe02393760306a18d06 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_16.gif b/Lib/idlelib/Icons/idle_16.gif new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9f001b1d79cf697bb81bde59a5bc2f8341f74ba6 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_16.png b/Lib/idlelib/Icons/idle_16.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6abde0af90cb6bc9dfc71cc4d8fd17abc3c7bf53 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_32.gif b/Lib/idlelib/Icons/idle_32.gif new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..af5b2d52cce8951a47ac1604b9332b9032c0ce2d GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_32.png b/Lib/idlelib/Icons/idle_32.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..41b70dbc37766a9f229f4e0b88ddc2f13d22e3db GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_48.gif b/Lib/idlelib/Icons/idle_48.gif new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fc5304f31eed32d5f7769e471c3817b58edc10e4 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_48.png b/Lib/idlelib/Icons/idle_48.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e5fa9280e21f86b0e5e750b02279b93e36009589 GIT binary patch [stripped] diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -16,7 +16,7 @@ import linecache from code import InteractiveInterpreter -from platform import python_version +from platform import python_version, system try: from tkinter import * @@ -1530,6 +1530,18 @@ # start editor and/or shell windows: root = Tk(className="Idle") + # set application icon + icondir = os.path.join(os.path.dirname(__file__), 'Icons') + if system() == 'Windows': + iconfile = os.path.join(icondir, 'idle.ico') + root.wm_iconbitmap(default=iconfile) + elif TkVersion >= 8.5: + ext = '.png' if TkVersion >= 8.6 else '.gif' + iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) + for size in (16, 32, 48)] + icons = [PhotoImage(file=iconfile) for iconfile in iconfiles] + root.wm_iconphoto(True, *icons) + fixwordbreaks(root) root.withdraw() flist = PyShellFileList(root) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -156,6 +156,9 @@ IDLE ---- +- Issue #20406: Use Python application icons for Idle window title bars. + Patch mostly by Serhiy Storchaka. + - Update the python.gif icon for the Idle classbrowser and pathbowser from the old green snake to the new new blue and yellow snakes. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 15:40:26 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 15:40:26 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTUz?= =?utf-8?q?=2E_Use_specific_asserts_in_ipaddress_tests=2E?= Message-ID: <3fLwyy0sMZz7LjM@mail.python.org> http://hg.python.org/cpython/rev/58be80e7e653 changeset: 89050:58be80e7e653 branch: 3.3 parent: 89048:bda1739215b4 user: Serhiy Storchaka date: Sat Feb 08 16:38:35 2014 +0200 summary: Issue #20553. Use specific asserts in ipaddress tests. files: Lib/test/test_ipaddress.py | 35 ++++++++++++------------- 1 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -921,13 +921,13 @@ 36893488147419103232) def testContains(self): - self.assertTrue(ipaddress.IPv4Interface('1.2.3.128/25') in - self.ipv4_network) - self.assertFalse(ipaddress.IPv4Interface('1.2.4.1/24') in + self.assertIn(ipaddress.IPv4Interface('1.2.3.128/25'), + self.ipv4_network) + self.assertNotIn(ipaddress.IPv4Interface('1.2.4.1/24'), self.ipv4_network) # We can test addresses and string as well. addr1 = ipaddress.IPv4Address('1.2.3.37') - self.assertTrue(addr1 in self.ipv4_network) + self.assertIn(addr1, self.ipv4_network) # issue 61, bad network comparison on like-ip'd network objects # with identical broadcast addresses. self.assertFalse(ipaddress.IPv4Network('1.1.0.0/16').__contains__( @@ -1500,8 +1500,8 @@ dummy[self.ipv6_address] = None dummy[ip1] = None dummy[ip2] = None - self.assertTrue(self.ipv4_address in dummy) - self.assertTrue(ip2 in dummy) + self.assertIn(self.ipv4_address, dummy) + self.assertIn(ip2, dummy) def testIPBases(self): net = self.ipv4_network @@ -1608,9 +1608,9 @@ def testNetworkElementCaching(self): # V4 - make sure we're empty - self.assertFalse('network_address' in self.ipv4_network._cache) - self.assertFalse('broadcast_address' in self.ipv4_network._cache) - self.assertFalse('hostmask' in self.ipv4_network._cache) + self.assertNotIn('network_address', self.ipv4_network._cache) + self.assertNotIn('broadcast_address', self.ipv4_network._cache) + self.assertNotIn('hostmask', self.ipv4_network._cache) # V4 - populate and test self.assertEqual(self.ipv4_network.network_address, @@ -1621,12 +1621,12 @@ ipaddress.IPv4Address('0.0.0.255')) # V4 - check we're cached - self.assertTrue('broadcast_address' in self.ipv4_network._cache) - self.assertTrue('hostmask' in self.ipv4_network._cache) + self.assertIn('broadcast_address', self.ipv4_network._cache) + self.assertIn('hostmask', self.ipv4_network._cache) # V6 - make sure we're empty - self.assertFalse('broadcast_address' in self.ipv6_network._cache) - self.assertFalse('hostmask' in self.ipv6_network._cache) + self.assertNotIn('broadcast_address', self.ipv6_network._cache) + self.assertNotIn('hostmask', self.ipv6_network._cache) # V6 - populate and test self.assertEqual(self.ipv6_network.network_address, @@ -1646,11 +1646,10 @@ ipaddress.IPv6Address('::ffff:ffff:ffff:ffff')) # V6 - check we're cached - self.assertTrue('broadcast_address' in self.ipv6_network._cache) - self.assertTrue('hostmask' in self.ipv6_network._cache) - self.assertTrue( - 'broadcast_address' in self.ipv6_interface.network._cache) - self.assertTrue('hostmask' in self.ipv6_interface.network._cache) + self.assertIn('broadcast_address', self.ipv6_network._cache) + self.assertIn('hostmask', self.ipv6_network._cache) + self.assertIn('broadcast_address', self.ipv6_interface.network._cache) + self.assertIn('hostmask', self.ipv6_interface.network._cache) def testTeredo(self): # stolen from wikipedia -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 15:40:27 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 8 Feb 2014 15:40:27 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320553=2E_Use_specific_asserts_in_ipaddress_test?= =?utf-8?q?s=2E?= Message-ID: <3fLwyz3Bfhz7LmZ@mail.python.org> http://hg.python.org/cpython/rev/2bcb574d579f changeset: 89051:2bcb574d579f parent: 89049:3aa6fd1dc2c9 parent: 89050:58be80e7e653 user: Serhiy Storchaka date: Sat Feb 08 16:39:51 2014 +0200 summary: Issue #20553. Use specific asserts in ipaddress tests. files: Lib/test/test_ipaddress.py | 35 ++++++++++++------------- 1 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -921,13 +921,13 @@ 36893488147419103232) def testContains(self): - self.assertTrue(ipaddress.IPv4Interface('1.2.3.128/25') in - self.ipv4_network) - self.assertFalse(ipaddress.IPv4Interface('1.2.4.1/24') in + self.assertIn(ipaddress.IPv4Interface('1.2.3.128/25'), + self.ipv4_network) + self.assertNotIn(ipaddress.IPv4Interface('1.2.4.1/24'), self.ipv4_network) # We can test addresses and string as well. addr1 = ipaddress.IPv4Address('1.2.3.37') - self.assertTrue(addr1 in self.ipv4_network) + self.assertIn(addr1, self.ipv4_network) # issue 61, bad network comparison on like-ip'd network objects # with identical broadcast addresses. self.assertFalse(ipaddress.IPv4Network('1.1.0.0/16').__contains__( @@ -1512,8 +1512,8 @@ dummy[self.ipv6_address] = None dummy[ip1] = None dummy[ip2] = None - self.assertTrue(self.ipv4_address in dummy) - self.assertTrue(ip2 in dummy) + self.assertIn(self.ipv4_address, dummy) + self.assertIn(ip2, dummy) def testIPBases(self): net = self.ipv4_network @@ -1620,9 +1620,9 @@ def testNetworkElementCaching(self): # V4 - make sure we're empty - self.assertFalse('network_address' in self.ipv4_network._cache) - self.assertFalse('broadcast_address' in self.ipv4_network._cache) - self.assertFalse('hostmask' in self.ipv4_network._cache) + self.assertNotIn('network_address', self.ipv4_network._cache) + self.assertNotIn('broadcast_address', self.ipv4_network._cache) + self.assertNotIn('hostmask', self.ipv4_network._cache) # V4 - populate and test self.assertEqual(self.ipv4_network.network_address, @@ -1633,12 +1633,12 @@ ipaddress.IPv4Address('0.0.0.255')) # V4 - check we're cached - self.assertTrue('broadcast_address' in self.ipv4_network._cache) - self.assertTrue('hostmask' in self.ipv4_network._cache) + self.assertIn('broadcast_address', self.ipv4_network._cache) + self.assertIn('hostmask', self.ipv4_network._cache) # V6 - make sure we're empty - self.assertFalse('broadcast_address' in self.ipv6_network._cache) - self.assertFalse('hostmask' in self.ipv6_network._cache) + self.assertNotIn('broadcast_address', self.ipv6_network._cache) + self.assertNotIn('hostmask', self.ipv6_network._cache) # V6 - populate and test self.assertEqual(self.ipv6_network.network_address, @@ -1658,11 +1658,10 @@ ipaddress.IPv6Address('::ffff:ffff:ffff:ffff')) # V6 - check we're cached - self.assertTrue('broadcast_address' in self.ipv6_network._cache) - self.assertTrue('hostmask' in self.ipv6_network._cache) - self.assertTrue( - 'broadcast_address' in self.ipv6_interface.network._cache) - self.assertTrue('hostmask' in self.ipv6_interface.network._cache) + self.assertIn('broadcast_address', self.ipv6_network._cache) + self.assertIn('hostmask', self.ipv6_network._cache) + self.assertIn('broadcast_address', self.ipv6_interface.network._cache) + self.assertIn('hostmask', self.ipv6_interface.network._cache) def testTeredo(self): # stolen from wikipedia -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 15:42:47 2014 From: python-checkins at python.org (terry.reedy) Date: Sat, 8 Feb 2014 15:42:47 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIwNDA2?= =?utf-8?q?=3A_Use_Python_application_icons_for_Idle_window_title_bars=2E?= Message-ID: <3fLx1g5kPWz7LmZ@mail.python.org> http://hg.python.org/cpython/rev/d4f9efd4be7d changeset: 89052:d4f9efd4be7d branch: 2.7 parent: 89040:1c3fcba31708 user: Terry Jan Reedy date: Sat Feb 08 09:39:51 2014 -0500 summary: Issue #20406: Use Python application icons for Idle window title bars. Patch mostly by Serhiy Storchaka. files: Lib/idlelib/Icons/idle.ico | Bin Lib/idlelib/Icons/idle_16.gif | Bin Lib/idlelib/Icons/idle_16.png | Bin Lib/idlelib/Icons/idle_32.gif | Bin Lib/idlelib/Icons/idle_32.png | Bin Lib/idlelib/Icons/idle_48.gif | Bin Lib/idlelib/Icons/idle_48.png | Bin Lib/idlelib/PyShell.py | 14 +++++++++++++- Misc/NEWS | 3 +++ 9 files changed, 16 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/Icons/idle.ico b/Lib/idlelib/Icons/idle.ico new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3357aef14888c501bcd7bfe02393760306a18d06 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_16.gif b/Lib/idlelib/Icons/idle_16.gif new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9f001b1d79cf697bb81bde59a5bc2f8341f74ba6 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_16.png b/Lib/idlelib/Icons/idle_16.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6abde0af90cb6bc9dfc71cc4d8fd17abc3c7bf53 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_32.gif b/Lib/idlelib/Icons/idle_32.gif new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..af5b2d52cce8951a47ac1604b9332b9032c0ce2d GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_32.png b/Lib/idlelib/Icons/idle_32.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..41b70dbc37766a9f229f4e0b88ddc2f13d22e3db GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_48.gif b/Lib/idlelib/Icons/idle_48.gif new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fc5304f31eed32d5f7769e471c3817b58edc10e4 GIT binary patch [stripped] diff --git a/Lib/idlelib/Icons/idle_48.png b/Lib/idlelib/Icons/idle_48.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e5fa9280e21f86b0e5e750b02279b93e36009589 GIT binary patch [stripped] diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -15,7 +15,7 @@ import linecache from code import InteractiveInterpreter -from platform import python_version +from platform import python_version, system try: from Tkinter import * @@ -1536,6 +1536,18 @@ # start editor and/or shell windows: root = Tk(className="Idle") + # set application icon + icondir = os.path.join(os.path.dirname(__file__), 'Icons') + if system() == 'Windows': + iconfile = os.path.join(icondir, 'idle.ico') + root.wm_iconbitmap(default=iconfile) + elif TkVersion >= 8.5: + ext = '.png' if TkVersion >= 8.6 else '.gif' + iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) + for size in (16, 32, 48)] + icons = [PhotoImage(file=iconfile) for iconfile in iconfiles] + root.tk.call('wm', 'iconphoto', str(root), "-default", *icons) + fixwordbreaks(root) root.withdraw() flist = PyShellFileList(root) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -211,6 +211,9 @@ IDLE ---- +- Issue #20406: Use Python application icons for Idle window title bars. + Patch mostly by Serhiy Storchaka. + - Issue #17721: Remove non-functional configuration dialog help button until we make it actually gives some help when clicked. Patch by Guilherme Sim?es. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 17:51:48 2014 From: python-checkins at python.org (r.david.murray) Date: Sat, 8 Feb 2014 17:51:48 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE5NzcyOiBEbyBu?= =?utf-8?q?ot_mutate_message_when_downcoding_to_7bit=2E?= Message-ID: <3fLztX4gZ3z7Ljm@mail.python.org> http://hg.python.org/cpython/rev/34fb36972f8d changeset: 89053:34fb36972f8d branch: 3.3 parent: 89050:58be80e7e653 user: R David Murray date: Sat Feb 08 11:48:20 2014 -0500 summary: #19772: Do not mutate message when downcoding to 7bit. This is a bit of an ugly hack because of the way generator pieces together the output message. The deepcopys aren't too expensive, though, because we know it is only called on messages that are not multiparts, and the payload (the thing that could be large) is an immutable object. Test and preliminary work on patch by Vajrasky Kok. files: Lib/email/generator.py | 14 ++++++++++++++ Lib/test/test_email/test_email.py | 12 +++++++++++- Misc/NEWS | 3 +++ 3 files changed, 28 insertions(+), 1 deletions(-) diff --git a/Lib/email/generator.py b/Lib/email/generator.py --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -12,6 +12,7 @@ import random import warnings +from copy import deepcopy from io import StringIO, BytesIO from email._policybase import compat32 from email.header import Header @@ -173,10 +174,18 @@ # necessary. oldfp = self._fp try: + self._munge_cte = None self._fp = sfp = self._new_buffer() self._dispatch(msg) finally: self._fp = oldfp + munge_cte = self._munge_cte + del self._munge_cte + # If we munged the cte, copy the message again and re-fix the CTE. + if munge_cte: + msg = deepcopy(msg) + msg.replace_header('content-transfer-encoding', munge_cte[0]) + msg.replace_header('content-type', munge_cte[1]) # Write the headers. First we see if the message object wants to # handle that itself. If not, we'll do it generically. meth = getattr(msg, '_write_headers', None) @@ -225,9 +234,14 @@ if _has_surrogates(msg._payload): charset = msg.get_param('charset') if charset is not None: + # XXX: This copy stuff is an ugly hack to avoid modifying the + # existing message. + msg = deepcopy(msg) del msg['content-transfer-encoding'] msg.set_payload(payload, charset) payload = msg.get_payload() + self._munge_cte = (msg['content-transfer-encoding'], + msg['content-type']) if self._mangle_from_: payload = fcre.sub('>From ', payload) self._write_lines(payload) diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -3495,7 +3495,7 @@ self.assertTrue(msg.get_payload(0).get_payload().endswith('\r\n')) -class Test8BitBytesHandling(unittest.TestCase): +class Test8BitBytesHandling(TestEmailBase): # In Python3 all input is string, but that doesn't work if the actual input # uses an 8bit transfer encoding. To hack around that, in email 5.1 we # decode byte streams using the surrogateescape error handler, and @@ -3748,6 +3748,16 @@ email.generator.Generator(out).flatten(msg) self.assertEqual(out.getvalue(), self.non_latin_bin_msg_as7bit_wrapped) + def test_str_generator_should_not_mutate_msg_when_handling_8bit(self): + msg = email.message_from_bytes(self.non_latin_bin_msg) + out = BytesIO() + BytesGenerator(out).flatten(msg) + orig_value = out.getvalue() + Generator(StringIO()).flatten(msg) # Should not mutate msg! + out = BytesIO() + BytesGenerator(out).flatten(msg) + self.assertEqual(out.getvalue(), orig_value) + def test_bytes_generator_with_unix_from(self): # The unixfrom contains a current date, so we can't check it # literally. Just make sure the first word is 'From' and the diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -48,6 +48,9 @@ Library ------- +- Issue #19772: email.generator no longer mutates the message object when + doing a down-transform from 8bit to 7bit CTEs. + - Issue #18805: the netmask/hostmask parsing in ipaddress now more reliably filters out illegal values and correctly allows any valid prefix length. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 17:51:50 2014 From: python-checkins at python.org (r.david.murray) Date: Sat, 8 Feb 2014 17:51:50 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge_=2319772=3A_Do_not_mutate_message_when_downcoding_?= =?utf-8?q?to_7bit=2E?= Message-ID: <3fLztZ05Frz7LkV@mail.python.org> http://hg.python.org/cpython/rev/2e97d3500970 changeset: 89054:2e97d3500970 parent: 89051:2bcb574d579f parent: 89053:34fb36972f8d user: R David Murray date: Sat Feb 08 11:51:18 2014 -0500 summary: Merge #19772: Do not mutate message when downcoding to 7bit. files: Lib/email/generator.py | 14 ++++++++++ Lib/test/test_email/test_contentmanager.py | 2 +- Lib/test/test_email/test_email.py | 12 +++++++- Misc/NEWS | 3 ++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Lib/email/generator.py b/Lib/email/generator.py --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -12,6 +12,7 @@ import random import warnings +from copy import deepcopy from io import StringIO, BytesIO from email._policybase import compat32 from email.header import Header @@ -173,10 +174,18 @@ # necessary. oldfp = self._fp try: + self._munge_cte = None self._fp = sfp = self._new_buffer() self._dispatch(msg) finally: self._fp = oldfp + munge_cte = self._munge_cte + del self._munge_cte + # If we munged the cte, copy the message again and re-fix the CTE. + if munge_cte: + msg = deepcopy(msg) + msg.replace_header('content-transfer-encoding', munge_cte[0]) + msg.replace_header('content-type', munge_cte[1]) # Write the headers. First we see if the message object wants to # handle that itself. If not, we'll do it generically. meth = getattr(msg, '_write_headers', None) @@ -225,9 +234,14 @@ if _has_surrogates(msg._payload): charset = msg.get_param('charset') if charset is not None: + # XXX: This copy stuff is an ugly hack to avoid modifying the + # existing message. + msg = deepcopy(msg) del msg['content-transfer-encoding'] msg.set_payload(payload, charset) payload = msg.get_payload() + self._munge_cte = (msg['content-transfer-encoding'], + msg['content-type']) if self._mangle_from_: payload = fcre.sub('>From ', payload) self._write_lines(payload) diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -536,8 +536,8 @@ From: victim at monty.org Subject: Help Content-Type: text/plain; charset="utf-8" + Content-Transfer-Encoding: base64 MIME-Version: 1.0 - Content-Transfer-Encoding: base64 aidhaSB1biBwcm9ibMOobWUgZGUgcHl0aG9uLiBpbCBlc3Qgc29ydGkgZGUgc29uIHZpdmFyaXVt Lgo= diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -3529,7 +3529,7 @@ self.assertTrue(msg.get_payload(0).get_payload().endswith('\r\n')) -class Test8BitBytesHandling(unittest.TestCase): +class Test8BitBytesHandling(TestEmailBase): # In Python3 all input is string, but that doesn't work if the actual input # uses an 8bit transfer encoding. To hack around that, in email 5.1 we # decode byte streams using the surrogateescape error handler, and @@ -3782,6 +3782,16 @@ email.generator.Generator(out).flatten(msg) self.assertEqual(out.getvalue(), self.non_latin_bin_msg_as7bit_wrapped) + def test_str_generator_should_not_mutate_msg_when_handling_8bit(self): + msg = email.message_from_bytes(self.non_latin_bin_msg) + out = BytesIO() + BytesGenerator(out).flatten(msg) + orig_value = out.getvalue() + Generator(StringIO()).flatten(msg) # Should not mutate msg! + out = BytesIO() + BytesGenerator(out).flatten(msg) + self.assertEqual(out.getvalue(), orig_value) + def test_bytes_generator_with_unix_from(self): # The unixfrom contains a current date, so we can't check it # literally. Just make sure the first word is 'From' and the diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,9 @@ Library ------- +- Issue #19772: email.generator no longer mutates the message object when + doing a down-transform from 8bit to 7bit CTEs. + - Issue #20536: the statistics module now correctly handle Decimal instances with positive exponents -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 19:13:51 2014 From: python-checkins at python.org (r.david.murray) Date: Sat, 8 Feb 2014 19:13:51 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE2OTgzOiBBcHBs?= =?utf-8?q?y_postel=27s_law_to_encoded_words_inside_quoted_strings=2E?= Message-ID: <3fM1jC3GGrz7LkF@mail.python.org> http://hg.python.org/cpython/rev/1dcb9d0d53a6 changeset: 89055:1dcb9d0d53a6 branch: 3.3 parent: 89053:34fb36972f8d user: R David Murray date: Sat Feb 08 13:12:00 2014 -0500 summary: #16983: Apply postel's law to encoded words inside quoted strings. This applies only to the new parser. The old parser decodes encoded words inside quoted strings already, although it gets the whitespace wrong when it does so. This version of the patch only handles the most common case (a single encoded word surrounded by quotes), but I haven't seen any other variations of this in the wild yet, so its good enough for now. files: Lib/email/_header_value_parser.py | 7 +++++++ Lib/test/test_email/test__header_value_parser.py | 9 +++++++++ Lib/test/test_email/test_headerregistry.py | 10 ++++++++++ Misc/NEWS | 3 +++ 4 files changed, 29 insertions(+), 0 deletions(-) diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -1559,6 +1559,13 @@ while value and value[0] != '"': if value[0] in WSP: token, value = get_fws(value) + elif value[:2] == '=?': + try: + token, value = get_encoded_word(value) + bare_quoted_string.defects.append(errors.InvalidHeaderDefect( + "encoded word inside quoted string")) + except errors.HeaderParseError: + token, value = get_qcontent(value) else: token, value = get_qcontent(value) bare_quoted_string.append(token) diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -540,6 +540,15 @@ self._test_get_x(parser.get_bare_quoted_string, '""', '""', '', [], '') + # Issue 16983: apply postel's law to some bad encoding. + def test_encoded_word_inside_quotes(self): + self._test_get_x(parser.get_bare_quoted_string, + '"=?utf-8?Q?not_really_valid?="', + '"not really valid"', + 'not really valid', + [errors.InvalidHeaderDefect], + '') + # get_comment def test_get_comment_only(self): diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -1143,6 +1143,16 @@ 'example.com', None), + 'rfc2047_atom_in_quoted_string_is_decoded': + ('"=?utf-8?q?=C3=89ric?=" ', + [errors.InvalidHeaderDefect], + '?ric ', + '?ric', + 'foo at example.com', + 'foo', + 'example.com', + None), + } # XXX: Need many more examples, and in particular some with names in diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -48,6 +48,9 @@ Library ------- +- Issue #16983: the new email header parsing code will now decode encoded words + that are (incorrectly) surrounded by quotes, and register a defect. + - Issue #19772: email.generator no longer mutates the message object when doing a down-transform from 8bit to 7bit CTEs. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 19:13:52 2014 From: python-checkins at python.org (r.david.murray) Date: Sat, 8 Feb 2014 19:13:52 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge=3A_=2316983=3A_Apply_postel=27s_law_to_encoded_wor?= =?utf-8?q?ds_inside_quoted_strings=2E?= Message-ID: <3fM1jD4rYWz7LkV@mail.python.org> http://hg.python.org/cpython/rev/5f7e626730df changeset: 89056:5f7e626730df parent: 89054:2e97d3500970 parent: 89055:1dcb9d0d53a6 user: R David Murray date: Sat Feb 08 13:13:01 2014 -0500 summary: Merge: #16983: Apply postel's law to encoded words inside quoted strings. files: Lib/email/_header_value_parser.py | 7 +++++++ Lib/test/test_email/test__header_value_parser.py | 9 +++++++++ Lib/test/test_email/test_headerregistry.py | 10 ++++++++++ Misc/NEWS | 3 +++ 4 files changed, 29 insertions(+), 0 deletions(-) diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -1556,6 +1556,13 @@ while value and value[0] != '"': if value[0] in WSP: token, value = get_fws(value) + elif value[:2] == '=?': + try: + token, value = get_encoded_word(value) + bare_quoted_string.defects.append(errors.InvalidHeaderDefect( + "encoded word inside quoted string")) + except errors.HeaderParseError: + token, value = get_qcontent(value) else: token, value = get_qcontent(value) bare_quoted_string.append(token) diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -540,6 +540,15 @@ self._test_get_x(parser.get_bare_quoted_string, '""', '""', '', [], '') + # Issue 16983: apply postel's law to some bad encoding. + def test_encoded_word_inside_quotes(self): + self._test_get_x(parser.get_bare_quoted_string, + '"=?utf-8?Q?not_really_valid?="', + '"not really valid"', + 'not really valid', + [errors.InvalidHeaderDefect], + '') + # get_comment def test_get_comment_only(self): diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -1155,6 +1155,16 @@ 'example.com', None), + 'rfc2047_atom_in_quoted_string_is_decoded': + ('"=?utf-8?q?=C3=89ric?=" ', + [errors.InvalidHeaderDefect], + '?ric ', + '?ric', + 'foo at example.com', + 'foo', + 'example.com', + None), + } # XXX: Need many more examples, and in particular some with names in diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,9 @@ Library ------- +- Issue #16983: the new email header parsing code will now decode encoded words + that are (incorrectly) surrounded by quotes, and register a defect. + - Issue #19772: email.generator no longer mutates the message object when doing a down-transform from 8bit to 7bit CTEs. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 20:36:25 2014 From: python-checkins at python.org (ethan.furman) Date: Sat, 8 Feb 2014 20:36:25 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_issue20534=3A_all_pi?= =?utf-8?q?ckle_protocols_now_supported=2E?= Message-ID: <3fM3XT6LMxz7LkD@mail.python.org> http://hg.python.org/cpython/rev/9f75f8a2cbb4 changeset: 89057:9f75f8a2cbb4 user: Ethan Furman date: Sat Feb 08 11:36:27 2014 -0800 summary: Close issue20534: all pickle protocols now supported. files: Doc/library/enum.rst | 14 +- Lib/enum.py | 28 ++- Lib/test/test_enum.py | 212 +++++++++++++++++++++++++++++- 3 files changed, 233 insertions(+), 21 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -369,10 +369,10 @@ the top level of a module, since unpickling requires them to be importable from that module. -.. warning:: +.. note:: - In order to support the singleton nature of enumeration members, pickle - protocol version 2 or higher must be used. + With pickle protocol version 4 it is possible to easily pickle enums + nested in other classes. Functional API @@ -420,6 +420,14 @@ >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__) +The new pickle protocol 4 also, in some circumstances, relies on +:attr:``__qualname__`` being set to the location where pickle will be able +to find the class. For example, if the class was made available in class +SomeData in the global scope:: + + >>> Animals = Enum('Animals', 'ant bee cat dog', qualname='SomeData.Animals') + + Derived Enumerations -------------------- diff --git a/Lib/enum.py b/Lib/enum.py --- a/Lib/enum.py +++ b/Lib/enum.py @@ -31,9 +31,9 @@ def _make_class_unpicklable(cls): """Make the given class un-picklable.""" - def _break_on_call_reduce(self): + def _break_on_call_reduce(self, proto): raise TypeError('%r cannot be pickled' % self) - cls.__reduce__ = _break_on_call_reduce + cls.__reduce_ex__ = _break_on_call_reduce cls.__module__ = '' @@ -115,12 +115,13 @@ # Reverse value->name map for hashable values. enum_class._value2member_map_ = {} - # check for a __getnewargs__, and if not present sabotage + # check for a supported pickle protocols, and if not present sabotage # pickling, since it won't work anyway - if (member_type is not object and - member_type.__dict__.get('__getnewargs__') is None - ): - _make_class_unpicklable(enum_class) + if member_type is not object: + methods = ('__getnewargs_ex__', '__getnewargs__', + '__reduce_ex__', '__reduce__') + if not any(map(member_type.__dict__.get, methods)): + _make_class_unpicklable(enum_class) # instantiate them, checking for duplicates as we go # we instantiate first instead of checking for duplicates first in case @@ -166,7 +167,7 @@ # double check that repr and friends are not the mixin's or various # things break (such as pickle) - for name in ('__repr__', '__str__', '__format__', '__getnewargs__'): + for name in ('__repr__', '__str__', '__format__', '__getnewargs__', '__reduce_ex__'): class_method = getattr(enum_class, name) obj_method = getattr(member_type, name, None) enum_method = getattr(first_enum, name, None) @@ -183,7 +184,7 @@ enum_class.__new__ = Enum.__new__ return enum_class - def __call__(cls, value, names=None, *, module=None, type=None): + def __call__(cls, value, names=None, *, module=None, qualname=None, type=None): """Either returns an existing member, or creates a new enum class. This method is used both when an enum class is given a value to match @@ -202,7 +203,7 @@ if names is None: # simple value lookup return cls.__new__(cls, value) # otherwise, functional API: we're creating a new Enum type - return cls._create_(value, names, module=module, type=type) + return cls._create_(value, names, module=module, qualname=qualname, type=type) def __contains__(cls, member): return isinstance(member, cls) and member.name in cls._member_map_ @@ -273,7 +274,7 @@ raise AttributeError('Cannot reassign members.') super().__setattr__(name, value) - def _create_(cls, class_name, names=None, *, module=None, type=None): + def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None): """Convenience method to create a new Enum class. `names` can be: @@ -315,6 +316,8 @@ _make_class_unpicklable(enum_class) else: enum_class.__module__ = module + if qualname is not None: + enum_class.__qualname__ = qualname return enum_class @@ -468,6 +471,9 @@ def __hash__(self): return hash(self._name_) + def __reduce_ex__(self, proto): + return self.__class__, self.__getnewargs__() + # DynamicClassAttribute is used to provide access to the `name` and # `value` properties of enum members while keeping some measure of # protection from modification, while still allowing for an enumeration diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -52,6 +52,11 @@ except Exception as exc: Answer = exc +try: + Theory = Enum('Theory', 'rule law supposition', qualname='spanish_inquisition') +except Exception as exc: + Theory = exc + # for doctests try: class Fruit(Enum): @@ -61,14 +66,18 @@ except Exception: pass -def test_pickle_dump_load(assertion, source, target=None): +def test_pickle_dump_load(assertion, source, target=None, + *, protocol=(0, HIGHEST_PROTOCOL)): + start, stop = protocol if target is None: target = source - for protocol in range(2, HIGHEST_PROTOCOL+1): + for protocol in range(start, stop+1): assertion(loads(dumps(source, protocol=protocol)), target) -def test_pickle_exception(assertion, exception, obj): - for protocol in range(2, HIGHEST_PROTOCOL+1): +def test_pickle_exception(assertion, exception, obj, + *, protocol=(0, HIGHEST_PROTOCOL)): + start, stop = protocol + for protocol in range(start, stop+1): with assertion(exception): dumps(obj, protocol=protocol) @@ -101,6 +110,7 @@ class TestEnum(unittest.TestCase): + def setUp(self): class Season(Enum): SPRING = 1 @@ -540,11 +550,31 @@ test_pickle_dump_load(self.assertIs, Question.who) test_pickle_dump_load(self.assertIs, Question) + def test_enum_function_with_qualname(self): + if isinstance(Theory, Exception): + raise Theory + self.assertEqual(Theory.__qualname__, 'spanish_inquisition') + + def test_class_nested_enum_and_pickle_protocol_four(self): + # would normally just have this directly in the class namespace + class NestedEnum(Enum): + twigs = 'common' + shiny = 'rare' + + self.__class__.NestedEnum = NestedEnum + self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__ + test_pickle_exception( + self.assertRaises, PicklingError, self.NestedEnum.twigs, + protocol=(0, 3)) + test_pickle_dump_load(self.assertIs, self.NestedEnum.twigs, + protocol=(4, HIGHEST_PROTOCOL)) + def test_exploding_pickle(self): - BadPickle = Enum('BadPickle', 'dill sweet bread-n-butter') - BadPickle.__qualname__ = 'BadPickle' # needed for pickle protocol 4 + BadPickle = Enum( + 'BadPickle', 'dill sweet bread-n-butter', module=__name__) globals()['BadPickle'] = BadPickle - enum._make_class_unpicklable(BadPickle) # will overwrite __qualname__ + # now break BadPickle to test exception raising + enum._make_class_unpicklable(BadPickle) test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill) test_pickle_exception(self.assertRaises, PicklingError, BadPickle) @@ -927,6 +957,174 @@ self.assertEqual(NEI.y.value, 2) test_pickle_dump_load(self.assertIs, NEI.y) + def test_subclasses_with_getnewargs_ex(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __getnewargs_ex__(self): + return self._args, {} + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertEqual, NI5, 5, protocol=(4, 4)) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertIs, NEI.y, protocol=(4, 4)) + + def test_subclasses_with_reduce(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __reduce__(self): + return self.__class__, self._args + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertEqual, NI5, 5) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertIs, NEI.y) + + def test_subclasses_with_reduce_ex(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __reduce_ex__(self, proto): + return self.__class__, self._args + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertEqual, NI5, 5) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertIs, NEI.y) + def test_subclasses_without_getnewargs(self): class NamedInt(int): __qualname__ = 'NamedInt' -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 22:55:35 2014 From: python-checkins at python.org (charles-francois.natali) Date: Sat, 8 Feb 2014 22:55:35 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwMDY1?= =?utf-8?q?=3A_socketmodule=3A_Fix_build_error_when_AF=5FCAN_is_defined_wi?= =?utf-8?q?thout_the?= Message-ID: <3fM6d357VWz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/b1ff233d3ab1 changeset: 89058:b1ff233d3ab1 branch: 3.3 parent: 89055:1dcb9d0d53a6 user: Charles-Fran?ois Natali date: Sat Feb 08 22:54:11 2014 +0100 summary: Issue #20065: socketmodule: Fix build error when AF_CAN is defined without the proper CAN headers. files: Modules/socketmodule.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1226,7 +1226,7 @@ } #endif -#ifdef AF_CAN +#ifdef HAVE_LINUX_CAN_H case AF_CAN: { struct sockaddr_can *a = (struct sockaddr_can *)addr; @@ -1654,7 +1654,7 @@ } #endif -#ifdef AF_CAN +#ifdef HAVE_LINUX_CAN_H case AF_CAN: switch (s->sock_proto) { case CAN_RAW: @@ -1859,7 +1859,7 @@ } #endif -#ifdef AF_CAN +#ifdef HAVE_LINUX_CAN_H case AF_CAN: { *len_ret = sizeof (struct sockaddr_can); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 22:55:36 2014 From: python-checkins at python.org (charles-francois.natali) Date: Sat, 8 Feb 2014 22:55:36 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320065=3A_socketmodule=3A_Fix_build_error_when_A?= =?utf-8?q?F=5FCAN_is_defined_without_the?= Message-ID: <3fM6d46WRnz7LjN@mail.python.org> http://hg.python.org/cpython/rev/39a60d62d2a6 changeset: 89059:39a60d62d2a6 parent: 89056:5f7e626730df parent: 89058:b1ff233d3ab1 user: Charles-Fran?ois Natali date: Sat Feb 08 22:54:48 2014 +0100 summary: Issue #20065: socketmodule: Fix build error when AF_CAN is defined without the proper CAN headers. files: Modules/socketmodule.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1165,7 +1165,7 @@ } #endif -#ifdef AF_CAN +#ifdef HAVE_LINUX_CAN_H case AF_CAN: { struct sockaddr_can *a = (struct sockaddr_can *)addr; @@ -1589,7 +1589,7 @@ } #endif -#ifdef AF_CAN +#ifdef HAVE_LINUX_CAN_H case AF_CAN: switch (s->sock_proto) { case CAN_RAW: @@ -1796,7 +1796,7 @@ } #endif -#ifdef AF_CAN +#ifdef HAVE_LINUX_CAN_H case AF_CAN: { *len_ret = sizeof (struct sockaddr_can); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 22:55:38 2014 From: python-checkins at python.org (charles-francois.natali) Date: Sat, 8 Feb 2014 22:55:38 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?b?KTogTWVyZ2Uu?= Message-ID: <3fM6d62kXwz7Lk9@mail.python.org> http://hg.python.org/cpython/rev/68c40567e8f8 changeset: 89060:68c40567e8f8 parent: 89059:39a60d62d2a6 parent: 89057:9f75f8a2cbb4 user: Charles-Fran?ois Natali date: Sat Feb 08 22:55:13 2014 +0100 summary: Merge. files: Doc/library/enum.rst | 14 +- Lib/enum.py | 28 ++- Lib/test/test_enum.py | 212 +++++++++++++++++++++++++++++- 3 files changed, 233 insertions(+), 21 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -369,10 +369,10 @@ the top level of a module, since unpickling requires them to be importable from that module. -.. warning:: +.. note:: - In order to support the singleton nature of enumeration members, pickle - protocol version 2 or higher must be used. + With pickle protocol version 4 it is possible to easily pickle enums + nested in other classes. Functional API @@ -420,6 +420,14 @@ >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__) +The new pickle protocol 4 also, in some circumstances, relies on +:attr:``__qualname__`` being set to the location where pickle will be able +to find the class. For example, if the class was made available in class +SomeData in the global scope:: + + >>> Animals = Enum('Animals', 'ant bee cat dog', qualname='SomeData.Animals') + + Derived Enumerations -------------------- diff --git a/Lib/enum.py b/Lib/enum.py --- a/Lib/enum.py +++ b/Lib/enum.py @@ -31,9 +31,9 @@ def _make_class_unpicklable(cls): """Make the given class un-picklable.""" - def _break_on_call_reduce(self): + def _break_on_call_reduce(self, proto): raise TypeError('%r cannot be pickled' % self) - cls.__reduce__ = _break_on_call_reduce + cls.__reduce_ex__ = _break_on_call_reduce cls.__module__ = '' @@ -115,12 +115,13 @@ # Reverse value->name map for hashable values. enum_class._value2member_map_ = {} - # check for a __getnewargs__, and if not present sabotage + # check for a supported pickle protocols, and if not present sabotage # pickling, since it won't work anyway - if (member_type is not object and - member_type.__dict__.get('__getnewargs__') is None - ): - _make_class_unpicklable(enum_class) + if member_type is not object: + methods = ('__getnewargs_ex__', '__getnewargs__', + '__reduce_ex__', '__reduce__') + if not any(map(member_type.__dict__.get, methods)): + _make_class_unpicklable(enum_class) # instantiate them, checking for duplicates as we go # we instantiate first instead of checking for duplicates first in case @@ -166,7 +167,7 @@ # double check that repr and friends are not the mixin's or various # things break (such as pickle) - for name in ('__repr__', '__str__', '__format__', '__getnewargs__'): + for name in ('__repr__', '__str__', '__format__', '__getnewargs__', '__reduce_ex__'): class_method = getattr(enum_class, name) obj_method = getattr(member_type, name, None) enum_method = getattr(first_enum, name, None) @@ -183,7 +184,7 @@ enum_class.__new__ = Enum.__new__ return enum_class - def __call__(cls, value, names=None, *, module=None, type=None): + def __call__(cls, value, names=None, *, module=None, qualname=None, type=None): """Either returns an existing member, or creates a new enum class. This method is used both when an enum class is given a value to match @@ -202,7 +203,7 @@ if names is None: # simple value lookup return cls.__new__(cls, value) # otherwise, functional API: we're creating a new Enum type - return cls._create_(value, names, module=module, type=type) + return cls._create_(value, names, module=module, qualname=qualname, type=type) def __contains__(cls, member): return isinstance(member, cls) and member.name in cls._member_map_ @@ -273,7 +274,7 @@ raise AttributeError('Cannot reassign members.') super().__setattr__(name, value) - def _create_(cls, class_name, names=None, *, module=None, type=None): + def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None): """Convenience method to create a new Enum class. `names` can be: @@ -315,6 +316,8 @@ _make_class_unpicklable(enum_class) else: enum_class.__module__ = module + if qualname is not None: + enum_class.__qualname__ = qualname return enum_class @@ -468,6 +471,9 @@ def __hash__(self): return hash(self._name_) + def __reduce_ex__(self, proto): + return self.__class__, self.__getnewargs__() + # DynamicClassAttribute is used to provide access to the `name` and # `value` properties of enum members while keeping some measure of # protection from modification, while still allowing for an enumeration diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -52,6 +52,11 @@ except Exception as exc: Answer = exc +try: + Theory = Enum('Theory', 'rule law supposition', qualname='spanish_inquisition') +except Exception as exc: + Theory = exc + # for doctests try: class Fruit(Enum): @@ -61,14 +66,18 @@ except Exception: pass -def test_pickle_dump_load(assertion, source, target=None): +def test_pickle_dump_load(assertion, source, target=None, + *, protocol=(0, HIGHEST_PROTOCOL)): + start, stop = protocol if target is None: target = source - for protocol in range(2, HIGHEST_PROTOCOL+1): + for protocol in range(start, stop+1): assertion(loads(dumps(source, protocol=protocol)), target) -def test_pickle_exception(assertion, exception, obj): - for protocol in range(2, HIGHEST_PROTOCOL+1): +def test_pickle_exception(assertion, exception, obj, + *, protocol=(0, HIGHEST_PROTOCOL)): + start, stop = protocol + for protocol in range(start, stop+1): with assertion(exception): dumps(obj, protocol=protocol) @@ -101,6 +110,7 @@ class TestEnum(unittest.TestCase): + def setUp(self): class Season(Enum): SPRING = 1 @@ -540,11 +550,31 @@ test_pickle_dump_load(self.assertIs, Question.who) test_pickle_dump_load(self.assertIs, Question) + def test_enum_function_with_qualname(self): + if isinstance(Theory, Exception): + raise Theory + self.assertEqual(Theory.__qualname__, 'spanish_inquisition') + + def test_class_nested_enum_and_pickle_protocol_four(self): + # would normally just have this directly in the class namespace + class NestedEnum(Enum): + twigs = 'common' + shiny = 'rare' + + self.__class__.NestedEnum = NestedEnum + self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__ + test_pickle_exception( + self.assertRaises, PicklingError, self.NestedEnum.twigs, + protocol=(0, 3)) + test_pickle_dump_load(self.assertIs, self.NestedEnum.twigs, + protocol=(4, HIGHEST_PROTOCOL)) + def test_exploding_pickle(self): - BadPickle = Enum('BadPickle', 'dill sweet bread-n-butter') - BadPickle.__qualname__ = 'BadPickle' # needed for pickle protocol 4 + BadPickle = Enum( + 'BadPickle', 'dill sweet bread-n-butter', module=__name__) globals()['BadPickle'] = BadPickle - enum._make_class_unpicklable(BadPickle) # will overwrite __qualname__ + # now break BadPickle to test exception raising + enum._make_class_unpicklable(BadPickle) test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill) test_pickle_exception(self.assertRaises, PicklingError, BadPickle) @@ -927,6 +957,174 @@ self.assertEqual(NEI.y.value, 2) test_pickle_dump_load(self.assertIs, NEI.y) + def test_subclasses_with_getnewargs_ex(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __getnewargs_ex__(self): + return self._args, {} + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertEqual, NI5, 5, protocol=(4, 4)) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertIs, NEI.y, protocol=(4, 4)) + + def test_subclasses_with_reduce(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __reduce__(self): + return self.__class__, self._args + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertEqual, NI5, 5) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertIs, NEI.y) + + def test_subclasses_with_reduce_ex(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + name, *args = args + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __reduce_ex__(self, proto): + return self.__class__, self._args + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertEqual, NI5, 5) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertIs, NEI.y) + def test_subclasses_without_getnewargs(self): class NamedInt(int): __qualname__ = 'NamedInt' -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 23:09:18 2014 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 8 Feb 2014 23:09:18 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogSXNzdWUgIzIwNTQw?= =?utf-8?q?=3A_Fix_a_performance_regression_=28vs=2E_Python_3=2E2=29_when_?= =?utf-8?q?layering_a?= Message-ID: <3fM6wt0DwHz7Ljg@mail.python.org> http://hg.python.org/cpython/rev/4816ab0477d2 changeset: 89061:4816ab0477d2 branch: 3.3 parent: 89058:b1ff233d3ab1 user: Antoine Pitrou date: Sat Feb 08 23:03:56 2014 +0100 summary: Issue #20540: Fix a performance regression (vs. Python 3.2) when layering a multiprocessing Connection over a TCP socket. For small payloads, Nagle's algorithm would introduce idle delays before the entire transmission of a message. files: Lib/multiprocessing/connection.py | 22 +++++++++++++----- Misc/NEWS | 5 ++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -395,13 +395,23 @@ return buf def _send_bytes(self, buf): + n = len(buf) # For wire compatibility with 3.2 and lower - n = len(buf) - self._send(struct.pack("!i", n)) - # The condition is necessary to avoid "broken pipe" errors - # when sending a 0-length buffer if the other end closed the pipe. - if n > 0: - self._send(buf) + header = struct.pack("!i", n) + if n > 16384: + # The payload is large so Nagle's algorithm won't be triggered + # and we'd better avoid the cost of concatenation. + chunks = [header, buf] + elif n > 0: + # Issue #?20540: concatenate before sending, to avoid delays due + # to Nagle's algorithm on a TCP socket. + chunks = [header + buf] + else: + # This code path is necessary to avoid "broken pipe" errors + # when sending a 0-length buffer if the other end closed the pipe. + chunks = [header] + for chunk in chunks: + self._send(chunk) def _recv_bytes(self, maxsize=None): buf = self._recv(4) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -48,6 +48,11 @@ Library ------- +- Issue #20540: Fix a performance regression (vs. Python 3.2) when layering + a multiprocessing Connection over a TCP socket. For small payloads, Nagle's + algorithm would introduce idle delays before the entire transmission of a + message. + - Issue #16983: the new email header parsing code will now decode encoded words that are (incorrectly) surrounded by quotes, and register a defect. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 23:09:19 2014 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 8 Feb 2014 23:09:19 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320540=3A_Fix_a_performance_regression_=28vs=2E_?= =?utf-8?q?Python_3=2E2=29_when_layering_a?= Message-ID: <3fM6wv1l40z7Ljg@mail.python.org> http://hg.python.org/cpython/rev/125c24f47f3c changeset: 89062:125c24f47f3c parent: 89060:68c40567e8f8 parent: 89061:4816ab0477d2 user: Antoine Pitrou date: Sat Feb 08 23:05:52 2014 +0100 summary: Issue #20540: Fix a performance regression (vs. Python 3.2) when layering a multiprocessing Connection over a TCP socket. For small payloads, Nagle's algorithm would introduce idle delays before the entire transmission of a message. files: Lib/multiprocessing/connection.py | 22 +++++++++++++----- Misc/NEWS | 5 ++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -395,13 +395,23 @@ return buf def _send_bytes(self, buf): + n = len(buf) # For wire compatibility with 3.2 and lower - n = len(buf) - self._send(struct.pack("!i", n)) - # The condition is necessary to avoid "broken pipe" errors - # when sending a 0-length buffer if the other end closed the pipe. - if n > 0: - self._send(buf) + header = struct.pack("!i", n) + if n > 16384: + # The payload is large so Nagle's algorithm won't be triggered + # and we'd better avoid the cost of concatenation. + chunks = [header, buf] + elif n > 0: + # Issue #?20540: concatenate before sending, to avoid delays due + # to Nagle's algorithm on a TCP socket. + chunks = [header + buf] + else: + # This code path is necessary to avoid "broken pipe" errors + # when sending a 0-length buffer if the other end closed the pipe. + chunks = [header] + for chunk in chunks: + self._send(chunk) def _recv_bytes(self, maxsize=None): buf = self._recv(4) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,11 @@ Library ------- +- Issue #20540: Fix a performance regression (vs. Python 3.2) when layering + a multiprocessing Connection over a TCP socket. For small payloads, Nagle's + algorithm would introduce idle delays before the entire transmission of a + message. + - Issue #16983: the new email header parsing code will now decode encoded words that are (incorrectly) surrounded by quotes, and register a defect. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 23:23:27 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 8 Feb 2014 23:23:27 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_doc=3A_sort_method?= =?utf-8?q?s?= Message-ID: <3fM7FC3bc0z7LjQ@mail.python.org> http://hg.python.org/cpython/rev/6c07690d9f3a changeset: 89063:6c07690d9f3a user: Victor Stinner date: Sat Feb 08 22:50:07 2014 +0100 summary: asyncio doc: sort methods files: Doc/library/asyncio-stream.rst | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -123,6 +123,11 @@ Transport. + .. method:: can_write_eof() + + Return :const:`True` if the transport supports :meth:`write_eof`, + :const:`False` if not. See :meth:`WriteTransport.can_write_eof`. + .. method:: close() Close the transport: see :meth:`BaseTransport.close`. @@ -158,11 +163,6 @@ Write a list (or any iterable) of data bytes to the transport: see :meth:`WriteTransport.writelines`. - .. method:: can_write_eof() - - Return :const:`True` if the transport supports :meth:`write_eof`, - :const:`False` if not. See :meth:`WriteTransport.can_write_eof`. - .. method:: write_eof() Close the write end of the transport after flushing buffered data: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 23:23:28 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 8 Feb 2014 23:23:28 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_doc=3A_document_mi?= =?utf-8?q?ssing_event_loop_methods?= Message-ID: <3fM7FD5KbGz7Lks@mail.python.org> http://hg.python.org/cpython/rev/a4030dd01456 changeset: 89064:a4030dd01456 user: Victor Stinner date: Sat Feb 08 23:22:58 2014 +0100 summary: asyncio doc: document missing event loop methods files: Doc/library/asyncio-eventloop.rst | 90 ++++++++++++++++++- 1 files changed, 86 insertions(+), 4 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -272,16 +272,98 @@ -Resolve name ------------- +Watch file descriptors +---------------------- + +.. method:: BaseEventLoop.add_reader(fd, callback, \*args) + + Start watching the file descriptor for read availability and then call the + *callback* with specified arguments. + +.. method:: BaseEventLoop.remove_reader(fd) + + Stop watching the file descriptor for read availability. + +.. method:: BaseEventLoop.add_writer(fd, callback, \*args) + + Start watching the file descriptor for write availability and then call the + *callback* with specified arguments. + +.. method:: BaseEventLoop.remove_writer(fd) + + Stop watching the file descriptor for write availability. + + +Low-level socket operations +--------------------------- + +.. method:: BaseEventLoop.sock_recv(sock, nbytes) + + Receive data from the socket. The return value is a bytes object + representing the data received. The maximum amount of data to be received + at once is specified by *nbytes*. + + This method returns a :ref:`coroutine object `. + + .. seealso:: + + The :meth:`socket.socket.recv` method. + +.. method:: BaseEventLoop.sock_sendall(sock, data) + + Send data to the socket. The socket must be connected to a remote socket. + This method continues to send data from *data* until either all data has + been sent or an error occurs. ``None`` is returned on success. On error, + an exception is raised, and there is no way to determine how much data, if + any, was successfully sent. + + This method returns a :ref:`coroutine object `. + + .. seealso:: + + The :meth:`socket.socket.sendall` method. + +.. method:: BaseEventLoop.sock_connect(sock, address) + + Connect to a remote socket at *address*. + + This method returns a :ref:`coroutine object `. + + .. seealso:: + + The :meth:`BaseEventLoop.create_connection` method, the + :func:`open_connection` function and the :meth:`socket.socket.connect` + method. + + +.. method:: BaseEventLoop.sock_accept(sock) + + Accept a connection. The socket must be bound to an address and listening + for connections. The return value is a pair ``(conn, address)`` where *conn* + is a *new* socket object usable to send and receive data on the connection, + and *address* is the address bound to the socket on the other end of the + connection. + + This method returns a :ref:`coroutine object `. + + .. seealso:: + + The :meth:`BaseEventLoop.create_server` method, the :func:`start_server` + function and the :meth:`socket.socket.accept` method. + + +Resolve host name +----------------- .. method:: BaseEventLoop.getaddrinfo(host, port, \*, family=0, type=0, proto=0, flags=0) - XXX + Similar to the :meth:`socket.getaddrinfo` function, but return a + :ref:`coroutine object `. .. method:: BaseEventLoop.getnameinfo(sockaddr, flags=0) - XXX + Similar to the :meth:`socket.getnameinfo` function, but return a + :ref:`coroutine object `. Running subprocesses -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 23:28:53 2014 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 8 Feb 2014 23:28:53 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_Include_the_me?= =?utf-8?q?ntion_of_ResourceWarning_being_displayed_by_default_by_the_test?= Message-ID: <3fM7MT1DT9z7Ljg@mail.python.org> http://hg.python.org/cpython/rev/a8a6dc7f478b changeset: 89065:a8a6dc7f478b branch: 3.3 parent: 89061:4816ab0477d2 user: Senthil Kumaran date: Sat Feb 08 14:28:03 2014 -0800 summary: Include the mention of ResourceWarning being displayed by default by the test runner. Addressing #issue 20529 files: Doc/library/unittest.rst | 15 ++++++++------- 1 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1754,13 +1754,14 @@ applications which run test suites should provide alternate implementations. By default this runner shows :exc:`DeprecationWarning`, - :exc:`PendingDeprecationWarning`, and :exc:`ImportWarning` even if they are - :ref:`ignored by default `. Deprecation warnings caused by - :ref:`deprecated unittest methods ` are also - special-cased and, when the warning filters are ``'default'`` or ``'always'``, - they will appear only once per-module, in order to avoid too many warning - messages. This behavior can be overridden using the :option:`-Wd` or - :option:`-Wa` options and leaving *warnings* to ``None``. + :exc:`PendingDeprecationWarning`, :exc:`ResourceWarning` and + :exc:`ImportWarning` even if they are :ref:`ignored by default `. Deprecation warnings caused by :ref:`deprecated unittest methods + ` are also special-cased and, when the warning filters + are ``'default'`` or ``'always'``, they will appear only once per-module, in + order to avoid too many warning messages. This behavior can be overridden + using the :option:`-Wd` or :option:`-Wa` options and leaving *warnings* to + ``None``. .. versionchanged:: 3.2 Added the ``warnings`` argument. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 23:28:54 2014 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 8 Feb 2014 23:28:54 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E3?= Message-ID: <3fM7MV3QGPz7LkY@mail.python.org> http://hg.python.org/cpython/rev/7fc1e8095fb8 changeset: 89066:7fc1e8095fb8 parent: 89064:a4030dd01456 parent: 89065:a8a6dc7f478b user: Senthil Kumaran date: Sat Feb 08 14:28:44 2014 -0800 summary: merge from 3.3 Include the mention of ResourceWarning being displayed by default by the test runner. Addressing #issue 20529 files: Doc/library/unittest.rst | 15 ++++++++------- 1 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1916,13 +1916,14 @@ applications which run test suites should provide alternate implementations. By default this runner shows :exc:`DeprecationWarning`, - :exc:`PendingDeprecationWarning`, and :exc:`ImportWarning` even if they are - :ref:`ignored by default `. Deprecation warnings caused by - :ref:`deprecated unittest methods ` are also - special-cased and, when the warning filters are ``'default'`` or ``'always'``, - they will appear only once per-module, in order to avoid too many warning - messages. This behavior can be overridden using the :option:`-Wd` or - :option:`-Wa` options and leaving *warnings* to ``None``. + :exc:`PendingDeprecationWarning`, :exc:`ResourceWarning` and + :exc:`ImportWarning` even if they are :ref:`ignored by default `. Deprecation warnings caused by :ref:`deprecated unittest methods + ` are also special-cased and, when the warning filters + are ``'default'`` or ``'always'``, they will appear only once per-module, in + order to avoid too many warning messages. This behavior can be overridden + using the :option:`-Wd` or :option:`-Wa` options and leaving *warnings* to + ``None``. .. versionchanged:: 3.2 Added the ``warnings`` argument. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 23:56:38 2014 From: python-checkins at python.org (r.david.murray) Date: Sat, 8 Feb 2014 23:56:38 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzE0OTgzOiBhbHdh?= =?utf-8?q?ys_add_a_line_end_after_a_MIME_boundary_marker=2E?= Message-ID: <3fM7zV2Fj5z7Ljd@mail.python.org> http://hg.python.org/cpython/rev/d579866d6419 changeset: 89067:d579866d6419 branch: 2.7 parent: 89052:d4f9efd4be7d user: R David Murray date: Sat Feb 08 17:54:12 2014 -0500 summary: #14983: always add a line end after a MIME boundary marker. This is more RFC compliant (see issue) and fixes a problem with signature verifiers rejecting the part when signed. There is some amount of backward compatibility concern here since it changes the output, but the RFC issue coupled with fixing the problem with signature verifiers seems worth the small risk of breaking code that depends on the current incorrect output. files: Lib/email/generator.py | 3 +-- Lib/email/test/data/msg_02.txt | 1 + Lib/email/test/test_email.py | 15 ++++++++++----- Lib/email/test/test_email_renamed.py | 15 ++++++++++----- Misc/NEWS | 4 ++++ 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Lib/email/generator.py b/Lib/email/generator.py --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -231,9 +231,8 @@ # body-part self._fp.write(body_part) # close-delimiter transport-padding - self._fp.write('\n--' + boundary + '--') + self._fp.write('\n--' + boundary + '--' + NL) if msg.epilogue is not None: - print >> self._fp if self._mangle_from_: epilogue = fcre.sub('>From ', msg.epilogue) else: diff --git a/Lib/email/test/data/msg_02.txt b/Lib/email/test/data/msg_02.txt --- a/Lib/email/test/data/msg_02.txt +++ b/Lib/email/test/data/msg_02.txt @@ -119,6 +119,7 @@ --__--__---- + --192.168.1.2.889.32614.987812255.500.21814 Content-type: text/plain; charset=us-ascii Content-description: Digest Footer diff --git a/Lib/email/test/test_email.py b/Lib/email/test/test_email.py --- a/Lib/email/test/test_email.py +++ b/Lib/email/test/test_email.py @@ -1206,7 +1206,8 @@ --BOUNDARY ---BOUNDARY--''') +--BOUNDARY-- +''') def test_no_parts_in_a_multipart_with_empty_epilogue(self): outer = MIMEBase('multipart', 'mixed') @@ -1251,7 +1252,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_empty_preamble(self): eq = self.ndiffAssertEqual @@ -1277,7 +1279,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_none_preamble(self): @@ -1303,7 +1306,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_none_epilogue(self): @@ -1329,7 +1333,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_empty_epilogue(self): diff --git a/Lib/email/test/test_email_renamed.py b/Lib/email/test/test_email_renamed.py --- a/Lib/email/test/test_email_renamed.py +++ b/Lib/email/test/test_email_renamed.py @@ -1146,7 +1146,8 @@ --BOUNDARY ---BOUNDARY--''') +--BOUNDARY-- +''') def test_no_parts_in_a_multipart_with_empty_epilogue(self): outer = MIMEBase('multipart', 'mixed') @@ -1191,7 +1192,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_empty_preamble(self): eq = self.ndiffAssertEqual @@ -1217,7 +1219,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_none_preamble(self): @@ -1243,7 +1246,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_none_epilogue(self): @@ -1269,7 +1273,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_empty_epilogue(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -42,6 +42,10 @@ Library ------- +- Issue #14983: email.generator now always adds a line end after each MIME + boundary marker, instead of doing so only when there is an epilogue. This + fixes an RFC compliance bug and solves an issue with signed MIME parts. + - Issue #20013: Some imap servers disconnect if the current mailbox is deleted, and imaplib did not handle that case gracefully. Now it handles the 'bye' correctly. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 23:56:39 2014 From: python-checkins at python.org (r.david.murray) Date: Sat, 8 Feb 2014 23:56:39 +0100 (CET) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogIzE0OTgzOiBhbHdh?= =?utf-8?q?ys_add_a_line_end_after_a_MIME_boundary_marker=2E?= Message-ID: <3fM7zW42sPz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/c2538789c8cf changeset: 89068:c2538789c8cf branch: 3.3 parent: 89065:a8a6dc7f478b user: R David Murray date: Sat Feb 08 17:54:56 2014 -0500 summary: #14983: always add a line end after a MIME boundary marker. This is more RFC compliant (see issue) and fixes a problem with signature verifiers rejecting the part when signed. There is some amount of backward compatibility concern here since it changes the output, but the RFC issue coupled with fixing the problem with signature verifiers seems worth the small risk of breaking code that depends on the current incorrect output. files: Lib/email/generator.py | 3 +-- Lib/test/test_email/data/msg_02.txt | 1 + Lib/test/test_email/test_email.py | 15 ++++++++++----- Misc/NEWS | 4 ++++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Lib/email/generator.py b/Lib/email/generator.py --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -299,9 +299,8 @@ # body-part self._fp.write(body_part) # close-delimiter transport-padding - self.write(self._NL + '--' + boundary + '--') + self.write(self._NL + '--' + boundary + '--' + self._NL) if msg.epilogue is not None: - self.write(self._NL) if self._mangle_from_: epilogue = fcre.sub('>From ', msg.epilogue) else: diff --git a/Lib/test/test_email/data/msg_02.txt b/Lib/test/test_email/data/msg_02.txt --- a/Lib/test/test_email/data/msg_02.txt +++ b/Lib/test/test_email/data/msg_02.txt @@ -119,6 +119,7 @@ --__--__---- + --192.168.1.2.889.32614.987812255.500.21814 Content-type: text/plain; charset=us-ascii Content-description: Digest Footer diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -1711,7 +1711,8 @@ --BOUNDARY ---BOUNDARY--''') +--BOUNDARY-- +''') def test_no_parts_in_a_multipart_with_empty_epilogue(self): outer = MIMEBase('multipart', 'mixed') @@ -1756,7 +1757,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_empty_preamble(self): eq = self.ndiffAssertEqual @@ -1782,7 +1784,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_none_preamble(self): @@ -1808,7 +1811,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_none_epilogue(self): @@ -1834,7 +1838,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_empty_epilogue(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -48,6 +48,10 @@ Library ------- +- Issue #14983: email.generator now always adds a line end after each MIME + boundary marker, instead of doing so only when there is an epilogue. This + fixes an RFC compliance bug and solves an issue with signed MIME parts. + - Issue #20540: Fix a performance regression (vs. Python 3.2) when layering a multiprocessing Connection over a TCP socket. For small payloads, Nagle's algorithm would introduce idle delays before the entire transmission of a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Feb 8 23:56:40 2014 From: python-checkins at python.org (r.david.murray) Date: Sat, 8 Feb 2014 23:56:40 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E3_-=3E_default?= =?utf-8?q?=29=3A_Merge=3A_=2314983=3A_always_add_a_line_end_after_a_MIME_?= =?utf-8?q?boundary_marker=2E?= Message-ID: <3fM7zX6BMRz7Lkn@mail.python.org> http://hg.python.org/cpython/rev/7486c45eb53f changeset: 89069:7486c45eb53f parent: 89066:7fc1e8095fb8 parent: 89068:c2538789c8cf user: R David Murray date: Sat Feb 08 17:56:17 2014 -0500 summary: Merge: #14983: always add a line end after a MIME boundary marker. files: Lib/email/generator.py | 3 +-- Lib/test/test_email/data/msg_02.txt | 1 + Lib/test/test_email/test_email.py | 15 ++++++++++----- Misc/NEWS | 4 ++++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Lib/email/generator.py b/Lib/email/generator.py --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -299,9 +299,8 @@ # body-part self._fp.write(body_part) # close-delimiter transport-padding - self.write(self._NL + '--' + boundary + '--') + self.write(self._NL + '--' + boundary + '--' + self._NL) if msg.epilogue is not None: - self.write(self._NL) if self._mangle_from_: epilogue = fcre.sub('>From ', msg.epilogue) else: diff --git a/Lib/test/test_email/data/msg_02.txt b/Lib/test/test_email/data/msg_02.txt --- a/Lib/test/test_email/data/msg_02.txt +++ b/Lib/test/test_email/data/msg_02.txt @@ -119,6 +119,7 @@ --__--__---- + --192.168.1.2.889.32614.987812255.500.21814 Content-type: text/plain; charset=us-ascii Content-description: Digest Footer diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -1745,7 +1745,8 @@ --BOUNDARY ---BOUNDARY--''') +--BOUNDARY-- +''') def test_no_parts_in_a_multipart_with_empty_epilogue(self): outer = MIMEBase('multipart', 'mixed') @@ -1790,7 +1791,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_empty_preamble(self): eq = self.ndiffAssertEqual @@ -1816,7 +1818,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_none_preamble(self): @@ -1842,7 +1845,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_none_epilogue(self): @@ -1868,7 +1872,8 @@ Content-Transfer-Encoding: 7bit hello world ---BOUNDARY--''') +--BOUNDARY-- +''') def test_seq_parts_in_a_multipart_with_empty_epilogue(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,10 @@ Library ------- +- Issue #14983: email.generator now always adds a line end after each MIME + boundary marker, instead of doing so only when there is an epilogue. This + fixes an RFC compliance bug and solves an issue with signed MIME parts. + - Issue #20540: Fix a performance regression (vs. Python 3.2) when layering a multiprocessing Connection over a TCP socket. For small payloads, Nagle's algorithm would introduce idle delays before the entire transmission of a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 00:18:39 2014 From: python-checkins at python.org (nick.coghlan) Date: Sun, 9 Feb 2014 00:18:39 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_=2320563=3A_Declare_?= =?utf-8?q?ipaddress_API_stable?= Message-ID: <3fM8Sv4Q4zz7LjN@mail.python.org> http://hg.python.org/cpython/rev/2b9c10f3b969 changeset: 89070:2b9c10f3b969 user: Nick Coghlan date: Sun Feb 09 09:18:26 2014 +1000 summary: Close #20563: Declare ipaddress API stable files: Doc/library/ipaddress.rst | 7 ------- Doc/whatsnew/3.4.rst | 11 +++++++++++ Misc/NEWS | 2 ++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Doc/library/ipaddress.rst b/Doc/library/ipaddress.rst --- a/Doc/library/ipaddress.rst +++ b/Doc/library/ipaddress.rst @@ -9,13 +9,6 @@ -------------- -.. note:: - - The ``ipaddress`` module has been included in the standard library on a - :term:`provisional basis `. Backwards incompatible - changes (up to and including removal of the package) may occur if deemed - necessary by the core developers. - :mod:`ipaddress` provides the capabilities to create, manipulate and operate on IPv4 and IPv6 addresses and networks. diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -134,6 +134,7 @@ handling ` (:issue:`18891`). * :mod:`plistlib` has a cleaned up interface and support for binary plist files (:issue:`14455`) +* The :mod:`ipaddress` module API has been declared stable CPython implementation improvements: @@ -840,6 +841,16 @@ by Stefan Behnel and Yury Selivanov in :issue:`17159`) +ipaddress +--------- + +:mod:`ipaddress` was added to the standard library in Python 3.3 as a +:term:`provisional API`. With the release of Python 3.4, this qualification +has been removed: :mod:`ipaddress` is now considered a stable API, covered +by the normal standard library requirements to maintain backwards +compatibility. + + logging ------- diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,8 @@ Library ------- +- Issue #20563: The ipaddress module API is now considered stable. + - Issue #14983: email.generator now always adds a line end after each MIME boundary marker, instead of doing so only when there is an epilogue. This fixes an RFC compliance bug and solves an issue with signed MIME parts. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 01:10:39 2014 From: python-checkins at python.org (nick.coghlan) Date: Sun, 9 Feb 2014 01:10:39 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Tweaks_to_What=27s_New_and?= =?utf-8?q?_some_referenced_docs?= Message-ID: <3fM9cv0Lvlz7Lmr@mail.python.org> http://hg.python.org/cpython/rev/254b4bb1de9e changeset: 89071:254b4bb1de9e user: Nick Coghlan date: Sun Feb 09 10:10:24 2014 +1000 summary: Tweaks to What's New and some referenced docs files: Doc/c-api/type.rst | 2 +- Doc/library/venv.rst | 3 +- Doc/whatsnew/3.4.rst | 50 +++++++++++++++++++++++-------- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -100,7 +100,7 @@ .. c:function:: void* PyType_GetSlot(PyTypeObject *type, int slot) - Return the function pointer stored int the given slot. If the + Return the function pointer stored in the given slot. If the result is *NULL*, this indicates that either the slot is *NULL*, or that the function was called with invalid parameters. Callers will typically cast the result pointer into the appropriate diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -107,7 +107,8 @@ upgraded in-place (defaults to ``False``). * ``with_pip`` -- a Boolean value which, if true, ensures pip is - installed in the virtual environment + installed in the virtual environment. This uses :mod:`ensurepip` with + the ``--default-pip`` option. .. versionchanged:: 3.4 Added the ``with_pip`` parameter diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -69,8 +69,9 @@ `changelog `_. .. note:: Prerelease users should be aware that this document is currently in - draft form. It will be updated substantially as Python 3.4 moves towards - release, so it's worth checking back even after reading earlier versions. + draft form. While it should be close to complete for the Python 3.4 + release candidates, adjustments and additions to the document may be made + up until the final release. .. seealso:: @@ -132,10 +133,9 @@ a new :mod:`~email.message.Message` subclass (:class:`~email.contentmanager.EmailMessage`) that :ref:`simplify MIME handling ` (:issue:`18891`). -* :mod:`plistlib` has a cleaned up interface and support for binary - plist files (:issue:`14455`) * The :mod:`ipaddress` module API has been declared stable + CPython implementation improvements: * :ref:`Safe object finalization ` (:pep:`442`). @@ -163,9 +163,13 @@ -------------------------------------------------------------- The new :mod:`ensurepip` module (defined in :pep:`453`) provides a standard -cross-platform mechanism to boostrap the pip installer into Python +cross-platform mechanism to bootstrap the pip installer into Python installations and virtual environments. +By default, the scripts ``pipX`` and ``pipX.Y`` will be installed (where +X.Y stands for the version of the Python installation), along with the +``pip`` Python package and its dependencies. + The :mod:`venv` module and the :command:`pyvenv` utility make use of this module to make ``pip`` readily available in virtual environments. When using the command line interface, ``pip`` is installed by default, while @@ -187,12 +191,13 @@ .. note:: - The implementation of PEP 453 is still a work in progress. Refer to - :issue:`19347` for the progress on additional steps: - - * Having the binary installers install ``pip`` by default - * Recommending the use of ``pip`` in the "Installing Python Module" - documentation. + To avoid conflicts between parallel Python 2 and Python 3 installations, + only the versioned ``pip3`` and ``pip3.4`` commands are bootstrapped by + default when ``ensurepip`` is invoked directly (including by the CPython + installers). ``pyvenv`` ensures that the unqualified ``pip`` command is + made available in virtual environments, and ``pip`` can always be + invoked via the ``-m`` switch rather than directly to avoid ambiguity on + systems with multiple Python installations. .. seealso:: @@ -257,6 +262,11 @@ File "", line 1, in LookupError: 'rot13' is not a text encoding; use codecs.encode() to handle arbitrary codecs + >>> open("foo.txt", encoding="hex") + Traceback (most recent call last): + File "", line 1, in + LookupError: 'hex' is not a text encoding; use codecs.open() to handle arbitrary codecs + In a related change, whenever it is feasible without breaking backwards compatibility, exceptions raised during encoding and decoding operations will be wrapped in a chained exception of the same type that mentions the @@ -308,6 +318,7 @@ (Contributed by Nick Coghlan in :issue:`7475`, , :issue:`17827`, :issue:`17828` and :issue:`19619`) + .. _whatsnew-pep-451: PEP 451: A ModuleSpec Type for the Import System @@ -959,8 +970,6 @@ pickle ------ -protocol 4 - :mod:`pickle` now supports (but does not use by default) a new pickle protocol, protocol 4. This new protocol addresses a number of issues that were present in previous protocols, such as the serialization of nested classes, very large @@ -973,6 +982,14 @@ PEP written by Antoine Pitrou and implemented by Alexandre Vassalotti. +plistlib +-------- + +:mod:`plistlib` now supports binary plist files, and offers the common +``load``/``loads``/``dump``/``dumps`` API pattern for serialization formats +(Contributed by Ronald Oussoren and others in :issue:`14455`). + + poplib ------ @@ -1354,6 +1371,9 @@ accurate signatures for builtins and standard library extension modules implemented in C. +Some standard library extension modules have been converted to use Argument +Clinic in Python 3.4, and :mod:`inspect` has been updated accordingly. + .. note:: The Argument Clinic PEP is not fully up to date with the state of the implementation. This has been deemed acceptable by the release manager @@ -1369,6 +1389,10 @@ Other Build and C API Changes ----------------------------- +* The new :c:func:`PyType_GetSlot` function has been added to the stable ABI, + allowing retrieval of function pointers from named type slots when using + the limited API. (Contributed by Martin von L?wis in :issue:`17162`) + * The new :c:func:`Py_SetStandardStreamEncoding` pre-initialization API allows applications embedding the CPython interpreter to reliably force a particular encoding and error handler for the standard streams -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 01:26:31 2014 From: python-checkins at python.org (victor.stinner) Date: Sun, 9 Feb 2014 01:26:31 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_tests=3A_Remove_sc?= =?utf-8?q?ories_of_resolution/granularity?= Message-ID: <3fM9zC4hvQz7LjM@mail.python.org> http://hg.python.org/cpython/rev/53de17e05d05 changeset: 89072:53de17e05d05 user: Victor Stinner date: Sun Feb 09 01:25:52 2014 +0100 summary: asyncio tests: Remove scories of resolution/granularity files: Lib/test/test_asyncio/test_proactor_events.py | 2 -- Lib/test/test_asyncio/test_selector_events.py | 1 - 2 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_asyncio/test_proactor_events.py b/Lib/test/test_asyncio/test_proactor_events.py --- a/Lib/test/test_asyncio/test_proactor_events.py +++ b/Lib/test/test_asyncio/test_proactor_events.py @@ -17,7 +17,6 @@ def setUp(self): self.loop = test_utils.TestLoop() self.proactor = unittest.mock.Mock() - self.proactor.resolution = 1e-3 self.loop._proactor = self.proactor self.protocol = test_utils.make_test_protocol(asyncio.Protocol) self.sock = unittest.mock.Mock(socket.socket) @@ -343,7 +342,6 @@ def setUp(self): self.sock = unittest.mock.Mock(socket.socket) self.proactor = unittest.mock.Mock() - self.proactor.resolution = 1e-3 self.ssock, self.csock = unittest.mock.Mock(), unittest.mock.Mock() diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -39,7 +39,6 @@ def setUp(self): selector = unittest.mock.Mock() - selector.resolution = 1e-3 self.loop = TestBaseSelectorEventLoop(selector) def test_make_socket_transport(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 01:35:38 2014 From: python-checkins at python.org (victor.stinner) Date: Sun, 9 Feb 2014 01:35:38 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio_doc=3A_fix_gather?= =?utf-8?b?KCkgZG9j?= Message-ID: <3fMB9k2MP3z7Lk8@mail.python.org> http://hg.python.org/cpython/rev/df5f692e9368 changeset: 89073:df5f692e9368 user: Victor Stinner date: Sun Feb 09 01:35:24 2014 +0100 summary: asyncio doc: fix gather() doc files: Doc/library/asyncio-task.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -415,7 +415,7 @@ All futures must share the same event loop. If all the tasks are done successfully, the returned future's result is the list of results (in the order of the original sequence, not necessarily the order of results - arrival). If *result_exception* is True, exceptions in the tasks are + arrival). If *return_exceptions* is True, exceptions in the tasks are treated the same as successful results, and gathered in the result list; otherwise, the first raised exception will be immediately propagated to the returned future. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 01:37:04 2014 From: python-checkins at python.org (brian.curtin) Date: Sun, 9 Feb 2014 01:37:04 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Spelling_corrections=2E_Su?= =?utf-8?q?bmitted_to_python-dev_by_python=40mrabarnett=2Eplus=2Ecom?= Message-ID: <3fMBCN1mbGz7Lk1@mail.python.org> http://hg.python.org/cpython/rev/3cfaeb788e00 changeset: 89074:3cfaeb788e00 parent: 89072:53de17e05d05 user: Brian Curtin date: Sat Feb 08 18:36:14 2014 -0600 summary: Spelling corrections. Submitted to python-dev by python at mrabarnett.plus.com files: Doc/library/asyncio-protocol.rst | 2 +- Doc/library/asyncio-subprocess.rst | 6 +++--- Doc/library/asyncio-task.rst | 4 ++-- Doc/library/asyncio.rst | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/library/asyncio-protocol.rst b/Doc/library/asyncio-protocol.rst --- a/Doc/library/asyncio-protocol.rst +++ b/Doc/library/asyncio-protocol.rst @@ -194,7 +194,7 @@ .. method:: get_pipe_transport(fd) - Return the transport for the communication pipe correspondong to the + Return the transport for the communication pipe corresponding to the integer file descriptor *fd*. The return value can be a readable or writable streaming transport, depending on the *fd*. If *fd* doesn't correspond to a pipe belonging to this transport, :const:`None` is diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -3,8 +3,8 @@ Subprocess ========== -Create a subproces ------------------- +Create a subprocess +------------------- .. function:: create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) @@ -116,7 +116,7 @@ Kills the child. On Posix OSs the function sends :py:data:`SIGKILL` to the child. On Windows :meth:`kill` is an alias for :meth:`terminate`. - .. method:: send_signal(signale) + .. method:: send_signal(signal) Sends the signal *signal* to the child process. diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -111,7 +111,7 @@ loop.close() ``compute()`` is chained to ``print_sum()``: ``print_sum()`` coroutine waits -until ``compute()`` is completed before returing its result. +until ``compute()`` is completed before returning its result. Sequence diagram of the example: @@ -315,7 +315,7 @@ The frames are always ordered from oldest to newest. - The optional limit gives the maximum nummber of frames to return; by + The optional limit gives the maximum number of frames to return; by default all available frames are returned. Its meaning differs depending on whether a stack or a traceback is returned: the newest frames of a stack are returned, but the oldest frames of a traceback are returned. diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -24,7 +24,7 @@ * concrete support for TCP, UDP, SSL, subprocess pipes, delayed calls, and others (some may be system-dependent); -* a :class:`Future` class that mimicks the one in the :mod:`concurrent.futures` +* a :class:`Future` class that mimics the one in the :mod:`concurrent.futures` module, but adapted for use with the event loop; * coroutines and tasks based on ``yield from`` (:PEP:`380`), to help write -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 01:37:05 2014 From: python-checkins at python.org (brian.curtin) Date: Sun, 9 Feb 2014 01:37:05 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3fMBCP3X9jz7Lk1@mail.python.org> http://hg.python.org/cpython/rev/435350b76922 changeset: 89075:435350b76922 parent: 89074:3cfaeb788e00 parent: 89073:df5f692e9368 user: Brian Curtin date: Sat Feb 08 18:36:57 2014 -0600 summary: merge files: Doc/library/asyncio-task.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -415,7 +415,7 @@ All futures must share the same event loop. If all the tasks are done successfully, the returned future's result is the list of results (in the order of the original sequence, not necessarily the order of results - arrival). If *result_exception* is True, exceptions in the tasks are + arrival). If *return_exceptions* is True, exceptions in the tasks are treated the same as successful results, and gathered in the result list; otherwise, the first raised exception will be immediately propagated to the returned future. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 01:47:12 2014 From: python-checkins at python.org (nick.coghlan) Date: Sun, 9 Feb 2014 01:47:12 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_=2320500=3A_Don=27t_?= =?utf-8?q?trigger_PyObject=5FStr_assertion_at_shutdown?= Message-ID: <3fMBR46KlYz7Lk1@mail.python.org> http://hg.python.org/cpython/rev/e7708864a8d5 changeset: 89076:e7708864a8d5 user: Nick Coghlan date: Sun Feb 09 10:43:21 2014 +1000 summary: Close #20500: Don't trigger PyObject_Str assertion at shutdown files: Lib/test/test_cmd_line_script.py | 18 ++++++++++++++++++ Misc/NEWS | 5 ++++- Objects/object.c | 2 +- Python/pythonrun.c | 5 +++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -405,6 +405,24 @@ 'stdout=%r stderr=%r' % (stdout, stderr)) self.assertEqual(0, rc) + def test_issue20500_exit_with_exception_value(self): + script = textwrap.dedent("""\ + import sys + error = None + try: + raise ValueError('some text') + except ValueError as err: + error = err + + if error: + sys.exit(error) + """) + with temp_dir() as script_dir: + script_name = _make_test_script(script_dir, 'script', script) + exitcode, stdout, stderr = assert_python_failure(script_name) + text = stderr.decode('ascii') + self.assertEqual(text, "some text") + def test_main(): support.run_unittest(CmdLineTest) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,7 +10,10 @@ Core and Builtins ----------------- -- Issue #20538: UTF-7 incremental decoder produced inconsistant string when +- Issue #20500: Displaying an exception at interpreter shutdown no longer + risks triggering an assertion failure in PyObject_Str. + +- Issue #20538: UTF-7 incremental decoder produced inconsistent string when input was truncated in BASE64 section. - Issue #20404: io.TextIOWrapper (and hence the open() builtin) now uses the diff --git a/Objects/object.c b/Objects/object.c --- a/Objects/object.c +++ b/Objects/object.c @@ -508,7 +508,7 @@ #ifdef Py_DEBUG /* PyObject_Str() must not be called with an exception set, because it may clear it (directly or indirectly) and so the - caller looses its exception */ + caller loses its exception */ assert(!PyErr_Occurred()); #endif diff --git a/Python/pythonrun.c b/Python/pythonrun.c --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1792,6 +1792,11 @@ exitcode = (int)PyLong_AsLong(value); else { PyObject *sys_stderr = _PySys_GetObjectId(&PyId_stderr); + /* We clear the exception here to avoid triggering the assertion + * in PyObject_Str that ensures it won't silently lose exception + * details. + */ + PyErr_Clear(); if (sys_stderr != NULL && sys_stderr != Py_None) { PyFile_WriteObject(value, sys_stderr, Py_PRINT_RAW); } else { -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 01:57:47 2014 From: python-checkins at python.org (nick.coghlan) Date: Sun, 9 Feb 2014 01:57:47 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Note_the_new_debug_asserti?= =?utf-8?q?on_in_PyObject=5FStr?= Message-ID: <3fMBgH5vjvz7Ljl@mail.python.org> http://hg.python.org/cpython/rev/2b3b298091fa changeset: 89077:2b3b298091fa user: Nick Coghlan date: Sun Feb 09 10:57:34 2014 +1000 summary: Note the new debug assertion in PyObject_Str files: Doc/c-api/object.rst | 4 ++++ Doc/whatsnew/3.4.rst | 7 +++++++ 2 files changed, 11 insertions(+), 0 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -170,6 +170,10 @@ Python expression ``str(o)``. Called by the :func:`str` built-in function and, therefore, by the :func:`print` function. + .. versionchanged:: 3.4 + This function now includes a debug assertion that ensures it does not + silently discard an active exception. + .. c:function:: PyObject* PyObject_Bytes(PyObject *o) .. index:: builtin: bytes diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -1708,6 +1708,13 @@ Changes in the C API -------------------- +* :c:func:`PyObject_Str` now includes a debug assertion that ensures it will + no longer silently discard currently active exceptions. In cases where + discarding the active exception is expected and desired (for example, + because it has already been saved locally with :c:func:`PyErr_Fetch`), an + explicit :c:func:`PyErr_Clear` call will be needed to avoid triggering the + assertion. + * :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg** argument is not set. Previously only ``NULL`` was returned with no exception set. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 02:35:52 2014 From: python-checkins at python.org (guido.van.rossum) Date: Sun, 9 Feb 2014 02:35:52 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=3A_Test_fix=2E?= Message-ID: <3fMCWD4MbBz7LjM@mail.python.org> http://hg.python.org/cpython/rev/a5dea12a45a1 changeset: 89078:a5dea12a45a1 user: Guido van Rossum date: Sat Feb 08 17:35:09 2014 -0800 summary: asyncio: Test fix. files: Lib/test/test_asyncio/test_tasks.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -860,7 +860,7 @@ def runner(): result = [] c = coro('ham') - for f in asyncio.as_completed({c, c, coro('spam')}, loop=self.loop): + for f in asyncio.as_completed([c, c, coro('spam')], loop=self.loop): result.append((yield from f)) return result -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 02:52:10 2014 From: python-checkins at python.org (victor.stinner) Date: Sun, 9 Feb 2014 02:52:10 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=3A_Remove_Process?= =?utf-8?q?=2Esubprocess_attribute=3B_it=27s_too_easy_to_get_inconsistent?= Message-ID: <3fMCt20pxDz7Lld@mail.python.org> http://hg.python.org/cpython/rev/8e00345c3b30 changeset: 89079:8e00345c3b30 user: Victor Stinner date: Sun Feb 09 02:51:40 2014 +0100 summary: asyncio: Remove Process.subprocess attribute; it's too easy to get inconsistent Process and Popen objects files: Doc/library/asyncio-subprocess.rst | 6 +--- Lib/asyncio/subprocess.py | 4 -- Lib/test/test_asyncio/test_subprocess.py | 20 ------------ 3 files changed, 1 insertions(+), 29 deletions(-) diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -87,10 +87,6 @@ Standard error stream (read), ``None`` if the process was created with ``stderr=None``. - .. attribute:: subprocess - - Underlying :class:`subprocess.Popen` object. - .. method:: communicate(input=None) Interact with process: Send data to stdin. Read data from stdout and @@ -102,7 +98,7 @@ :meth:`communicate` returns a tuple ``(stdoutdata, stderrdata)``. Note that if you want to send data to the process's stdin, you need to - create the Popen object with ``stdin=PIPE``. Similarly, to get anything + create the Process object with ``stdin=PIPE``. Similarly, to get anything other than ``None`` in the result tuple, you need to give ``stdout=PIPE`` and/or ``stderr=PIPE`` too. diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py --- a/Lib/asyncio/subprocess.py +++ b/Lib/asyncio/subprocess.py @@ -106,10 +106,6 @@ yield from waiter return waiter.result() - @property - def subprocess(self): - return self._transport.get_extra_info('subprocess') - def _check_alive(self): if self._transport.get_returncode() is not None: raise ProcessLookupError() diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -7,9 +7,6 @@ if sys.platform != 'win32': from asyncio import unix_events -# Program exiting quickly -PROGRAM_EXIT_FAST = [sys.executable, '-c', 'pass'] - # Program blocking PROGRAM_BLOCKED = [sys.executable, '-c', 'import time; time.sleep(3600)'] @@ -119,23 +116,6 @@ returncode = self.loop.run_until_complete(proc.wait()) self.assertEqual(-signal.SIGHUP, returncode) - def test_subprocess(self): - args = PROGRAM_EXIT_FAST - - @asyncio.coroutine - def run(): - proc = yield from asyncio.create_subprocess_exec(*args, - loop=self.loop) - yield from proc.wait() - # need to poll subprocess.Popen, otherwise the returncode - # attribute is not set - proc.subprocess.wait() - return proc - - proc = self.loop.run_until_complete(run()) - self.assertEqual(proc.subprocess.returncode, proc.returncode) - self.assertEqual(proc.subprocess.pid, proc.pid) - def test_broken_pipe(self): large_data = b'x' * support.PIPE_MAX_SIZE -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 03:01:03 2014 From: python-checkins at python.org (nick.coghlan) Date: Sun, 9 Feb 2014 03:01:03 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320500=3A_Note_oth?= =?utf-8?q?er_public_APIs_with_the_new_assertion?= Message-ID: <3fMD4H73rmz7LjM@mail.python.org> http://hg.python.org/cpython/rev/41023a501c7b changeset: 89080:41023a501c7b user: Nick Coghlan date: Sun Feb 09 12:00:01 2014 +1000 summary: Issue #20500: Note other public APIs with the new assertion files: Doc/c-api/object.rst | 7 +++++-- Doc/c-api/veryhigh.rst | 4 ++++ Doc/whatsnew/3.4.rst | 14 +++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -149,6 +149,9 @@ representation on success, *NULL* on failure. This is the equivalent of the Python expression ``repr(o)``. Called by the :func:`repr` built-in function. + .. versionchanged:: 3.4 + This function now includes a debug assertion to help ensure that it + does not silently discard an active exception. .. c:function:: PyObject* PyObject_ASCII(PyObject *o) @@ -171,8 +174,8 @@ and, therefore, by the :func:`print` function. .. versionchanged:: 3.4 - This function now includes a debug assertion that ensures it does not - silently discard an active exception. + This function now includes a debug assertion to help ensure that it + does not silently discard an active exception. .. c:function:: PyObject* PyObject_Bytes(PyObject *o) diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -322,6 +322,10 @@ it causes an exception to immediately be thrown; this is used for the :meth:`~generator.throw` methods of generator objects. + .. versionchanged:: 3.4 + This function now includes a debug assertion to help ensure that it + does not silently discard an active exception. + .. c:function:: int PyEval_MergeCompilerFlags(PyCompilerFlags *cf) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -1708,12 +1708,16 @@ Changes in the C API -------------------- -* :c:func:`PyObject_Str` now includes a debug assertion that ensures it will - no longer silently discard currently active exceptions. In cases where +* :c:func:`PyEval_EvalFrameEx`, :c:func:`PyObject_Repr(), and + :c:func:`PyObject_Str`, along with some other internal C APIs, now include + a debugging assertion that ensures they are not used in situations where + they may silently discard a currently active exception. In cases where discarding the active exception is expected and desired (for example, - because it has already been saved locally with :c:func:`PyErr_Fetch`), an - explicit :c:func:`PyErr_Clear` call will be needed to avoid triggering the - assertion. + because it has already been saved locally with :c:func:`PyErr_Fetch` or + is being deliberately replaced with a different exception), an explicit + :c:func:`PyErr_Clear` call will be needed to avoid triggering the + assertion when running against a version of Python that is compiled with + assertions enabled. * :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg** argument is not set. Previously only ``NULL`` was returned with no exception -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 03:05:26 2014 From: python-checkins at python.org (nick.coghlan) Date: Sun, 9 Feb 2014 03:05:26 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320500=3A_clarify_?= =?utf-8?q?that_invocation_may_be_indirect?= Message-ID: <3fMD9L2qSFz7LjM@mail.python.org> http://hg.python.org/cpython/rev/9e7594d65178 changeset: 89081:9e7594d65178 user: Nick Coghlan date: Sun Feb 09 12:05:13 2014 +1000 summary: Issue #20500: clarify that invocation may be indirect files: Doc/whatsnew/3.4.rst | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -1716,8 +1716,9 @@ because it has already been saved locally with :c:func:`PyErr_Fetch` or is being deliberately replaced with a different exception), an explicit :c:func:`PyErr_Clear` call will be needed to avoid triggering the - assertion when running against a version of Python that is compiled with - assertions enabled. + assertion when invoking these operations (directly or indirectly) and + running against a version of Python that is compiled with assertions + enabled. * :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg** argument is not set. Previously only ``NULL`` was returned with no exception -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 04:44:11 2014 From: python-checkins at python.org (guido.van.rossum) Date: Sun, 9 Feb 2014 04:44:11 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_asyncio=3A_Remove_more_rel?= =?utf-8?q?ics_of_resolution/granularity=2E?= Message-ID: <3fMGMH3KVjz7LjN@mail.python.org> http://hg.python.org/cpython/rev/346e507b2cfb changeset: 89082:346e507b2cfb user: Guido van Rossum date: Sat Feb 08 19:44:02 2014 -0800 summary: asyncio: Remove more relics of resolution/granularity. files: Lib/asyncio/test_utils.py | 5 ----- Lib/asyncio/windows_events.py | 1 - 2 files changed, 0 insertions(+), 6 deletions(-) diff --git a/Lib/asyncio/test_utils.py b/Lib/asyncio/test_utils.py --- a/Lib/asyncio/test_utils.py +++ b/Lib/asyncio/test_utils.py @@ -144,10 +144,6 @@ def __init__(self): self.keys = {} - @property - def resolution(self): - return 1e-3 - def register(self, fileobj, events, data=None): key = selectors.SelectorKey(fileobj, 0, events, data) self.keys[fileobj] = key @@ -196,7 +192,6 @@ next(self._gen) self._time = 0 self._timers = [] - self._granularity = 1e-9 self._selector = TestSelector() self.readers = {} diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -191,7 +191,6 @@ self._cache = {} self._registered = weakref.WeakSet() self._stopped_serving = weakref.WeakSet() - self.resolution = 1e-3 def set_loop(self, loop): self._loop = loop -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Feb 9 07:16:12 2014 From: python-checkins at python.org (larry.hastings) Date: Sun, 9 Feb 2014 07:16:12 +0100 (CET) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320530=3A_Argument?= =?utf-8?q?_Clinic=27s_signature_format_has_been_revised_again=2E?= Message-ID: <3fMKkh2HNZz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/29d9638bf449 changeset: 89083:29d9638bf449 user: Larry Hastings date: Sat Feb 08 22:15:29 2014 -0800 summary: Issue #20530: Argument Clinic's signature format has been revised again. The new syntax is highly human readable while still preventing false positives. The syntax also extends Python syntax to denote "self" and positional-only parameters, allowing inspect.Signature objects to be totally accurate for all supported builtins in Python 3.4. files: Include/object.h | 4 +- Lib/inspect.py | 117 +++++++++-- Lib/test/test_capi.py | 17 +- Lib/test/test_inspect.py | 73 ++++++- Misc/NEWS | 16 +- Modules/_cryptmodule.c | 6 +- Modules/_datetimemodule.c | 6 +- Modules/_dbmmodule.c | 12 +- Modules/_opcode.c | 6 +- Modules/_sre.c | 6 +- Modules/_testcapimodule.c | 27 ++- Modules/_weakref.c | 6 +- Modules/clinic/_bz2module.c.h | 22 +- Modules/clinic/_lzmamodule.c.h | 30 ++- Modules/clinic/_pickle.c.h | 68 +++++- Modules/clinic/audioop.c.h | 107 ++++++++-- Modules/clinic/binascii.c.h | 58 ++++- Modules/clinic/zlibmodule.c.h | 52 +++- Modules/posixmodule.c | 19 +- Modules/unicodedata.c | 6 +- Objects/descrobject.c | 12 +- Objects/dictobject.c | 12 +- Objects/methodobject.c | 4 +- Objects/typeobject.c | 205 ++++++++++++-------- Objects/unicodeobject.c | 6 +- Python/import.c | 72 ++++-- Tools/clinic/clinic.py | 162 +++++++++++---- Tools/clinic/clinic_test.py | 43 +++- 28 files changed, 836 insertions(+), 338 deletions(-) diff --git a/Include/object.h b/Include/object.h --- a/Include/object.h +++ b/Include/object.h @@ -496,8 +496,8 @@ PyAPI_FUNC(void) PyType_Modified(PyTypeObject *); #ifndef Py_LIMITED_API -PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *); -PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *); +PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *); +PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *); #endif /* Generic operations on objects */ diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -39,6 +39,7 @@ import re import sys import tokenize +import token import types import warnings import functools @@ -1648,25 +1649,88 @@ return spec[2:pos] +def _signature_strip_non_python_syntax(signature): + """ + Takes a signature in Argument Clinic's extended signature format. + Returns a tuple of three things: + * that signature re-rendered in standard Python syntax, + * the index of the "self" parameter (generally 0), or None if + the function does not have a "self" parameter, and + * the index of the last "positional only" parameter, + or None if the signature has no positional-only parameters. + """ + + if not signature: + return signature, None, None + + self_parameter = None + last_positional_only = None + + lines = [l.encode('ascii') for l in signature.split('\n')] + generator = iter(lines).__next__ + token_stream = tokenize.tokenize(generator) + + delayed_comma = False + skip_next_comma = False + text = [] + add = text.append + + current_parameter = 0 + OP = token.OP + ERRORTOKEN = token.ERRORTOKEN + + # token stream always starts with ENCODING token, skip it + t = next(token_stream) + assert t.type == tokenize.ENCODING + + for t in token_stream: + type, string = t.type, t.string + + if type == OP: + if string == ',': + if skip_next_comma: + skip_next_comma = False + else: + assert not delayed_comma + delayed_comma = True + current_parameter += 1 + continue + + if string == '/': + assert not skip_next_comma + assert last_positional_only is None + skip_next_comma = True + last_positional_only = current_parameter - 1 + continue + + if (type == ERRORTOKEN) and (string == '$'): + assert self_parameter is None + self_parameter = current_parameter + continue + + if delayed_comma: + delayed_comma = False + if not ((type == OP) and (string == ')')): + add(', ') + add(string) + if (string == ','): + add(' ') + clean_signature = ''.join(text) + return clean_signature, self_parameter, last_positional_only + + def _signature_fromstr(cls, obj, s): # Internal helper to parse content of '__text_signature__' # and return a Signature based on it Parameter = cls._parameter_cls - if s.endswith("/)"): - kind = Parameter.POSITIONAL_ONLY - s = s[:-2] + ')' - else: - kind = Parameter.POSITIONAL_OR_KEYWORD - - first_parameter_is_self = s.startswith("($") - if first_parameter_is_self: - s = '(' + s[2:] - - s = "def foo" + s + ": pass" + clean_signature, self_parameter, last_positional_only = \ + _signature_strip_non_python_syntax(s) + + program = "def foo" + clean_signature + ": pass" try: - module = ast.parse(s) + module = ast.parse(program) except SyntaxError: module = None @@ -1750,8 +1814,14 @@ args = reversed(f.args.args) defaults = reversed(f.args.defaults) iter = itertools.zip_longest(args, defaults, fillvalue=None) - for name, default in reversed(list(iter)): + if last_positional_only is not None: + kind = Parameter.POSITIONAL_ONLY + else: + kind = Parameter.POSITIONAL_OR_KEYWORD + for i, (name, default) in enumerate(reversed(list(iter))): p(name, default) + if i == last_positional_only: + kind = Parameter.POSITIONAL_OR_KEYWORD # *args if f.args.vararg: @@ -1768,7 +1838,7 @@ kind = Parameter.VAR_KEYWORD p(f.args.kwarg, empty) - if first_parameter_is_self: + if self_parameter is not None: assert parameters if getattr(obj, '__self__', None): # strip off self, it's already been bound @@ -1861,12 +1931,13 @@ # At this point we know, that `obj` is a class, with no user- # defined '__init__', '__new__', or class-level '__call__' - for base in obj.__mro__: + for base in obj.__mro__[:-1]: # Since '__text_signature__' is implemented as a # descriptor that extracts text signature from the # class docstring, if 'obj' is derived from a builtin # class, its own '__text_signature__' may be 'None'. - # Therefore, we go through the MRO to find the first + # Therefore, we go through the MRO (except the last + # class in there, which is 'object') to find the first # class with non-empty text signature. try: text_sig = base.__text_signature__ @@ -1881,13 +1952,7 @@ # No '__text_signature__' was found for the 'obj' class. # Last option is to check if its '__init__' is # object.__init__ or type.__init__. - if type in obj.__mro__: - # 'obj' is a metaclass without user-defined __init__ - # or __new__. - if obj.__init__ is type.__init__: - # Return a signature of 'type' builtin. - return signature(type) - else: + if type not in obj.__mro__: # We have a class (not metaclass), but no user-defined # __init__ or __new__ for it if obj.__init__ is object.__init__: @@ -1901,7 +1966,11 @@ # infinite recursion (and even potential segfault) call = _signature_get_user_defined_method(type(obj), '__call__') if call is not None: - sig = signature(call) + try: + sig = signature(call) + except ValueError as ex: + msg = 'no signature found for {!r}'.format(obj) + raise ValueError(msg) from ex if sig is not None: # For classes and objects we skip the first parameter of their diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -126,20 +126,29 @@ self.assertEqual(_testcapi.docstring_no_signature.__text_signature__, None) self.assertEqual(_testcapi.docstring_with_invalid_signature.__doc__, - "sig= (module, boo)\n" + "docstring_with_invalid_signature($module, /, boo)\n" "\n" "This docstring has an invalid signature." ) self.assertEqual(_testcapi.docstring_with_invalid_signature.__text_signature__, None) + self.assertEqual(_testcapi.docstring_with_invalid_signature2.__doc__, + "docstring_with_invalid_signature2($module, /, boo)\n" + "\n" + "--\n" + "\n" + "This docstring also has an invalid signature." + ) + self.assertEqual(_testcapi.docstring_with_invalid_signature2.__text_signature__, None) + self.assertEqual(_testcapi.docstring_with_signature.__doc__, "This docstring has a valid signature.") - self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "(module, sig)") + self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "($module, /, sig)") self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__doc__, - "This docstring has a valid signature and some extra newlines.") + "\nThis docstring has a valid signature and some extra newlines.") self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__, - "(module, parameter)") + "($module, /, parameter)") @unittest.skipUnless(threading, 'Threading required for this test.') 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 @@ -1684,7 +1684,6 @@ self.assertEqual(p('sys'), sys.maxsize) self.assertEqual(p('exp'), sys.maxsize - 1) - test_callable(type) test_callable(object) # normal method @@ -1710,9 +1709,12 @@ # support for 'method-wrapper' test_callable(min.__call__) - class ThisWorksNow: - __call__ = type - test_callable(ThisWorksNow()) + # This doesn't work now. + # (We don't have a valid signature for "type" in 3.4) + with self.assertRaisesRegex(ValueError, "no signature found"): + class ThisWorksNow: + __call__ = type + test_callable(ThisWorksNow()) @cpython_only @unittest.skipIf(MISSING_C_DOCSTRINGS, @@ -2213,11 +2215,11 @@ # Test meta-classes without user-defined __init__ or __new__ class C(type): pass - self.assertEqual(str(inspect.signature(C)), - '(object_or_name, bases, dict)') class D(C): pass - self.assertEqual(str(inspect.signature(D)), - '(object_or_name, bases, dict)') + with self.assertRaisesRegex(ValueError, "callable.*is not supported"): + self.assertEqual(inspect.signature(C), None) + with self.assertRaisesRegex(ValueError, "callable.*is not supported"): + self.assertEqual(inspect.signature(D), None) @unittest.skipIf(MISSING_C_DOCSTRINGS, "Signature information for builtins requires docstrings") @@ -2768,6 +2770,61 @@ self.assertEqual(getter('($self, obj)'), 'self') self.assertEqual(getter('($cls, /, obj)'), 'cls') + def _strip_non_python_syntax(self, input, + clean_signature, self_parameter, last_positional_only): + computed_clean_signature, \ + computed_self_parameter, \ + computed_last_positional_only = \ + inspect._signature_strip_non_python_syntax(input) + self.assertEqual(computed_clean_signature, clean_signature) + self.assertEqual(computed_self_parameter, self_parameter) + self.assertEqual(computed_last_positional_only, last_positional_only) + + def test_signature_strip_non_python_syntax(self): + self._strip_non_python_syntax( + "($module, /, path, mode, *, dir_fd=None, " + + "effective_ids=False,\n follow_symlinks=True)", + "(module, path, mode, *, dir_fd=None, " + + "effective_ids=False, follow_symlinks=True)", + 0, + 0) + + self._strip_non_python_syntax( + "($module, word, salt, /)", + "(module, word, salt)", + 0, + 2) + + self._strip_non_python_syntax( + "(x, y=None, z=None, /)", + "(x, y=None, z=None)", + None, + 2) + + self._strip_non_python_syntax( + "(x, y=None, z=None)", + "(x, y=None, z=None)", + None, + None) + + self._strip_non_python_syntax( + "(x,\n y=None,\n z = None )", + "(x, y=None, z=None)", + None, + None) + + self._strip_non_python_syntax( + "", + "", + None, + None) + + self._strip_non_python_syntax( + None, + None, + None, + None) + class TestUnwrap(unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -200,18 +200,24 @@ Tools/Demos ----------- -- #Issue 20456: Argument Clinic now observes the C preprocessor conditional +- Issue #20530: Argument Clinic's signature format has been revised again. + The new syntax is highly human readable while still preventing false + positives. The syntax also extends Python syntax to denote "self" and + positional-only parameters, allowing inspect.Signature objects to be + totally accurate for all supported builtins in Python 3.4. + +- Issue #20456: Argument Clinic now observes the C preprocessor conditional compilation statements of the C files it parses. When a Clinic block is inside a conditional code, it adjusts its output to match, including automatically generating an empty methoddef macro. -- #Issue 20456: Cloned functions in Argument Clinic now use the correct +- Issue #20456: Cloned functions in Argument Clinic now use the correct name, not the name of the function they were cloned from, for text strings inside generated code. -- #Issue 20456: Fixed Argument Clinic's test suite and "--converters" feature. - -- #Issue 20456: Argument Clinic now allows specifying different names +- Issue #20456: Fixed Argument Clinic's test suite and "--converters" feature. + +- Issue #20456: Argument Clinic now allows specifying different names for a parameter in Python and C, using "as" on the parameter line. - Issue #20326: Argument Clinic now uses a simple, unique signature to diff --git a/Modules/_cryptmodule.c b/Modules/_cryptmodule.c --- a/Modules/_cryptmodule.c +++ b/Modules/_cryptmodule.c @@ -30,7 +30,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(crypt_crypt__doc__, -"sig=($module, word, salt)\n" +"crypt($module, word, salt, /)\n" +"--\n" +"\n" "Hash a *word* with the given *salt* and return the hashed password.\n" "\n" "*word* will usually be a user\'s password. *salt* (either a random 2 or 16\n" @@ -63,7 +65,7 @@ static PyObject * crypt_crypt_impl(PyModuleDef *module, const char *word, const char *salt) -/*[clinic end generated code: output=c7443257e03fca92 input=4d93b6d0f41fbf58]*/ +/*[clinic end generated code: output=3eaacdf994a6ff23 input=4d93b6d0f41fbf58]*/ { /* On some platforms (AtheOS) crypt returns NULL for an invalid salt. Return None in that case. XXX Maybe raise an exception? */ diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4159,7 +4159,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(datetime_datetime_now__doc__, -"sig=($type, tz=None)\n" +"now($type, /, tz=None)\n" +"--\n" +"\n" "Returns new datetime object representing current time local to tz.\n" "\n" " tz\n" @@ -4192,7 +4194,7 @@ static PyObject * datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz) -/*[clinic end generated code: output=c8a47308483e579a input=80d09869c5267d00]*/ +/*[clinic end generated code: output=583c5637e3c843fa input=80d09869c5267d00]*/ { PyObject *self; diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -278,7 +278,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(dbm_dbm_get__doc__, -"sig=($self, key, default=None)\n" +"get($self, key, default=None, /)\n" +"--\n" +"\n" "Return the value for key if present, otherwise default."); #define DBM_DBM_GET_METHODDEF \ @@ -307,7 +309,7 @@ static PyObject * dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length, PyObject *default_value) -/*[clinic end generated code: output=2bbaf9a187f9b6bf input=aecf5efd2f2b1a3b]*/ +/*[clinic end generated code: output=452ea11394e7e92d input=aecf5efd2f2b1a3b]*/ { datum dbm_key, val; @@ -448,7 +450,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(dbmopen__doc__, -"sig=($module, filename, flags=\'r\', mode=0o666)\n" +"open($module, filename, flags=\'r\', mode=0o666, /)\n" +"--\n" +"\n" "Return a database object.\n" "\n" " filename\n" @@ -485,7 +489,7 @@ static PyObject * dbmopen_impl(PyModuleDef *module, const char *filename, const char *flags, int mode) -/*[clinic end generated code: output=a1da6a481d9d332b input=6499ab0fab1333ac]*/ +/*[clinic end generated code: output=9a7b725f9c4dcec2 input=6499ab0fab1333ac]*/ { int iflags; diff --git a/Modules/_opcode.c b/Modules/_opcode.c --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -18,7 +18,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(_opcode_stack_effect__doc__, -"sig=($module, opcode, oparg=None)\n" +"stack_effect($module, opcode, oparg=None, /)\n" +"--\n" +"\n" "Compute the stack effect of the opcode."); #define _OPCODE_STACK_EFFECT_METHODDEF \ @@ -50,7 +52,7 @@ static int _opcode_stack_effect_impl(PyModuleDef *module, int opcode, PyObject *oparg) -/*[clinic end generated code: output=4fe636f5db87c0a9 input=2d0a9ee53c0418f5]*/ +/*[clinic end generated code: output=9e1133f8d587bc67 input=2d0a9ee53c0418f5]*/ { int effect; int oparg_int = 0; diff --git a/Modules/_sre.c b/Modules/_sre.c --- a/Modules/_sre.c +++ b/Modules/_sre.c @@ -540,7 +540,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(pattern_match__doc__, -"sig=($self, pattern, pos=0, endpos=sys.maxsize)\n" +"match($self, /, pattern, pos=0, endpos=sys.maxsize)\n" +"--\n" +"\n" "Matches zero or more characters at the beginning of the string."); #define PATTERN_MATCH_METHODDEF \ @@ -570,7 +572,7 @@ static PyObject * pattern_match_impl(PatternObject *self, PyObject *pattern, Py_ssize_t pos, Py_ssize_t endpos) -/*[clinic end generated code: output=9f5b785661677848 input=26f9fd31befe46b9]*/ +/*[clinic end generated code: output=1528eafdb8b025ad input=26f9fd31befe46b9]*/ { SRE_STATE state; Py_ssize_t status; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2851,26 +2851,40 @@ ); PyDoc_STRVAR(docstring_with_invalid_signature, -"sig= (module, boo)\n" +"docstring_with_invalid_signature($module, /, boo)\n" "\n" "This docstring has an invalid signature." ); +PyDoc_STRVAR(docstring_with_invalid_signature2, +"docstring_with_invalid_signature2($module, /, boo)\n" +"\n" +"--\n" +"\n" +"This docstring also has an invalid signature." +); + PyDoc_STRVAR(docstring_with_signature, -"sig=(module, sig)\n" +"docstring_with_signature($module, /, sig)\n" +"--\n" +"\n" "This docstring has a valid signature." ); PyDoc_STRVAR(docstring_with_signature_and_extra_newlines, -"sig=(module, parameter)\n" -"\n" +"docstring_with_signature_and_extra_newlines($module, /, parameter)\n" +"--\n" "\n" "\n" "This docstring has a valid signature and some extra newlines." ); PyDoc_STRVAR(docstring_with_signature_with_defaults, -"sig=(module, s='avocado', b=b'bytes', d=3.14, i=35, n=None, t=True, f=False, local=the_number_three, sys=sys.maxsize, exp=sys.maxsize - 1)\n" +"docstring_with_signature_with_defaults(module, s='avocado',\n" +" b=b'bytes', d=3.14, i=35, n=None, t=True, f=False,\n" +" local=the_number_three, sys=sys.maxsize,\n" +" exp=sys.maxsize - 1)\n" +"--\n" "\n" "\n" "\n" @@ -3090,6 +3104,9 @@ {"docstring_with_invalid_signature", (PyCFunction)test_with_docstring, METH_NOARGS, docstring_with_invalid_signature}, + {"docstring_with_invalid_signature2", + (PyCFunction)test_with_docstring, METH_NOARGS, + docstring_with_invalid_signature2}, {"docstring_with_signature", (PyCFunction)test_with_docstring, METH_NOARGS, docstring_with_signature}, diff --git a/Modules/_weakref.c b/Modules/_weakref.c --- a/Modules/_weakref.c +++ b/Modules/_weakref.c @@ -20,7 +20,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(_weakref_getweakrefcount__doc__, -"sig=($module, object)\n" +"getweakrefcount($module, object, /)\n" +"--\n" +"\n" "Return the number of weak references to \'object\'."); #define _WEAKREF_GETWEAKREFCOUNT_METHODDEF \ @@ -46,7 +48,7 @@ static Py_ssize_t _weakref_getweakrefcount_impl(PyModuleDef *module, PyObject *object) -/*[clinic end generated code: output=ef51baac56180816 input=cedb69711b6a2507]*/ +/*[clinic end generated code: output=032eedbfd7d69e10 input=cedb69711b6a2507]*/ { PyWeakReference **list; diff --git a/Modules/clinic/_bz2module.c.h b/Modules/clinic/_bz2module.c.h --- a/Modules/clinic/_bz2module.c.h +++ b/Modules/clinic/_bz2module.c.h @@ -3,7 +3,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(_bz2_BZ2Compressor_compress__doc__, -"sig=($self, data)\n" +"compress($self, data, /)\n" +"--\n" +"\n" "Provide data to the compressor object.\n" "\n" "Returns a chunk of compressed data if possible, or b\'\' otherwise.\n" @@ -38,7 +40,9 @@ } PyDoc_STRVAR(_bz2_BZ2Compressor_flush__doc__, -"sig=($self)\n" +"flush($self, /)\n" +"--\n" +"\n" "Finish the compression process.\n" "\n" "Returns the compressed data left in internal buffers.\n" @@ -58,7 +62,9 @@ } PyDoc_STRVAR(_bz2_BZ2Compressor___init____doc__, -"sig=(compresslevel=9)\n" +"BZ2Compressor(compresslevel=9, /)\n" +"--\n" +"\n" "Create a compressor object for compressing data incrementally.\n" "\n" " compresslevel\n" @@ -89,7 +95,9 @@ } PyDoc_STRVAR(_bz2_BZ2Decompressor_decompress__doc__, -"sig=($self, data)\n" +"decompress($self, data, /)\n" +"--\n" +"\n" "Provide data to the decompressor object.\n" "\n" "Returns a chunk of decompressed data if possible, or b\'\' otherwise.\n" @@ -125,7 +133,9 @@ } PyDoc_STRVAR(_bz2_BZ2Decompressor___init____doc__, -"sig=()\n" +"BZ2Decompressor()\n" +"--\n" +"\n" "Create a decompressor object for decompressing data incrementally.\n" "\n" "For one-shot decompression, use the decompress() function instead."); @@ -149,4 +159,4 @@ exit: return return_value; } -/*[clinic end generated code: output=aca4f6329c1c773a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=21ca4405519a0931 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_lzmamodule.c.h b/Modules/clinic/_lzmamodule.c.h --- a/Modules/clinic/_lzmamodule.c.h +++ b/Modules/clinic/_lzmamodule.c.h @@ -3,7 +3,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(_lzma_LZMACompressor_compress__doc__, -"sig=($self, data)\n" +"compress($self, data, /)\n" +"--\n" +"\n" "Provide data to the compressor object.\n" "\n" "Returns a chunk of compressed data if possible, or b\'\' otherwise.\n" @@ -38,7 +40,9 @@ } PyDoc_STRVAR(_lzma_LZMACompressor_flush__doc__, -"sig=($self)\n" +"flush($self, /)\n" +"--\n" +"\n" "Finish the compression process.\n" "\n" "Returns the compressed data left in internal buffers.\n" @@ -58,7 +62,9 @@ } PyDoc_STRVAR(_lzma_LZMADecompressor_decompress__doc__, -"sig=($self, data)\n" +"decompress($self, data, /)\n" +"--\n" +"\n" "Provide data to the decompressor object.\n" "\n" "Returns a chunk of decompressed data if possible, or b\'\' otherwise.\n" @@ -94,7 +100,9 @@ } PyDoc_STRVAR(_lzma_LZMADecompressor___init____doc__, -"sig=(format=FORMAT_AUTO, memlimit=None, filters=None)\n" +"LZMADecompressor(format=FORMAT_AUTO, memlimit=None, filters=None)\n" +"--\n" +"\n" "Create a decompressor object for decompressing data incrementally.\n" "\n" " format\n" @@ -137,7 +145,9 @@ } PyDoc_STRVAR(_lzma_is_check_supported__doc__, -"sig=($module, check_id)\n" +"is_check_supported($module, check_id, /)\n" +"--\n" +"\n" "Test whether the given integrity check is supported.\n" "\n" "Always returns True for CHECK_NONE and CHECK_CRC32."); @@ -165,7 +175,9 @@ } PyDoc_STRVAR(_lzma__encode_filter_properties__doc__, -"sig=($module, filter)\n" +"_encode_filter_properties($module, filter, /)\n" +"--\n" +"\n" "Return a bytes object encoding the options (properties) of the filter specified by *filter* (a dict).\n" "\n" "The result does not include the filter ID itself, only the options."); @@ -197,7 +209,9 @@ } PyDoc_STRVAR(_lzma__decode_filter_properties__doc__, -"sig=($module, filter_id, encoded_props)\n" +"_decode_filter_properties($module, filter_id, encoded_props, /)\n" +"--\n" +"\n" "Return a bytes object encoding the options (properties) of the filter specified by *filter* (a dict).\n" "\n" "The result does not include the filter ID itself, only the options."); @@ -228,4 +242,4 @@ return return_value; } -/*[clinic end generated code: output=fe63bc798a5c5c55 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=808fec8216ac712b input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_pickle.c.h b/Modules/clinic/_pickle.c.h --- a/Modules/clinic/_pickle.c.h +++ b/Modules/clinic/_pickle.c.h @@ -3,7 +3,9 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_Pickler_clear_memo__doc__, -"sig=($self)\n" +"clear_memo($self, /)\n" +"--\n" +"\n" "Clears the pickler\'s \"memo\".\n" "\n" "The memo is the data structure that remembers which objects the\n" @@ -24,14 +26,18 @@ } PyDoc_STRVAR(_pickle_Pickler_dump__doc__, -"sig=($self, obj)\n" +"dump($self, obj, /)\n" +"--\n" +"\n" "Write a pickled representation of the given object to the open file."); #define _PICKLE_PICKLER_DUMP_METHODDEF \ {"dump", (PyCFunction)_pickle_Pickler_dump, METH_O, _pickle_Pickler_dump__doc__}, PyDoc_STRVAR(_pickle_Pickler___init____doc__, -"sig=(file, protocol=None, fix_imports=True)\n" +"Pickler(file, protocol=None, fix_imports=True)\n" +"--\n" +"\n" "This takes a binary file for writing a pickle data stream.\n" "\n" "The optional *protocol* argument tells the pickler to use the given\n" @@ -74,7 +80,9 @@ } PyDoc_STRVAR(_pickle_PicklerMemoProxy_clear__doc__, -"sig=($self)\n" +"clear($self, /)\n" +"--\n" +"\n" "Remove all items from memo."); #define _PICKLE_PICKLERMEMOPROXY_CLEAR_METHODDEF \ @@ -90,7 +98,9 @@ } PyDoc_STRVAR(_pickle_PicklerMemoProxy_copy__doc__, -"sig=($self)\n" +"copy($self, /)\n" +"--\n" +"\n" "Copy the memo to a new object."); #define _PICKLE_PICKLERMEMOPROXY_COPY_METHODDEF \ @@ -106,7 +116,9 @@ } PyDoc_STRVAR(_pickle_PicklerMemoProxy___reduce____doc__, -"sig=($self)\n" +"__reduce__($self, /)\n" +"--\n" +"\n" "Implement pickle support."); #define _PICKLE_PICKLERMEMOPROXY___REDUCE___METHODDEF \ @@ -122,7 +134,9 @@ } PyDoc_STRVAR(_pickle_Unpickler_load__doc__, -"sig=($self)\n" +"load($self, /)\n" +"--\n" +"\n" "Load a pickle.\n" "\n" "Read a pickled object representation from the open file object given\n" @@ -142,7 +156,9 @@ } PyDoc_STRVAR(_pickle_Unpickler_find_class__doc__, -"sig=($self, module_name, global_name)\n" +"find_class($self, module_name, global_name, /)\n" +"--\n" +"\n" "Return an object from a specified module.\n" "\n" "If necessary, the module will be imported. Subclasses may override\n" @@ -176,7 +192,9 @@ } PyDoc_STRVAR(_pickle_Unpickler___init____doc__, -"sig=(file, *, fix_imports=True, encoding=\'ASCII\', errors=\'strict\')\n" +"Unpickler(file, *, fix_imports=True, encoding=\'ASCII\', errors=\'strict\')\n" +"--\n" +"\n" "This takes a binary file for reading a pickle data stream.\n" "\n" "The protocol version of the pickle is detected automatically, so no\n" @@ -222,7 +240,9 @@ } PyDoc_STRVAR(_pickle_UnpicklerMemoProxy_clear__doc__, -"sig=($self)\n" +"clear($self, /)\n" +"--\n" +"\n" "Remove all items from memo."); #define _PICKLE_UNPICKLERMEMOPROXY_CLEAR_METHODDEF \ @@ -238,7 +258,9 @@ } PyDoc_STRVAR(_pickle_UnpicklerMemoProxy_copy__doc__, -"sig=($self)\n" +"copy($self, /)\n" +"--\n" +"\n" "Copy the memo to a new object."); #define _PICKLE_UNPICKLERMEMOPROXY_COPY_METHODDEF \ @@ -254,7 +276,9 @@ } PyDoc_STRVAR(_pickle_UnpicklerMemoProxy___reduce____doc__, -"sig=($self)\n" +"__reduce__($self, /)\n" +"--\n" +"\n" "Implement pickling support."); #define _PICKLE_UNPICKLERMEMOPROXY___REDUCE___METHODDEF \ @@ -270,7 +294,9 @@ } PyDoc_STRVAR(_pickle_dump__doc__, -"sig=($module, obj, file, protocol=None, *, fix_imports=True)\n" +"dump($module, /, obj, file, protocol=None, *, fix_imports=True)\n" +"--\n" +"\n" "Write a pickled representation of obj to the open file object file.\n" "\n" "This is equivalent to ``Pickler(file, protocol).dump(obj)``, but may\n" @@ -320,7 +346,9 @@ } PyDoc_STRVAR(_pickle_dumps__doc__, -"sig=($module, obj, protocol=None, *, fix_imports=True)\n" +"dumps($module, /, obj, protocol=None, *, fix_imports=True)\n" +"--\n" +"\n" "Return the pickled representation of the object as a bytes object.\n" "\n" "The optional *protocol* argument tells the pickler to use the given\n" @@ -361,7 +389,10 @@ } PyDoc_STRVAR(_pickle_load__doc__, -"sig=($module, file, *, fix_imports=True, encoding=\'ASCII\', errors