From python-checkins at python.org Sun Jun 1 00:25:44 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 1 Jun 2014 00:25:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogcG9zdCAyLjcuNyB2?= =?utf-8?q?ersion_bump?= Message-ID: <3ggy081qs3z7PCt@mail.python.org> http://hg.python.org/cpython/rev/076705776bbe changeset: 90935:076705776bbe branch: 2.7 user: Benjamin Peterson date: Sat May 31 15:25:38 2014 -0700 summary: post 2.7.7 version bump files: Include/patchlevel.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -27,7 +27,7 @@ #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "2.7.7" +#define PY_VERSION "2.7.7+" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository). Empty -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 06:31:23 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 1 Jun 2014 06:31:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNDc3?= =?utf-8?q?=3A_Update_htest_docstring_and_remove_extraneous_differences_be?= =?utf-8?q?tween?= Message-ID: <3gh6631hD9z7f7b@mail.python.org> http://hg.python.org/cpython/rev/334b6725b2f7 changeset: 90936:334b6725b2f7 branch: 2.7 user: Terry Jan Reedy date: Sun Jun 01 00:30:28 2014 -0400 summary: Issue #21477: Update htest docstring and remove extraneous differences between 2.7 and 3.4. Original patch by Saimadhav Heblikar. files: Lib/idlelib/idle_test/htest.py | 26 ++++++++++++++------- 1 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -1,9 +1,12 @@ '''Run human tests of Idle's window, dialog, and popup widgets. -run(test): run *test*, a callable that causes a widget to be displayed. -runall(): run all tests defined in this file. +run(*tests) +Run each callable in tests after finding the matching test spec in this file. +If there are none, run an htest for each spec dict in this file after finding +the matching callable in the module named in the spec. -Let X be a global name bound to a widget callable. End the module with +In a tested module, let X be a global name bound to a widget callable. +End the module with if __name__ == '__main__': @@ -13,9 +16,9 @@ The X object must have a .__name__ attribute and a 'parent' parameter. X will often be a widget class, but a callable instance with .__name__ or a wrapper function also work. The name of wrapper functions, like -'_editor_Window', should start with '_'. +'_editor_window', should start with '_'. -This file must contain a matching instance of the folling template, +This file must contain a matching instance of the following template, with X.__name__ prepended, as in '_editor_window_spec ...'. _spec = { @@ -24,11 +27,17 @@ 'msg': "" } -file (no .py): used in runall() to import the file and get X. +file (no .py): used in run() to import the file and get X. kwds: passed to X (**kwds), after 'parent' is added, to initialize X. title: an example; used for some widgets, delete if not. msg: displayed in a master window. Hints as to how the user might test the widget. Close the window to skip or end the test. + +Modules not being tested at the moment: +PyShell.PyShellEditorWindow +Debugger.Debugger +AutoCompleteWindow.AutoCompleteWindow +OutputWindow.OutputWindow (indirectly being tested with grep test) ''' from importlib import import_module from idlelib.macosxSupport import _initializeTkVariantTests @@ -94,7 +103,7 @@ _editor_window_spec = { 'file': 'EditorWindow', 'kwds': {}, - 'msg': "Test editor functions of interest" + 'msg': "Test editor functions of interest." } GetCfgSectionNameDialog_spec = { @@ -305,7 +314,6 @@ scrollbar.pack(side='right', fill='y', expand=False) text.pack(side='left', fill='both', expand=True) - test_list = [] # List of tuples of the form (spec, callable widget) if tests: for test in tests: @@ -333,7 +341,7 @@ test_spec, callable_object[0] = test_list.pop() test_kwds[0] = test_spec['kwds'] test_kwds[0]['parent'] = root - test_name[0].set('test ' + test_spec['name']) + test_name[0].set('Test ' + test_spec['name']) text.configure(state='normal') # enable text editing text.delete('1.0','end') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 06:31:24 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 1 Jun 2014 06:31:24 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNDc3?= =?utf-8?q?=3A_Update_htest_docstring_and_remove_extraneous_differences_be?= =?utf-8?q?tween?= Message-ID: <3gh6643PCvz7f7h@mail.python.org> http://hg.python.org/cpython/rev/e56c3585ea80 changeset: 90937:e56c3585ea80 branch: 3.4 parent: 90932:5d21491733d8 user: Terry Jan Reedy date: Sun Jun 01 00:30:34 2014 -0400 summary: Issue #21477: Update htest docstring and remove extraneous differences between 2.7 and 3.4. Original patch by Saimadhav Heblikar. files: Lib/idlelib/idle_test/htest.py | 27 ++++++++++++++------- 1 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -1,9 +1,12 @@ '''Run human tests of Idle's window, dialog, and popup widgets. -run(test): run *test*, a callable that causes a widget to be displayed. -runall(): run all tests defined in this file. +run(*tests) +Run each callable in tests after finding the matching test spec in this file. +If there are none, run an htest for each spec dict in this file after finding +the matching callable in the module named in the spec. -Let X be a global name bound to a widget callable. End the module with +In a tested module, let X be a global name bound to a widget callable. +End the module with if __name__ == '__main__': @@ -13,9 +16,9 @@ The X object must have a .__name__ attribute and a 'parent' parameter. X will often be a widget class, but a callable instance with .__name__ or a wrapper function also work. The name of wrapper functions, like -'_editor_Window', should start with '_'. +'_editor_window', should start with '_'. -This file must contain a matching instance of the folling template, +This file must contain a matching instance of the following template, with X.__name__ prepended, as in '_editor_window_spec ...'. _spec = { @@ -24,11 +27,17 @@ 'msg': "" } -file (no .py): used in runall() to import the file and get X. +file (no .py): used in run() to import the file and get X. kwds: passed to X (**kwds), after 'parent' is added, to initialize X. title: an example; used for some widgets, delete if not. msg: displayed in a master window. Hints as to how the user might test the widget. Close the window to skip or end the test. + +Modules not being tested at the moment: +PyShell.PyShellEditorWindow +Debugger.Debugger +AutoCompleteWindow.AutoCompleteWindow +OutputWindow.OutputWindow (indirectly being tested with grep test) ''' from importlib import import_module from idlelib.macosxSupport import _initializeTkVariantTests @@ -41,7 +50,7 @@ }, 'msg': "Test every button. Ensure Python, TK and IDLE versions " "are correctly displayed.\n [Close] to exit.", - } + } _calltip_window_spec = { 'file': 'CallTipWindow', @@ -212,7 +221,7 @@ 'kwds': {}, 'msg': "Click the 'Replace' button.\n" "Test various replace options in the 'Replace dialog'.\n" - "Click [Close] or [X] to close 'Replace Dialog'." + "Click [Close] or [X] to close the 'Replace Dialog'." } _search_dialog_spec = { @@ -220,7 +229,7 @@ 'kwds': {}, 'msg': "Click the 'Search' button.\n" "Test various search options in the 'Search dialog'.\n" - "Click [Close] or [X] to close 'Search Dialog'." + "Click [Close] or [X] to close the 'Search Dialog'." } _scrolled_list_spec = { -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 06:31:25 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 1 Jun 2014 06:31:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gh6655D8Mz7cl2@mail.python.org> http://hg.python.org/cpython/rev/063a0f5c2dae changeset: 90938:063a0f5c2dae parent: 90933:a53ea2e23c14 parent: 90937:e56c3585ea80 user: Terry Jan Reedy date: Sun Jun 01 00:30:55 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/idle_test/htest.py | 27 ++++++++++++++------- 1 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -1,9 +1,12 @@ '''Run human tests of Idle's window, dialog, and popup widgets. -run(test): run *test*, a callable that causes a widget to be displayed. -runall(): run all tests defined in this file. +run(*tests) +Run each callable in tests after finding the matching test spec in this file. +If there are none, run an htest for each spec dict in this file after finding +the matching callable in the module named in the spec. -Let X be a global name bound to a widget callable. End the module with +In a tested module, let X be a global name bound to a widget callable. +End the module with if __name__ == '__main__': @@ -13,9 +16,9 @@ The X object must have a .__name__ attribute and a 'parent' parameter. X will often be a widget class, but a callable instance with .__name__ or a wrapper function also work. The name of wrapper functions, like -'_editor_Window', should start with '_'. +'_editor_window', should start with '_'. -This file must contain a matching instance of the folling template, +This file must contain a matching instance of the following template, with X.__name__ prepended, as in '_editor_window_spec ...'. _spec = { @@ -24,11 +27,17 @@ 'msg': "" } -file (no .py): used in runall() to import the file and get X. +file (no .py): used in run() to import the file and get X. kwds: passed to X (**kwds), after 'parent' is added, to initialize X. title: an example; used for some widgets, delete if not. msg: displayed in a master window. Hints as to how the user might test the widget. Close the window to skip or end the test. + +Modules not being tested at the moment: +PyShell.PyShellEditorWindow +Debugger.Debugger +AutoCompleteWindow.AutoCompleteWindow +OutputWindow.OutputWindow (indirectly being tested with grep test) ''' from importlib import import_module from idlelib.macosxSupport import _initializeTkVariantTests @@ -41,7 +50,7 @@ }, 'msg': "Test every button. Ensure Python, TK and IDLE versions " "are correctly displayed.\n [Close] to exit.", - } + } _calltip_window_spec = { 'file': 'CallTipWindow', @@ -212,7 +221,7 @@ 'kwds': {}, 'msg': "Click the 'Replace' button.\n" "Test various replace options in the 'Replace dialog'.\n" - "Click [Close] or [X] to close 'Replace Dialog'." + "Click [Close] or [X] to close the 'Replace Dialog'." } _search_dialog_spec = { @@ -220,7 +229,7 @@ 'kwds': {}, 'msg': "Click the 'Search' button.\n" "Test various search options in the 'Search dialog'.\n" - "Click [Close] or [X] to close 'Search Dialog'." + "Click [Close] or [X] to close the 'Search Dialog'." } _scrolled_list_spec = { -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sun Jun 1 09:09:40 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 01 Jun 2014 09:09:40 +0200 Subject: [Python-checkins] Daily reference leaks (a53ea2e23c14): sum=3 Message-ID: results for a53ea2e23c14 on branch "default" -------------------------------------------- test_functools leaked [0, 0, 3] memory blocks, sum=3 test_site leaked [-2, 0, 2] references, sum=0 test_site leaked [-2, 0, 2] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogXdRqhw', '-x'] From python-checkins at python.org Sun Jun 1 09:33:47 2014 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 1 Jun 2014 09:33:47 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Remove_the_obsolete_MAXFD_?= =?utf-8?q?constant_and_Popen=2E=5Fclose=5Ffds=28=29_method=2E?= Message-ID: <3ghB8W0M2Sz7LjY@mail.python.org> http://hg.python.org/cpython/rev/f98b0a5e5ef5 changeset: 90939:f98b0a5e5ef5 user: Gregory P. Smith date: Sun Jun 01 00:33:35 2014 -0700 summary: Remove the obsolete MAXFD constant and Popen._close_fds() method. They should have been removed years ago when removing the old pure Python implementation from the file. files: Lib/subprocess.py | 14 -------------- 1 files changed, 0 insertions(+), 14 deletions(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -469,10 +469,6 @@ __del__ = Close __str__ = __repr__ -try: - MAXFD = os.sysconf("SC_OPEN_MAX") -except: - MAXFD = 256 # This lists holds Popen instances for which the underlying process had not # exited at the time its __del__ method got called: those processes are wait()ed @@ -1335,16 +1331,6 @@ errread, errwrite) - def _close_fds(self, fds_to_keep): - start_fd = 3 - for fd in sorted(fds_to_keep): - if fd >= start_fd: - os.closerange(start_fd, fd) - start_fd = fd + 1 - if start_fd <= MAXFD: - os.closerange(start_fd, MAXFD) - - def _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 10:23:17 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 1 Jun 2014 10:23:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Added_missed_c?= =?utf-8?q?alls_of_splitlist=28=29=2E?= Message-ID: <3ghCFd2jt7z7Ljx@mail.python.org> http://hg.python.org/cpython/rev/09b784f0863d changeset: 90940:09b784f0863d branch: 2.7 parent: 90936:334b6725b2f7 user: Serhiy Storchaka date: Sun Jun 01 11:21:34 2014 +0300 summary: Added missed calls of splitlist(). files: Lib/lib-tk/test/test_ttk/test_functions.py | 2 - Lib/lib-tk/tkFont.py | 3 +- Lib/lib-tk/ttk.py | 11 ++++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Lib/lib-tk/test/test_ttk/test_functions.py b/Lib/lib-tk/test/test_ttk/test_functions.py --- a/Lib/lib-tk/test/test_ttk/test_functions.py +++ b/Lib/lib-tk/test/test_ttk/test_functions.py @@ -407,8 +407,6 @@ ('name', 'no_minus', 'value')) self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, ('something', '-children')) # no children - self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, - ('something', '-children', 'value')) # invalid children def test_val_or_dict(self): diff --git a/Lib/lib-tk/tkFont.py b/Lib/lib-tk/tkFont.py --- a/Lib/lib-tk/tkFont.py +++ b/Lib/lib-tk/tkFont.py @@ -78,7 +78,8 @@ if exists: self.delete_font = False # confirm font exists - if self.name not in root.tk.call("font", "names"): + if self.name not in root.tk.splitlist( + root.tk.call("font", "names")): raise Tkinter._tkinter.TclError, "named font %s does not already exist" % (self.name,) # if font config info supplied, apply it if font: diff --git a/Lib/lib-tk/ttk.py b/Lib/lib-tk/ttk.py --- a/Lib/lib-tk/ttk.py +++ b/Lib/lib-tk/ttk.py @@ -277,6 +277,7 @@ def _list_from_layouttuple(tk, ltuple): """Construct a list from the tuple returned by ttk::layout, this is somewhat the reverse of _format_layoutlist.""" + ltuple = tk.splitlist(ltuple) res = [] indx = 0 @@ -295,8 +296,6 @@ indx += 2 if opt == 'children': - if not tk.wantobjects(): - val = tk.splitlist(val) val = _list_from_layouttuple(tk, val) opts[opt] = val @@ -410,8 +409,8 @@ return _list_from_statespec(self.tk.splitlist( self.tk.call(self._name, "map", style, '-%s' % query_opt))) - return _dict_from_tcltuple( - self.tk.call(self._name, "map", style, *(_format_mapdict(kw)))) + return _dict_from_tcltuple(self.tk.splitlist( + self.tk.call(self._name, "map", style, *(_format_mapdict(kw))))) def lookup(self, style, option, state=None, default=None): @@ -465,8 +464,8 @@ lspec = "null" # could be any other word, but this may make sense # when calling layout(style) later - return _list_from_layouttuple(self.tk, self.tk.splitlist( - self.tk.call(self._name, "layout", style, lspec))) + return _list_from_layouttuple(self.tk, + self.tk.call(self._name, "layout", style, lspec)) def element_create(self, elementname, etype, *args, **kw): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 10:23:18 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 1 Jun 2014 10:23:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Added_missed_c?= =?utf-8?q?alls_of_splitlist=28=29=2E?= Message-ID: <3ghCFf4d5Mz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/dfe92d077570 changeset: 90941:dfe92d077570 branch: 3.4 parent: 90937:e56c3585ea80 user: Serhiy Storchaka date: Sun Jun 01 11:21:55 2014 +0300 summary: Added missed calls of splitlist(). files: Lib/tkinter/font.py | 3 +- Lib/tkinter/test/test_ttk/test_functions.py | 2 - Lib/tkinter/ttk.py | 11 ++++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py --- a/Lib/tkinter/font.py +++ b/Lib/tkinter/font.py @@ -81,7 +81,8 @@ if exists: self.delete_font = False # confirm font exists - if self.name not in root.tk.call("font", "names"): + if self.name not in root.tk.splitlist( + root.tk.call("font", "names")): raise tkinter._tkinter.TclError( "named font %s does not already exist" % (self.name,)) # if font config info supplied, apply it diff --git a/Lib/tkinter/test/test_ttk/test_functions.py b/Lib/tkinter/test/test_ttk/test_functions.py --- a/Lib/tkinter/test/test_ttk/test_functions.py +++ b/Lib/tkinter/test/test_ttk/test_functions.py @@ -407,8 +407,6 @@ ('name', 'no_minus', 'value')) self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, ('something', '-children')) # no children - self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, - ('something', '-children', 'value')) # invalid children def test_val_or_dict(self): diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -275,6 +275,7 @@ def _list_from_layouttuple(tk, ltuple): """Construct a list from the tuple returned by ttk::layout, this is somewhat the reverse of _format_layoutlist.""" + ltuple = tk.splitlist(ltuple) res = [] indx = 0 @@ -293,8 +294,6 @@ indx += 2 if opt == 'children': - if not tk.wantobjects(): - val = tk.splitlist(val) val = _list_from_layouttuple(tk, val) opts[opt] = val @@ -408,8 +407,8 @@ return _list_from_statespec(self.tk.splitlist( self.tk.call(self._name, "map", style, '-%s' % query_opt))) - return _dict_from_tcltuple( - self.tk.call(self._name, "map", style, *(_format_mapdict(kw)))) + return _dict_from_tcltuple(self.tk.splitlist( + self.tk.call(self._name, "map", style, *(_format_mapdict(kw))))) def lookup(self, style, option, state=None, default=None): @@ -463,8 +462,8 @@ lspec = "null" # could be any other word, but this may make sense # when calling layout(style) later - return _list_from_layouttuple(self.tk, self.tk.splitlist( - self.tk.call(self._name, "layout", style, lspec))) + return _list_from_layouttuple(self.tk, + self.tk.call(self._name, "layout", style, lspec)) def element_create(self, elementname, etype, *args, **kw): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 10:23:19 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 1 Jun 2014 10:23:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Added_missed_calls_of_splitlist=28=29=2E?= Message-ID: <3ghCFg6bsNz7g6d@mail.python.org> http://hg.python.org/cpython/rev/bb43815ba4f6 changeset: 90942:bb43815ba4f6 parent: 90939:f98b0a5e5ef5 parent: 90941:dfe92d077570 user: Serhiy Storchaka date: Sun Jun 01 11:22:21 2014 +0300 summary: Added missed calls of splitlist(). files: Lib/tkinter/font.py | 3 +- Lib/tkinter/test/test_ttk/test_functions.py | 2 - Lib/tkinter/ttk.py | 11 ++++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py --- a/Lib/tkinter/font.py +++ b/Lib/tkinter/font.py @@ -81,7 +81,8 @@ if exists: self.delete_font = False # confirm font exists - if self.name not in root.tk.call("font", "names"): + if self.name not in root.tk.splitlist( + root.tk.call("font", "names")): raise tkinter._tkinter.TclError( "named font %s does not already exist" % (self.name,)) # if font config info supplied, apply it diff --git a/Lib/tkinter/test/test_ttk/test_functions.py b/Lib/tkinter/test/test_ttk/test_functions.py --- a/Lib/tkinter/test/test_ttk/test_functions.py +++ b/Lib/tkinter/test/test_ttk/test_functions.py @@ -407,8 +407,6 @@ ('name', 'no_minus', 'value')) self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, ('something', '-children')) # no children - self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, - ('something', '-children', 'value')) # invalid children def test_val_or_dict(self): diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -275,6 +275,7 @@ def _list_from_layouttuple(tk, ltuple): """Construct a list from the tuple returned by ttk::layout, this is somewhat the reverse of _format_layoutlist.""" + ltuple = tk.splitlist(ltuple) res = [] indx = 0 @@ -293,8 +294,6 @@ indx += 2 if opt == 'children': - if not tk.wantobjects(): - val = tk.splitlist(val) val = _list_from_layouttuple(tk, val) opts[opt] = val @@ -408,8 +407,8 @@ return _list_from_statespec(self.tk.splitlist( self.tk.call(self._name, "map", style, '-%s' % query_opt))) - return _dict_from_tcltuple( - self.tk.call(self._name, "map", style, *(_format_mapdict(kw)))) + return _dict_from_tcltuple(self.tk.splitlist( + self.tk.call(self._name, "map", style, *(_format_mapdict(kw))))) def lookup(self, style, option, state=None, default=None): @@ -463,8 +462,8 @@ lspec = "null" # could be any other word, but this may make sense # when calling layout(style) later - return _list_from_layouttuple(self.tk, self.tk.splitlist( - self.tk.call(self._name, "layout", style, lspec))) + return _list_from_layouttuple(self.tk, + self.tk.call(self._name, "layout", style, lspec)) def element_create(self, elementname, etype, *args, **kw): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 11:39:14 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 1 Jun 2014 11:39:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNjA1?= =?utf-8?q?=3A_Added_tests_for_Tkinter_images=2E?= Message-ID: <3ghDxG2BJwz7Ll4@mail.python.org> http://hg.python.org/cpython/rev/fcbb15edb73a changeset: 90943:fcbb15edb73a branch: 2.7 parent: 90940:09b784f0863d user: Serhiy Storchaka date: Sun Jun 01 12:34:42 2014 +0300 summary: Issue #21605: Added tests for Tkinter images. files: Lib/lib-tk/test/test_tkinter/test_images.py | 339 ++++++++++ Misc/NEWS | 2 + 2 files changed, 341 insertions(+), 0 deletions(-) diff --git a/Lib/lib-tk/test/test_tkinter/test_images.py b/Lib/lib-tk/test/test_tkinter/test_images.py new file mode 100644 --- /dev/null +++ b/Lib/lib-tk/test/test_tkinter/test_images.py @@ -0,0 +1,339 @@ +import unittest +import Tkinter as tkinter +import ttk +import test.test_support as support +from test_ttk.support import requires_tcl + +support.requires('gui') + + +class MiscTest(unittest.TestCase): + + def setUp(self): + self.root = ttk.setup_master() + + def test_image_types(self): + image_types = self.root.image_types() + self.assertIsInstance(image_types, tuple) + self.assertIn('photo', image_types) + self.assertIn('bitmap', image_types) + + def test_image_names(self): + image_names = self.root.image_names() + self.assertIsInstance(image_names, tuple) + + +class BitmapImageTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.testfile = support.findfile('python.xbm', subdir='imghdrdata') + + def setUp(self): + self.root = ttk.setup_master() + + def test_create_from_file(self): + image = tkinter.BitmapImage('::img::test', master=self.root, + foreground='yellow', background='blue', + file=self.testfile) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'bitmap') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def test_create_from_data(self): + with open(self.testfile, 'rb') as f: + data = f.read() + image = tkinter.BitmapImage('::img::test', master=self.root, + foreground='yellow', background='blue', + data=data) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'bitmap') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def assertEqualStrList(self, actual, expected): + self.assertIsInstance(actual, str) + self.assertEqual(self.root.splitlist(actual), expected) + + def test_configure_data(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['data'], '-data {} {} {} {}') + with open(self.testfile, 'rb') as f: + data = f.read() + image.configure(data=data) + self.assertEqualStrList(image['data'], + ('-data', '', '', '', data)) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + self.assertEqual(image['maskdata'], '-maskdata {} {} {} {}') + image.configure(maskdata=data) + self.assertEqualStrList(image['maskdata'], + ('-maskdata', '', '', '', data)) + + def test_configure_file(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['file'], '-file {} {} {} {}') + image.configure(file=self.testfile) + self.assertEqualStrList(image['file'], + ('-file', '', '', '',self.testfile)) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + self.assertEqual(image['maskfile'], '-maskfile {} {} {} {}') + image.configure(maskfile=self.testfile) + self.assertEqualStrList(image['maskfile'], + ('-maskfile', '', '', '', self.testfile)) + + def test_configure_background(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['background'], '-background {} {} {} {}') + image.configure(background='blue') + self.assertEqual(image['background'], '-background {} {} {} blue') + + def test_configure_foreground(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['foreground'], + '-foreground {} {} #000000 #000000') + image.configure(foreground='yellow') + self.assertEqual(image['foreground'], + '-foreground {} {} #000000 yellow') + + +class PhotoImageTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.testfile = support.findfile('python.gif', subdir='imghdrdata') + + def setUp(self): + self.root = ttk.setup_master() + self.wantobjects = self.root.wantobjects() + + def create(self): + return tkinter.PhotoImage('::img::test', master=self.root, + file=self.testfile) + + def colorlist(self, *args): + if tkinter.TkVersion >= 8.6 and self.wantobjects: + return args + else: + return tkinter._join(args) + + def check_create_from_file(self, ext): + testfile = support.findfile('python.' + ext, subdir='imghdrdata') + image = tkinter.PhotoImage('::img::test', master=self.root, + file=testfile) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'photo') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image['data'], '') + self.assertEqual(image['file'], testfile) + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def check_create_from_data(self, ext): + testfile = support.findfile('python.' + ext, subdir='imghdrdata') + with open(testfile, 'rb') as f: + data = f.read() + image = tkinter.PhotoImage('::img::test', master=self.root, + data=data) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'photo') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image['data'], data) + self.assertEqual(image['file'], '') + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def test_create_from_ppm_file(self): + self.check_create_from_file('ppm') + + @unittest.skip('issue #21580') + def test_create_from_ppm_data(self): + self.check_create_from_data('ppm') + + def test_create_from_pgm_file(self): + self.check_create_from_file('pgm') + + @unittest.skip('issue #21580') + def test_create_from_pgm_data(self): + self.check_create_from_data('pgm') + + def test_create_from_gif_file(self): + self.check_create_from_file('gif') + + @unittest.skip('issue #21580') + def test_create_from_gif_data(self): + self.check_create_from_data('gif') + + @requires_tcl(8, 6) + def test_create_from_png_file(self): + self.check_create_from_file('png') + + @unittest.skip('issue #21580') + @requires_tcl(8, 6) + def test_create_from_png_data(self): + self.check_create_from_data('png') + + @unittest.skip('issue #21580') + def test_configure_data(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['data'], '') + with open(self.testfile, 'rb') as f: + data = f.read() + image.configure(data=data) + self.assertEqual(image['data'], data) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_format(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['format'], '') + image.configure(file=self.testfile, format='gif') + self.assertEqual(image['format'], ('gif',) if self.wantobjects + else 'gif') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_file(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['file'], '') + image.configure(file=self.testfile) + self.assertEqual(image['file'], self.testfile) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_gamma(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['gamma'], '1.0') + image.configure(gamma=2.0) + self.assertEqual(image['gamma'], '2.0') + + def test_configure_width_height(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['width'], '0') + self.assertEqual(image['height'], '0') + image.configure(width=20) + image.configure(height=10) + self.assertEqual(image['width'], '20') + self.assertEqual(image['height'], '10') + self.assertEqual(image.width(), 20) + self.assertEqual(image.height(), 10) + + def test_configure_palette(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['palette'], '') + image.configure(palette=256) + self.assertEqual(image['palette'], '256') + image.configure(palette='3/4/2') + self.assertEqual(image['palette'], '3/4/2') + + def test_blank(self): + image = self.create() + image.blank() + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image.get(4, 6), self.colorlist(0, 0, 0)) + + def test_copy(self): + image = self.create() + image2 = image.copy() + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image.get(4, 6), image.get(4, 6)) + + def test_subsample(self): + image = self.create() + image2 = image.subsample(2, 3) + self.assertEqual(image2.width(), 8) + self.assertEqual(image2.height(), 6) + self.assertEqual(image2.get(2, 2), image.get(4, 6)) + + image2 = image.subsample(2) + self.assertEqual(image2.width(), 8) + self.assertEqual(image2.height(), 8) + self.assertEqual(image2.get(2, 3), image.get(4, 6)) + + def test_zoom(self): + image = self.create() + image2 = image.zoom(2, 3) + self.assertEqual(image2.width(), 32) + self.assertEqual(image2.height(), 48) + self.assertEqual(image2.get(8, 18), image.get(4, 6)) + self.assertEqual(image2.get(9, 20), image.get(4, 6)) + + image2 = image.zoom(2) + self.assertEqual(image2.width(), 32) + self.assertEqual(image2.height(), 32) + self.assertEqual(image2.get(8, 12), image.get(4, 6)) + self.assertEqual(image2.get(9, 13), image.get(4, 6)) + + def test_put(self): + image = self.create() + image.put('{red green} {blue yellow}', to=(4, 6)) + self.assertEqual(image.get(4, 6), self.colorlist(255, 0, 0)) + self.assertEqual(image.get(5, 6), + self.colorlist(0, 128 if tkinter.TkVersion >= 8.6 + else 255, 0)) + self.assertEqual(image.get(4, 7), self.colorlist(0, 0, 255)) + self.assertEqual(image.get(5, 7), self.colorlist(255, 255, 0)) + + image.put((('#f00', '#00ff00'), ('#000000fff', '#ffffffff0000'))) + self.assertEqual(image.get(0, 0), self.colorlist(255, 0, 0)) + self.assertEqual(image.get(1, 0), self.colorlist(0, 255, 0)) + self.assertEqual(image.get(0, 1), self.colorlist(0, 0, 255)) + self.assertEqual(image.get(1, 1), self.colorlist(255, 255, 0)) + + def test_get(self): + image = self.create() + self.assertEqual(image.get(4, 6), self.colorlist(62, 116, 162)) + self.assertEqual(image.get(0, 0), self.colorlist(0, 0, 0)) + self.assertEqual(image.get(15, 15), self.colorlist(0, 0, 0)) + self.assertRaises(tkinter.TclError, image.get, -1, 0) + self.assertRaises(tkinter.TclError, image.get, 0, -1) + self.assertRaises(tkinter.TclError, image.get, 16, 15) + self.assertRaises(tkinter.TclError, image.get, 15, 16) + + def test_write(self): + image = self.create() + self.addCleanup(support.unlink, support.TESTFN) + + image.write(support.TESTFN) + image2 = tkinter.PhotoImage('::img::test2', master=self.root, + format='ppm', + file=support.TESTFN) + self.assertEqual(str(image2), '::img::test2') + self.assertEqual(image2.type(), 'photo') + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(0, 0), image.get(0, 0)) + self.assertEqual(image2.get(15, 8), image.get(15, 8)) + + image.write(support.TESTFN, format='gif', from_coords=(4, 6, 6, 9)) + image3 = tkinter.PhotoImage('::img::test3', master=self.root, + format='gif', + file=support.TESTFN) + self.assertEqual(str(image3), '::img::test3') + self.assertEqual(image3.type(), 'photo') + self.assertEqual(image3.width(), 2) + self.assertEqual(image3.height(), 3) + self.assertEqual(image3.get(0, 0), image.get(4, 6)) + self.assertEqual(image3.get(1, 2), image.get(5, 8)) + + +tests_gui = (MiscTest, BitmapImageTest, PhotoImageTest,) + +if __name__ == "__main__": + support.run_unittest(*tests_gui) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -43,6 +43,8 @@ Tests ----- +- Issue #21605: Added tests for Tkinter images. + - Issue #21493: Added test for ntpath.expanduser(). Original patch by Claudiu Popa. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 11:39:15 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 1 Jun 2014 11:39:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjA1?= =?utf-8?q?=3A_Added_tests_for_Tkinter_images=2E?= Message-ID: <3ghDxH4sNPz7Ll4@mail.python.org> http://hg.python.org/cpython/rev/6c8b2ab55976 changeset: 90944:6c8b2ab55976 branch: 3.4 parent: 90941:dfe92d077570 user: Serhiy Storchaka date: Sun Jun 01 12:34:56 2014 +0300 summary: Issue #21605: Added tests for Tkinter images. files: Lib/tkinter/test/test_tkinter/test_images.py | 341 ++++++++++ Misc/NEWS | 2 + 2 files changed, 343 insertions(+), 0 deletions(-) diff --git a/Lib/tkinter/test/test_tkinter/test_images.py b/Lib/tkinter/test/test_tkinter/test_images.py new file mode 100644 --- /dev/null +++ b/Lib/tkinter/test/test_tkinter/test_images.py @@ -0,0 +1,341 @@ +import unittest +import tkinter +from tkinter import ttk +from test import support +from tkinter.test.support import requires_tcl + +support.requires('gui') + + +class MiscTest(unittest.TestCase): + + def setUp(self): + self.root = ttk.setup_master() + + def test_image_types(self): + image_types = self.root.image_types() + self.assertIsInstance(image_types, tuple) + self.assertIn('photo', image_types) + self.assertIn('bitmap', image_types) + + def test_image_names(self): + image_names = self.root.image_names() + self.assertIsInstance(image_names, tuple) + + +class BitmapImageTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.testfile = support.findfile('python.xbm', subdir='imghdrdata') + + def setUp(self): + self.root = ttk.setup_master() + + def test_create_from_file(self): + image = tkinter.BitmapImage('::img::test', master=self.root, + foreground='yellow', background='blue', + file=self.testfile) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'bitmap') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def test_create_from_data(self): + with open(self.testfile, 'rb') as f: + data = f.read() + image = tkinter.BitmapImage('::img::test', master=self.root, + foreground='yellow', background='blue', + data=data) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'bitmap') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def assertEqualStrList(self, actual, expected): + self.assertIsInstance(actual, str) + self.assertEqual(self.root.splitlist(actual), expected) + + def test_configure_data(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['data'], '-data {} {} {} {}') + with open(self.testfile, 'rb') as f: + data = f.read() + image.configure(data=data) + self.assertEqualStrList(image['data'], + ('-data', '', '', '', data.decode('ascii'))) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + self.assertEqual(image['maskdata'], '-maskdata {} {} {} {}') + image.configure(maskdata=data) + self.assertEqualStrList(image['maskdata'], + ('-maskdata', '', '', '', data.decode('ascii'))) + + def test_configure_file(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['file'], '-file {} {} {} {}') + image.configure(file=self.testfile) + self.assertEqualStrList(image['file'], + ('-file', '', '', '',self.testfile)) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + self.assertEqual(image['maskfile'], '-maskfile {} {} {} {}') + image.configure(maskfile=self.testfile) + self.assertEqualStrList(image['maskfile'], + ('-maskfile', '', '', '', self.testfile)) + + def test_configure_background(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['background'], '-background {} {} {} {}') + image.configure(background='blue') + self.assertEqual(image['background'], '-background {} {} {} blue') + + def test_configure_foreground(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['foreground'], + '-foreground {} {} #000000 #000000') + image.configure(foreground='yellow') + self.assertEqual(image['foreground'], + '-foreground {} {} #000000 yellow') + + +class PhotoImageTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.testfile = support.findfile('python.gif', subdir='imghdrdata') + + def setUp(self): + self.root = ttk.setup_master() + self.wantobjects = self.root.wantobjects() + + def create(self): + return tkinter.PhotoImage('::img::test', master=self.root, + file=self.testfile) + + def colorlist(self, *args): + if tkinter.TkVersion >= 8.6 and self.wantobjects: + return args + else: + return tkinter._join(args) + + def check_create_from_file(self, ext): + testfile = support.findfile('python.' + ext, subdir='imghdrdata') + image = tkinter.PhotoImage('::img::test', master=self.root, + file=testfile) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'photo') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image['data'], '') + self.assertEqual(image['file'], testfile) + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def check_create_from_data(self, ext): + testfile = support.findfile('python.' + ext, subdir='imghdrdata') + with open(testfile, 'rb') as f: + data = f.read() + image = tkinter.PhotoImage('::img::test', master=self.root, + data=data) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'photo') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image['data'], data if self.wantobjects + else data.decode('latin1')) + self.assertEqual(image['file'], '') + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def test_create_from_ppm_file(self): + self.check_create_from_file('ppm') + + @unittest.skip('issue #21580') + def test_create_from_ppm_data(self): + self.check_create_from_data('ppm') + + def test_create_from_pgm_file(self): + self.check_create_from_file('pgm') + + @unittest.skip('issue #21580') + def test_create_from_pgm_data(self): + self.check_create_from_data('pgm') + + def test_create_from_gif_file(self): + self.check_create_from_file('gif') + + @unittest.skip('issue #21580') + def test_create_from_gif_data(self): + self.check_create_from_data('gif') + + @requires_tcl(8, 6) + def test_create_from_png_file(self): + self.check_create_from_file('png') + + @unittest.skip('issue #21580') + @requires_tcl(8, 6) + def test_create_from_png_data(self): + self.check_create_from_data('png') + + @unittest.skip('issue #21580') + def test_configure_data(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['data'], '') + with open(self.testfile, 'rb') as f: + data = f.read() + image.configure(data=data) + self.assertEqual(image['data'], data if self.wantobjects + else data.decode('latin1')) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_format(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['format'], '') + image.configure(file=self.testfile, format='gif') + self.assertEqual(image['format'], ('gif',) if self.wantobjects + else 'gif') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_file(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['file'], '') + image.configure(file=self.testfile) + self.assertEqual(image['file'], self.testfile) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_gamma(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['gamma'], '1.0') + image.configure(gamma=2.0) + self.assertEqual(image['gamma'], '2.0') + + def test_configure_width_height(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['width'], '0') + self.assertEqual(image['height'], '0') + image.configure(width=20) + image.configure(height=10) + self.assertEqual(image['width'], '20') + self.assertEqual(image['height'], '10') + self.assertEqual(image.width(), 20) + self.assertEqual(image.height(), 10) + + def test_configure_palette(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['palette'], '') + image.configure(palette=256) + self.assertEqual(image['palette'], '256') + image.configure(palette='3/4/2') + self.assertEqual(image['palette'], '3/4/2') + + def test_blank(self): + image = self.create() + image.blank() + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image.get(4, 6), self.colorlist(0, 0, 0)) + + def test_copy(self): + image = self.create() + image2 = image.copy() + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image.get(4, 6), image.get(4, 6)) + + def test_subsample(self): + image = self.create() + image2 = image.subsample(2, 3) + self.assertEqual(image2.width(), 8) + self.assertEqual(image2.height(), 6) + self.assertEqual(image2.get(2, 2), image.get(4, 6)) + + image2 = image.subsample(2) + self.assertEqual(image2.width(), 8) + self.assertEqual(image2.height(), 8) + self.assertEqual(image2.get(2, 3), image.get(4, 6)) + + def test_zoom(self): + image = self.create() + image2 = image.zoom(2, 3) + self.assertEqual(image2.width(), 32) + self.assertEqual(image2.height(), 48) + self.assertEqual(image2.get(8, 18), image.get(4, 6)) + self.assertEqual(image2.get(9, 20), image.get(4, 6)) + + image2 = image.zoom(2) + self.assertEqual(image2.width(), 32) + self.assertEqual(image2.height(), 32) + self.assertEqual(image2.get(8, 12), image.get(4, 6)) + self.assertEqual(image2.get(9, 13), image.get(4, 6)) + + def test_put(self): + image = self.create() + image.put('{red green} {blue yellow}', to=(4, 6)) + self.assertEqual(image.get(4, 6), self.colorlist(255, 0, 0)) + self.assertEqual(image.get(5, 6), + self.colorlist(0, 128 if tkinter.TkVersion >= 8.6 + else 255, 0)) + self.assertEqual(image.get(4, 7), self.colorlist(0, 0, 255)) + self.assertEqual(image.get(5, 7), self.colorlist(255, 255, 0)) + + image.put((('#f00', '#00ff00'), ('#000000fff', '#ffffffff0000'))) + self.assertEqual(image.get(0, 0), self.colorlist(255, 0, 0)) + self.assertEqual(image.get(1, 0), self.colorlist(0, 255, 0)) + self.assertEqual(image.get(0, 1), self.colorlist(0, 0, 255)) + self.assertEqual(image.get(1, 1), self.colorlist(255, 255, 0)) + + def test_get(self): + image = self.create() + self.assertEqual(image.get(4, 6), self.colorlist(62, 116, 162)) + self.assertEqual(image.get(0, 0), self.colorlist(0, 0, 0)) + self.assertEqual(image.get(15, 15), self.colorlist(0, 0, 0)) + self.assertRaises(tkinter.TclError, image.get, -1, 0) + self.assertRaises(tkinter.TclError, image.get, 0, -1) + self.assertRaises(tkinter.TclError, image.get, 16, 15) + self.assertRaises(tkinter.TclError, image.get, 15, 16) + + def test_write(self): + image = self.create() + self.addCleanup(support.unlink, support.TESTFN) + + image.write(support.TESTFN) + image2 = tkinter.PhotoImage('::img::test2', master=self.root, + format='ppm', + file=support.TESTFN) + self.assertEqual(str(image2), '::img::test2') + self.assertEqual(image2.type(), 'photo') + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(0, 0), image.get(0, 0)) + self.assertEqual(image2.get(15, 8), image.get(15, 8)) + + image.write(support.TESTFN, format='gif', from_coords=(4, 6, 6, 9)) + image3 = tkinter.PhotoImage('::img::test3', master=self.root, + format='gif', + file=support.TESTFN) + self.assertEqual(str(image3), '::img::test3') + self.assertEqual(image3.type(), 'photo') + self.assertEqual(image3.width(), 2) + self.assertEqual(image3.height(), 3) + self.assertEqual(image3.get(0, 0), image.get(4, 6)) + self.assertEqual(image3.get(1, 2), image.get(5, 8)) + + +tests_gui = (MiscTest, BitmapImageTest, PhotoImageTest,) + +if __name__ == "__main__": + support.run_unittest(*tests_gui) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -56,6 +56,8 @@ Tests ----- +- Issue #21605: Added tests for Tkinter images. + - Issue #21493: Added test for ntpath.expanduser(). Original patch by Claudiu Popa. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 11:39:17 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 1 Jun 2014 11:39:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321605=3A_Added_tests_for_Tkinter_images=2E?= Message-ID: <3ghDxK0LrBz7g70@mail.python.org> http://hg.python.org/cpython/rev/13254db884e9 changeset: 90945:13254db884e9 parent: 90942:bb43815ba4f6 parent: 90944:6c8b2ab55976 user: Serhiy Storchaka date: Sun Jun 01 12:36:39 2014 +0300 summary: Issue #21605: Added tests for Tkinter images. files: Lib/tkinter/test/test_tkinter/test_images.py | 341 ++++++++++ Misc/NEWS | 2 + 2 files changed, 343 insertions(+), 0 deletions(-) diff --git a/Lib/tkinter/test/test_tkinter/test_images.py b/Lib/tkinter/test/test_tkinter/test_images.py new file mode 100644 --- /dev/null +++ b/Lib/tkinter/test/test_tkinter/test_images.py @@ -0,0 +1,341 @@ +import unittest +import tkinter +from tkinter import ttk +from test import support +from tkinter.test.support import requires_tcl + +support.requires('gui') + + +class MiscTest(unittest.TestCase): + + def setUp(self): + self.root = ttk.setup_master() + + def test_image_types(self): + image_types = self.root.image_types() + self.assertIsInstance(image_types, tuple) + self.assertIn('photo', image_types) + self.assertIn('bitmap', image_types) + + def test_image_names(self): + image_names = self.root.image_names() + self.assertIsInstance(image_names, tuple) + + +class BitmapImageTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.testfile = support.findfile('python.xbm', subdir='imghdrdata') + + def setUp(self): + self.root = ttk.setup_master() + + def test_create_from_file(self): + image = tkinter.BitmapImage('::img::test', master=self.root, + foreground='yellow', background='blue', + file=self.testfile) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'bitmap') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def test_create_from_data(self): + with open(self.testfile, 'rb') as f: + data = f.read() + image = tkinter.BitmapImage('::img::test', master=self.root, + foreground='yellow', background='blue', + data=data) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'bitmap') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def assertEqualStrList(self, actual, expected): + self.assertIsInstance(actual, str) + self.assertEqual(self.root.splitlist(actual), expected) + + def test_configure_data(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['data'], '-data {} {} {} {}') + with open(self.testfile, 'rb') as f: + data = f.read() + image.configure(data=data) + self.assertEqualStrList(image['data'], + ('-data', '', '', '', data.decode('ascii'))) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + self.assertEqual(image['maskdata'], '-maskdata {} {} {} {}') + image.configure(maskdata=data) + self.assertEqualStrList(image['maskdata'], + ('-maskdata', '', '', '', data.decode('ascii'))) + + def test_configure_file(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['file'], '-file {} {} {} {}') + image.configure(file=self.testfile) + self.assertEqualStrList(image['file'], + ('-file', '', '', '',self.testfile)) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + self.assertEqual(image['maskfile'], '-maskfile {} {} {} {}') + image.configure(maskfile=self.testfile) + self.assertEqualStrList(image['maskfile'], + ('-maskfile', '', '', '', self.testfile)) + + def test_configure_background(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['background'], '-background {} {} {} {}') + image.configure(background='blue') + self.assertEqual(image['background'], '-background {} {} {} blue') + + def test_configure_foreground(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['foreground'], + '-foreground {} {} #000000 #000000') + image.configure(foreground='yellow') + self.assertEqual(image['foreground'], + '-foreground {} {} #000000 yellow') + + +class PhotoImageTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.testfile = support.findfile('python.gif', subdir='imghdrdata') + + def setUp(self): + self.root = ttk.setup_master() + self.wantobjects = self.root.wantobjects() + + def create(self): + return tkinter.PhotoImage('::img::test', master=self.root, + file=self.testfile) + + def colorlist(self, *args): + if tkinter.TkVersion >= 8.6 and self.wantobjects: + return args + else: + return tkinter._join(args) + + def check_create_from_file(self, ext): + testfile = support.findfile('python.' + ext, subdir='imghdrdata') + image = tkinter.PhotoImage('::img::test', master=self.root, + file=testfile) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'photo') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image['data'], '') + self.assertEqual(image['file'], testfile) + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def check_create_from_data(self, ext): + testfile = support.findfile('python.' + ext, subdir='imghdrdata') + with open(testfile, 'rb') as f: + data = f.read() + image = tkinter.PhotoImage('::img::test', master=self.root, + data=data) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'photo') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image['data'], data if self.wantobjects + else data.decode('latin1')) + self.assertEqual(image['file'], '') + self.assertIn('::img::test', self.root.image_names()) + del image + self.assertNotIn('::img::test', self.root.image_names()) + + def test_create_from_ppm_file(self): + self.check_create_from_file('ppm') + + @unittest.skip('issue #21580') + def test_create_from_ppm_data(self): + self.check_create_from_data('ppm') + + def test_create_from_pgm_file(self): + self.check_create_from_file('pgm') + + @unittest.skip('issue #21580') + def test_create_from_pgm_data(self): + self.check_create_from_data('pgm') + + def test_create_from_gif_file(self): + self.check_create_from_file('gif') + + @unittest.skip('issue #21580') + def test_create_from_gif_data(self): + self.check_create_from_data('gif') + + @requires_tcl(8, 6) + def test_create_from_png_file(self): + self.check_create_from_file('png') + + @unittest.skip('issue #21580') + @requires_tcl(8, 6) + def test_create_from_png_data(self): + self.check_create_from_data('png') + + @unittest.skip('issue #21580') + def test_configure_data(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['data'], '') + with open(self.testfile, 'rb') as f: + data = f.read() + image.configure(data=data) + self.assertEqual(image['data'], data if self.wantobjects + else data.decode('latin1')) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_format(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['format'], '') + image.configure(file=self.testfile, format='gif') + self.assertEqual(image['format'], ('gif',) if self.wantobjects + else 'gif') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_file(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['file'], '') + image.configure(file=self.testfile) + self.assertEqual(image['file'], self.testfile) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_gamma(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['gamma'], '1.0') + image.configure(gamma=2.0) + self.assertEqual(image['gamma'], '2.0') + + def test_configure_width_height(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['width'], '0') + self.assertEqual(image['height'], '0') + image.configure(width=20) + image.configure(height=10) + self.assertEqual(image['width'], '20') + self.assertEqual(image['height'], '10') + self.assertEqual(image.width(), 20) + self.assertEqual(image.height(), 10) + + def test_configure_palette(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['palette'], '') + image.configure(palette=256) + self.assertEqual(image['palette'], '256') + image.configure(palette='3/4/2') + self.assertEqual(image['palette'], '3/4/2') + + def test_blank(self): + image = self.create() + image.blank() + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image.get(4, 6), self.colorlist(0, 0, 0)) + + def test_copy(self): + image = self.create() + image2 = image.copy() + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image.get(4, 6), image.get(4, 6)) + + def test_subsample(self): + image = self.create() + image2 = image.subsample(2, 3) + self.assertEqual(image2.width(), 8) + self.assertEqual(image2.height(), 6) + self.assertEqual(image2.get(2, 2), image.get(4, 6)) + + image2 = image.subsample(2) + self.assertEqual(image2.width(), 8) + self.assertEqual(image2.height(), 8) + self.assertEqual(image2.get(2, 3), image.get(4, 6)) + + def test_zoom(self): + image = self.create() + image2 = image.zoom(2, 3) + self.assertEqual(image2.width(), 32) + self.assertEqual(image2.height(), 48) + self.assertEqual(image2.get(8, 18), image.get(4, 6)) + self.assertEqual(image2.get(9, 20), image.get(4, 6)) + + image2 = image.zoom(2) + self.assertEqual(image2.width(), 32) + self.assertEqual(image2.height(), 32) + self.assertEqual(image2.get(8, 12), image.get(4, 6)) + self.assertEqual(image2.get(9, 13), image.get(4, 6)) + + def test_put(self): + image = self.create() + image.put('{red green} {blue yellow}', to=(4, 6)) + self.assertEqual(image.get(4, 6), self.colorlist(255, 0, 0)) + self.assertEqual(image.get(5, 6), + self.colorlist(0, 128 if tkinter.TkVersion >= 8.6 + else 255, 0)) + self.assertEqual(image.get(4, 7), self.colorlist(0, 0, 255)) + self.assertEqual(image.get(5, 7), self.colorlist(255, 255, 0)) + + image.put((('#f00', '#00ff00'), ('#000000fff', '#ffffffff0000'))) + self.assertEqual(image.get(0, 0), self.colorlist(255, 0, 0)) + self.assertEqual(image.get(1, 0), self.colorlist(0, 255, 0)) + self.assertEqual(image.get(0, 1), self.colorlist(0, 0, 255)) + self.assertEqual(image.get(1, 1), self.colorlist(255, 255, 0)) + + def test_get(self): + image = self.create() + self.assertEqual(image.get(4, 6), self.colorlist(62, 116, 162)) + self.assertEqual(image.get(0, 0), self.colorlist(0, 0, 0)) + self.assertEqual(image.get(15, 15), self.colorlist(0, 0, 0)) + self.assertRaises(tkinter.TclError, image.get, -1, 0) + self.assertRaises(tkinter.TclError, image.get, 0, -1) + self.assertRaises(tkinter.TclError, image.get, 16, 15) + self.assertRaises(tkinter.TclError, image.get, 15, 16) + + def test_write(self): + image = self.create() + self.addCleanup(support.unlink, support.TESTFN) + + image.write(support.TESTFN) + image2 = tkinter.PhotoImage('::img::test2', master=self.root, + format='ppm', + file=support.TESTFN) + self.assertEqual(str(image2), '::img::test2') + self.assertEqual(image2.type(), 'photo') + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(0, 0), image.get(0, 0)) + self.assertEqual(image2.get(15, 8), image.get(15, 8)) + + image.write(support.TESTFN, format='gif', from_coords=(4, 6, 6, 9)) + image3 = tkinter.PhotoImage('::img::test3', master=self.root, + format='gif', + file=support.TESTFN) + self.assertEqual(str(image3), '::img::test3') + self.assertEqual(image3.type(), 'photo') + self.assertEqual(image3.width(), 2) + self.assertEqual(image3.height(), 3) + self.assertEqual(image3.get(0, 0), image.get(4, 6)) + self.assertEqual(image3.get(1, 2), image.get(5, 8)) + + +tests_gui = (MiscTest, BitmapImageTest, PhotoImageTest,) + +if __name__ == "__main__": + support.run_unittest(*tests_gui) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -525,6 +525,8 @@ Tests ----- +- Issue #21605: Added tests for Tkinter images. + - Issue #21493: Added test for ntpath.expanduser(). Original patch by Claudiu Popa. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 21:14:00 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 1 Jun 2014 21:14:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5NjU2?= =?utf-8?q?=3A_Running_Python_with_the_-3_option_now_also_warns_about?= Message-ID: <3ghThS4Fxrz7LjV@mail.python.org> http://hg.python.org/cpython/rev/6bd21268876e changeset: 90946:6bd21268876e branch: 2.7 parent: 90943:fcbb15edb73a user: Serhiy Storchaka date: Sun Jun 01 22:13:39 2014 +0300 summary: Issue #19656: Running Python with the -3 option now also warns about non-ascii bytes literals. files: Lib/test/test_py3kwarn.py | 5 +++++ Misc/NEWS | 3 +++ Python/ast.c | 22 +++++++++++++++++----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_py3kwarn.py b/Lib/test/test_py3kwarn.py --- a/Lib/test/test_py3kwarn.py +++ b/Lib/test/test_py3kwarn.py @@ -307,6 +307,11 @@ w.reset() self.assertWarning(sequenceIncludes(range(3), 2), w, seq_warn) + def test_nonascii_bytes_literals(self): + expected = "non-ascii bytes literals not supported in 3.x" + with check_py3k_warnings((expected, SyntaxWarning)): + exec "b'\xbd'" + class TestStdlibRemovals(unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #19656: Running Python with the -3 option now also warns about + non-ascii bytes literals. + - Issue #21523: Fix over-pessimistic computation of the stack effect of some opcodes in the compiler. This also fixes a quadratic compilation time issue noticeable when compiling code with a large number of "and" diff --git a/Python/ast.c b/Python/ast.c --- a/Python/ast.c +++ b/Python/ast.c @@ -37,7 +37,7 @@ static expr_ty ast_for_call(struct compiling *, const node *, expr_ty); static PyObject *parsenumber(struct compiling *, const char *); -static PyObject *parsestr(struct compiling *, const char *); +static PyObject *parsestr(struct compiling *, const node *n, const char *); static PyObject *parsestrplus(struct compiling *, const node *n); #ifndef LINENO @@ -3444,13 +3444,14 @@ * parsestr parses it, and returns the decoded Python string object. */ static PyObject * -parsestr(struct compiling *c, const char *s) +parsestr(struct compiling *c, const node *n, const char *s) { - size_t len; + size_t len, i; int quote = Py_CHARMASK(*s); int rawmode = 0; int need_encoding; int unicode = c->c_future_unicode; + int bytes = 0; if (isalpha(quote) || quote == '_') { if (quote == 'u' || quote == 'U') { @@ -3460,6 +3461,7 @@ if (quote == 'b' || quote == 'B') { quote = *++s; unicode = 0; + bytes = 1; } if (quote == 'r' || quote == 'R') { quote = *++s; @@ -3489,6 +3491,16 @@ return NULL; } } + if (Py_Py3kWarningFlag && bytes) { + for (i = 0; i < len; i++) { + if ((unsigned char)s[i] > 127) { + if (!ast_warn(c, n, + "non-ascii bytes literals not supported in 3.x")) + return NULL; + break; + } + } + } #ifdef Py_USING_UNICODE if (unicode || Py_UnicodeFlag) { return decode_unicode(c, s, len, rawmode, c->c_encoding); @@ -3531,11 +3543,11 @@ PyObject *v; int i; REQ(CHILD(n, 0), STRING); - if ((v = parsestr(c, STR(CHILD(n, 0)))) != NULL) { + if ((v = parsestr(c, n, STR(CHILD(n, 0)))) != NULL) { /* String literal concatenation */ for (i = 1; i < NCH(n); i++) { PyObject *s; - s = parsestr(c, STR(CHILD(n, i))); + s = parsestr(c, n, STR(CHILD(n, i))); if (s == NULL) goto onError; if (PyString_Check(v) && PyString_Check(s)) { -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 22:22:25 2014 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 1 Jun 2014 22:22:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogRG9uJ3QgcmVzdHJp?= =?utf-8?q?ct_ourselves_to_a_=22max=22_fd_when_closing_fds_before_exec=28?= =?utf-8?q?=29?= Message-ID: <3ghWCP64YQz7LjX@mail.python.org> http://hg.python.org/cpython/rev/5453b9c59cd7 changeset: 90947:5453b9c59cd7 branch: 3.4 parent: 90944:6c8b2ab55976 user: Gregory P. Smith date: Sun Jun 01 13:18:28 2014 -0700 summary: Don't restrict ourselves to a "max" fd when closing fds before exec() when we have a way to get an actual list of all open fds from the OS. Fixes issue #21618: The subprocess module would ignore fds that were inherited by the calling process and already higher than POSIX resource limits would otherwise allow. On systems with a functioning /proc/self/fd or /dev/fd interface the max is now ignored and all fds are closed. files: Lib/test/test_subprocess.py | 53 +++++++++++++++ Misc/NEWS | 5 + Modules/_posixsubprocess.c | 87 ++++++++++++------------ 3 files changed, 102 insertions(+), 43 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1926,6 +1926,59 @@ "Some fds not in pass_fds were left open") self.assertIn(1, remaining_fds, "Subprocess failed") + + def test_close_fds_when_max_fd_is_lowered(self): + """Confirm that issue21618 is fixed (may fail under valgrind).""" + fd_status = support.findfile("fd_status.py", subdir="subprocessdata") + + open_fds = set() + # Add a bunch more fds to pass down. + for _ in range(10): + fd = os.open("/dev/null", os.O_RDONLY) + open_fds.add(fd) + + # Leave a two pairs of low ones available for use by the + # internal child error pipe and the stdout pipe. + for fd in sorted(open_fds)[:4]: + os.close(fd) + open_fds.remove(fd) + + for fd in open_fds: + self.addCleanup(os.close, fd) + os.set_inheritable(fd, True) + + max_fd_open = max(open_fds) + + import resource + rlim_cur, rlim_max = resource.getrlimit(resource.RLIMIT_NOFILE) + try: + # 9 is lower than the highest fds we are leaving open. + resource.setrlimit(resource.RLIMIT_NOFILE, (9, rlim_max)) + # Launch a new Python interpreter with our low fd rlim_cur that + # inherits open fds above that limit. It then uses subprocess + # with close_fds=True to get a report of open fds in the child. + # An explicit list of fds to check is passed to fd_status.py as + # letting fd_status rely on its default logic would miss the + # fds above rlim_cur as it normally only checks up to that limit. + p = subprocess.Popen( + [sys.executable, '-c', + textwrap.dedent(""" + import subprocess, sys + subprocess.Popen([sys.executable, {fd_status!r}] + + [str(x) for x in range({max_fd})], + close_fds=True) + """.format(fd_status=fd_status, max_fd=max_fd_open+1))], + stdout=subprocess.PIPE, close_fds=False) + finally: + resource.setrlimit(resource.RLIMIT_NOFILE, (rlim_cur, rlim_max)) + + output, unused_stderr = p.communicate() + remaining_fds = set(map(int, output.strip().split(b','))) + + self.assertFalse(remaining_fds & open_fds, + msg="Some fds were left open.") + + # Mac OS X Tiger (10.4) has a kernel bug: sometimes, the file # descriptor of a pipe closed in the parent process is valid in the # child process according to fstat(), but the mode of the file diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,11 @@ Library ------- +- Issue #21618: The subprocess module could fail to close open fds that were + inherited by the calling process and already higher than POSIX resource + limits would otherwise allow. On systems with a functioning /proc/self/fd + or /dev/fd interface the max is now ignored and all fds are closed. + - Issue #21552: Fixed possible integer overflow of too long string lengths in the tkinter module on 64-bit platforms. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -44,10 +44,6 @@ #define POSIX_CALL(call) do { if ((call) == -1) goto error; } while (0) -/* Maximum file descriptor, initialized on module load. */ -static long max_fd; - - /* Given the gc module call gc.enable() and return 0 on success. */ static int _enable_gc(PyObject *gc_module) @@ -166,14 +162,39 @@ } -/* Close all file descriptors in the range start_fd inclusive to - * end_fd exclusive except for those in py_fds_to_keep. If the - * range defined by [start_fd, end_fd) is large this will take a - * long time as it calls close() on EVERY possible fd. +/* Get the maximum file descriptor that could be opened by this process. + * This function is async signal safe for use between fork() and exec(). + */ +static long +safe_get_max_fd(void) +{ + long local_max_fd; +#if defined(__NetBSD__) + local_max_fd = fcntl(0, F_MAXFD); + if (local_max_fd >= 0) + return local_max_fd; +#endif +#ifdef _SC_OPEN_MAX + local_max_fd = sysconf(_SC_OPEN_MAX); + if (local_max_fd == -1) +#endif + local_max_fd = 256; /* Matches legacy Lib/subprocess.py behavior. */ + return local_max_fd; +} + + +/* Close all file descriptors in the range from start_fd and higher + * except for those in py_fds_to_keep. If the range defined by + * [start_fd, safe_get_max_fd()) is large this will take a long + * time as it calls close() on EVERY possible fd. + * + * It isn't possible to know for sure what the max fd to go up to + * is for processes with the capability of raising their maximum. */ static void -_close_fds_by_brute_force(int start_fd, int end_fd, PyObject *py_fds_to_keep) +_close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep) { + long end_fd = safe_get_max_fd(); Py_ssize_t num_fds_to_keep = PySequence_Length(py_fds_to_keep); Py_ssize_t keep_seq_idx; int fd_num; @@ -229,16 +250,14 @@ * it with some cpp #define magic to work on other OSes as well if you want. */ static void -_close_open_fd_range_safe(int start_fd, int end_fd, PyObject* py_fds_to_keep) +_close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep) { int fd_dir_fd; - if (start_fd >= end_fd) - return; fd_dir_fd = _Py_open(FD_DIR, O_RDONLY); if (fd_dir_fd == -1) { /* No way to get a list of open fds. */ - _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); + _close_fds_by_brute_force(start_fd, py_fds_to_keep); return; } else { char buffer[sizeof(struct linux_dirent64)]; @@ -253,23 +272,23 @@ entry = (struct linux_dirent64 *)(buffer + offset); if ((fd = _pos_int_from_ascii(entry->d_name)) < 0) continue; /* Not a number. */ - if (fd != fd_dir_fd && fd >= start_fd && fd < end_fd && + if (fd != fd_dir_fd && fd >= start_fd && !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep)) { while (close(fd) < 0 && errno == EINTR); } } } - close(fd_dir_fd); + while (close(fd_dir_fd) < 0 && errno == EINTR); } } -#define _close_open_fd_range _close_open_fd_range_safe +#define _close_open_fds _close_open_fds_safe #else /* NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */ -/* Close all open file descriptors in the range start_fd inclusive to end_fd - * exclusive. Do not close any in the sorted py_fds_to_keep list. +/* Close all open file descriptors from start_fd and higher. + * Do not close any in the sorted py_fds_to_keep list. * * This function violates the strict use of async signal safe functions. :( * It calls opendir(), readdir() and closedir(). Of these, the one most @@ -282,17 +301,13 @@ * http://womble.decadent.org.uk/readdir_r-advisory.html */ static void -_close_open_fd_range_maybe_unsafe(int start_fd, int end_fd, - PyObject* py_fds_to_keep) +_close_open_fds_maybe_unsafe(long start_fd, PyObject* py_fds_to_keep) { DIR *proc_fd_dir; #ifndef HAVE_DIRFD - while (_is_fd_in_sorted_fd_sequence(start_fd, py_fds_to_keep) && - (start_fd < end_fd)) { + while (_is_fd_in_sorted_fd_sequence(start_fd, py_fds_to_keep)) { ++start_fd; } - if (start_fd >= end_fd) - return; /* Close our lowest fd before we call opendir so that it is likely to * reuse that fd otherwise we might close opendir's file descriptor in * our loop. This trick assumes that fd's are allocated on a lowest @@ -300,8 +315,6 @@ while (close(start_fd) < 0 && errno == EINTR); ++start_fd; #endif - if (start_fd >= end_fd) - return; #if defined(__FreeBSD__) if (!_is_fdescfs_mounted_on_dev_fd()) @@ -311,7 +324,7 @@ proc_fd_dir = opendir(FD_DIR); if (!proc_fd_dir) { /* No way to get a list of open fds. */ - _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); + _close_fds_by_brute_force(start_fd, py_fds_to_keep); } else { struct dirent *dir_entry; #ifdef HAVE_DIRFD @@ -324,7 +337,7 @@ int fd; if ((fd = _pos_int_from_ascii(dir_entry->d_name)) < 0) continue; /* Not a number. */ - if (fd != fd_used_by_opendir && fd >= start_fd && fd < end_fd && + if (fd != fd_used_by_opendir && fd >= start_fd && !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep)) { while (close(fd) < 0 && errno == EINTR); } @@ -332,13 +345,13 @@ } if (errno) { /* readdir error, revert behavior. Highly Unlikely. */ - _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); + _close_fds_by_brute_force(start_fd, py_fds_to_keep); } closedir(proc_fd_dir); } } -#define _close_open_fd_range _close_open_fd_range_maybe_unsafe +#define _close_open_fds _close_open_fds_maybe_unsafe #endif /* else NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */ @@ -457,14 +470,8 @@ /* close FDs after executing preexec_fn, which might open FDs */ if (close_fds) { - int local_max_fd = max_fd; -#if defined(__NetBSD__) - local_max_fd = fcntl(0, F_MAXFD); - if (local_max_fd < 0) - local_max_fd = max_fd; -#endif /* TODO HP-UX could use pstat_getproc() if anyone cares about it. */ - _close_open_fd_range(3, local_max_fd, py_fds_to_keep); + _close_open_fds(3, py_fds_to_keep); } /* This loop matches the Lib/os.py _execvpe()'s PATH search when */ @@ -759,11 +766,5 @@ PyMODINIT_FUNC PyInit__posixsubprocess(void) { -#ifdef _SC_OPEN_MAX - max_fd = sysconf(_SC_OPEN_MAX); - if (max_fd == -1) -#endif - max_fd = 256; /* Matches Lib/subprocess.py */ - return PyModule_Create(&_posixsubprocessmodule); } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 22:22:27 2014 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 1 Jun 2014 22:22:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Don=27t_restrict_ourselves_to_a_=22max=22_fd_when_closin?= =?utf-8?q?g_fds_before_exec=28=29?= Message-ID: <3ghWCR205nz7Ljn@mail.python.org> http://hg.python.org/cpython/rev/012329c8c4ec changeset: 90948:012329c8c4ec parent: 90945:13254db884e9 parent: 90947:5453b9c59cd7 user: Gregory P. Smith date: Sun Jun 01 13:22:12 2014 -0700 summary: Don't restrict ourselves to a "max" fd when closing fds before exec() when we have a way to get an actual list of all open fds from the OS. Fixes issue #21618: The subprocess module would ignore fds that were inherited by the calling process and already higher than POSIX resource limits would otherwise allow. On systems with a functioning /proc/self/fd or /dev/fd interface the max is now ignored and all fds are closed. files: Lib/test/test_subprocess.py | 53 +++++++++++++++ Misc/NEWS | 5 + Modules/_posixsubprocess.c | 87 ++++++++++++------------ 3 files changed, 102 insertions(+), 43 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1926,6 +1926,59 @@ "Some fds not in pass_fds were left open") self.assertIn(1, remaining_fds, "Subprocess failed") + + def test_close_fds_when_max_fd_is_lowered(self): + """Confirm that issue21618 is fixed (may fail under valgrind).""" + fd_status = support.findfile("fd_status.py", subdir="subprocessdata") + + open_fds = set() + # Add a bunch more fds to pass down. + for _ in range(10): + fd = os.open("/dev/null", os.O_RDONLY) + open_fds.add(fd) + + # Leave a two pairs of low ones available for use by the + # internal child error pipe and the stdout pipe. + for fd in sorted(open_fds)[:4]: + os.close(fd) + open_fds.remove(fd) + + for fd in open_fds: + self.addCleanup(os.close, fd) + os.set_inheritable(fd, True) + + max_fd_open = max(open_fds) + + import resource + rlim_cur, rlim_max = resource.getrlimit(resource.RLIMIT_NOFILE) + try: + # 9 is lower than the highest fds we are leaving open. + resource.setrlimit(resource.RLIMIT_NOFILE, (9, rlim_max)) + # Launch a new Python interpreter with our low fd rlim_cur that + # inherits open fds above that limit. It then uses subprocess + # with close_fds=True to get a report of open fds in the child. + # An explicit list of fds to check is passed to fd_status.py as + # letting fd_status rely on its default logic would miss the + # fds above rlim_cur as it normally only checks up to that limit. + p = subprocess.Popen( + [sys.executable, '-c', + textwrap.dedent(""" + import subprocess, sys + subprocess.Popen([sys.executable, {fd_status!r}] + + [str(x) for x in range({max_fd})], + close_fds=True) + """.format(fd_status=fd_status, max_fd=max_fd_open+1))], + stdout=subprocess.PIPE, close_fds=False) + finally: + resource.setrlimit(resource.RLIMIT_NOFILE, (rlim_cur, rlim_max)) + + output, unused_stderr = p.communicate() + remaining_fds = set(map(int, output.strip().split(b','))) + + self.assertFalse(remaining_fds & open_fds, + msg="Some fds were left open.") + + # Mac OS X Tiger (10.4) has a kernel bug: sometimes, the file # descriptor of a pipe closed in the parent process is valid in the # child process according to fstat(), but the mode of the file diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -89,6 +89,11 @@ Library ------- +- Issue #21618: The subprocess module could fail to close open fds that were + inherited by the calling process and already higher than POSIX resource + limits would otherwise allow. On systems with a functioning /proc/self/fd + or /dev/fd interface the max is now ignored and all fds are closed. + - Issue #20383: Introduce importlib.util.module_from_spec() as the preferred way to create a new module. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -44,10 +44,6 @@ #define POSIX_CALL(call) do { if ((call) == -1) goto error; } while (0) -/* Maximum file descriptor, initialized on module load. */ -static long max_fd; - - /* Given the gc module call gc.enable() and return 0 on success. */ static int _enable_gc(PyObject *gc_module) @@ -166,14 +162,39 @@ } -/* Close all file descriptors in the range start_fd inclusive to - * end_fd exclusive except for those in py_fds_to_keep. If the - * range defined by [start_fd, end_fd) is large this will take a - * long time as it calls close() on EVERY possible fd. +/* Get the maximum file descriptor that could be opened by this process. + * This function is async signal safe for use between fork() and exec(). + */ +static long +safe_get_max_fd(void) +{ + long local_max_fd; +#if defined(__NetBSD__) + local_max_fd = fcntl(0, F_MAXFD); + if (local_max_fd >= 0) + return local_max_fd; +#endif +#ifdef _SC_OPEN_MAX + local_max_fd = sysconf(_SC_OPEN_MAX); + if (local_max_fd == -1) +#endif + local_max_fd = 256; /* Matches legacy Lib/subprocess.py behavior. */ + return local_max_fd; +} + + +/* Close all file descriptors in the range from start_fd and higher + * except for those in py_fds_to_keep. If the range defined by + * [start_fd, safe_get_max_fd()) is large this will take a long + * time as it calls close() on EVERY possible fd. + * + * It isn't possible to know for sure what the max fd to go up to + * is for processes with the capability of raising their maximum. */ static void -_close_fds_by_brute_force(int start_fd, int end_fd, PyObject *py_fds_to_keep) +_close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep) { + long end_fd = safe_get_max_fd(); Py_ssize_t num_fds_to_keep = PySequence_Length(py_fds_to_keep); Py_ssize_t keep_seq_idx; int fd_num; @@ -229,16 +250,14 @@ * it with some cpp #define magic to work on other OSes as well if you want. */ static void -_close_open_fd_range_safe(int start_fd, int end_fd, PyObject* py_fds_to_keep) +_close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep) { int fd_dir_fd; - if (start_fd >= end_fd) - return; fd_dir_fd = _Py_open(FD_DIR, O_RDONLY); if (fd_dir_fd == -1) { /* No way to get a list of open fds. */ - _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); + _close_fds_by_brute_force(start_fd, py_fds_to_keep); return; } else { char buffer[sizeof(struct linux_dirent64)]; @@ -253,23 +272,23 @@ entry = (struct linux_dirent64 *)(buffer + offset); if ((fd = _pos_int_from_ascii(entry->d_name)) < 0) continue; /* Not a number. */ - if (fd != fd_dir_fd && fd >= start_fd && fd < end_fd && + if (fd != fd_dir_fd && fd >= start_fd && !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep)) { while (close(fd) < 0 && errno == EINTR); } } } - close(fd_dir_fd); + while (close(fd_dir_fd) < 0 && errno == EINTR); } } -#define _close_open_fd_range _close_open_fd_range_safe +#define _close_open_fds _close_open_fds_safe #else /* NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */ -/* Close all open file descriptors in the range start_fd inclusive to end_fd - * exclusive. Do not close any in the sorted py_fds_to_keep list. +/* Close all open file descriptors from start_fd and higher. + * Do not close any in the sorted py_fds_to_keep list. * * This function violates the strict use of async signal safe functions. :( * It calls opendir(), readdir() and closedir(). Of these, the one most @@ -282,17 +301,13 @@ * http://womble.decadent.org.uk/readdir_r-advisory.html */ static void -_close_open_fd_range_maybe_unsafe(int start_fd, int end_fd, - PyObject* py_fds_to_keep) +_close_open_fds_maybe_unsafe(long start_fd, PyObject* py_fds_to_keep) { DIR *proc_fd_dir; #ifndef HAVE_DIRFD - while (_is_fd_in_sorted_fd_sequence(start_fd, py_fds_to_keep) && - (start_fd < end_fd)) { + while (_is_fd_in_sorted_fd_sequence(start_fd, py_fds_to_keep)) { ++start_fd; } - if (start_fd >= end_fd) - return; /* Close our lowest fd before we call opendir so that it is likely to * reuse that fd otherwise we might close opendir's file descriptor in * our loop. This trick assumes that fd's are allocated on a lowest @@ -300,8 +315,6 @@ while (close(start_fd) < 0 && errno == EINTR); ++start_fd; #endif - if (start_fd >= end_fd) - return; #if defined(__FreeBSD__) if (!_is_fdescfs_mounted_on_dev_fd()) @@ -311,7 +324,7 @@ proc_fd_dir = opendir(FD_DIR); if (!proc_fd_dir) { /* No way to get a list of open fds. */ - _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); + _close_fds_by_brute_force(start_fd, py_fds_to_keep); } else { struct dirent *dir_entry; #ifdef HAVE_DIRFD @@ -324,7 +337,7 @@ int fd; if ((fd = _pos_int_from_ascii(dir_entry->d_name)) < 0) continue; /* Not a number. */ - if (fd != fd_used_by_opendir && fd >= start_fd && fd < end_fd && + if (fd != fd_used_by_opendir && fd >= start_fd && !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep)) { while (close(fd) < 0 && errno == EINTR); } @@ -332,13 +345,13 @@ } if (errno) { /* readdir error, revert behavior. Highly Unlikely. */ - _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); + _close_fds_by_brute_force(start_fd, py_fds_to_keep); } closedir(proc_fd_dir); } } -#define _close_open_fd_range _close_open_fd_range_maybe_unsafe +#define _close_open_fds _close_open_fds_maybe_unsafe #endif /* else NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */ @@ -457,14 +470,8 @@ /* close FDs after executing preexec_fn, which might open FDs */ if (close_fds) { - int local_max_fd = max_fd; -#if defined(__NetBSD__) - local_max_fd = fcntl(0, F_MAXFD); - if (local_max_fd < 0) - local_max_fd = max_fd; -#endif /* TODO HP-UX could use pstat_getproc() if anyone cares about it. */ - _close_open_fd_range(3, local_max_fd, py_fds_to_keep); + _close_open_fds(3, py_fds_to_keep); } /* This loop matches the Lib/os.py _execvpe()'s PATH search when */ @@ -759,11 +766,5 @@ PyMODINIT_FUNC PyInit__posixsubprocess(void) { -#ifdef _SC_OPEN_MAX - max_fd = sysconf(_SC_OPEN_MAX); - if (max_fd == -1) -#endif - max_fd = 256; /* Matches Lib/subprocess.py */ - return PyModule_Create(&_posixsubprocessmodule); } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 22:47:43 2014 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 1 Jun 2014 22:47:43 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fix_the_commen?= =?utf-8?q?t_to_not_refer_to_the_removed_end=5Ffd_parameter=2E?= Message-ID: <3ghWmb54g6z7LjV@mail.python.org> http://hg.python.org/cpython/rev/2da7f0499440 changeset: 90949:2da7f0499440 branch: 3.4 parent: 90947:5453b9c59cd7 user: Gregory P. Smith date: Sun Jun 01 13:46:36 2014 -0700 summary: Fix the comment to not refer to the removed end_fd parameter. files: Modules/_posixsubprocess.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -234,8 +234,8 @@ char d_name[256]; /* Filename (null-terminated) */ }; -/* Close all open file descriptors in the range start_fd inclusive to end_fd - * exclusive. Do not close any in the sorted py_fds_to_keep list. +/* Close all open file descriptors in the range from start_fd and higher + * Do not close any in the sorted py_fds_to_keep list. * * This version is async signal safe as it does not make any unsafe C library * calls, malloc calls or handle any locks. It is _unfortunate_ to be forced -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 22:47:44 2014 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 1 Jun 2014 22:47:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Explicitly_wai?= =?utf-8?q?t_for_the_child_instead_of_letting_a_destructor_do_it=2E?= Message-ID: <3ghWmc6xjVz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/784bd2c37d52 changeset: 90950:784bd2c37d52 branch: 3.4 user: Gregory P. Smith date: Sun Jun 01 13:46:54 2014 -0700 summary: Explicitly wait for the child instead of letting a destructor do it. files: Lib/test/test_subprocess.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1966,7 +1966,7 @@ import subprocess, sys subprocess.Popen([sys.executable, {fd_status!r}] + [str(x) for x in range({max_fd})], - close_fds=True) + close_fds=True).wait() """.format(fd_status=fd_status, max_fd=max_fd_open+1))], stdout=subprocess.PIPE, close_fds=False) finally: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 1 22:47:46 2014 From: python-checkins at python.org (gregory.p.smith) Date: Sun, 1 Jun 2014 22:47:46 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge_the_end=5Ffd_comment_fix_and_explicit_wait=28=29_f?= =?utf-8?q?or_the_child=2E?= Message-ID: <3ghWmf1GDFz7Ljg@mail.python.org> http://hg.python.org/cpython/rev/e0bc42883ecd changeset: 90951:e0bc42883ecd parent: 90948:012329c8c4ec parent: 90950:784bd2c37d52 user: Gregory P. Smith date: Sun Jun 01 13:47:34 2014 -0700 summary: merge the end_fd comment fix and explicit wait() for the child. files: Lib/test/test_subprocess.py | 2 +- Modules/_posixsubprocess.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1966,7 +1966,7 @@ import subprocess, sys subprocess.Popen([sys.executable, {fd_status!r}] + [str(x) for x in range({max_fd})], - close_fds=True) + close_fds=True).wait() """.format(fd_status=fd_status, max_fd=max_fd_open+1))], stdout=subprocess.PIPE, close_fds=False) finally: diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -234,8 +234,8 @@ char d_name[256]; /* Filename (null-terminated) */ }; -/* Close all open file descriptors in the range start_fd inclusive to end_fd - * exclusive. Do not close any in the sorted py_fds_to_keep list. +/* Close all open file descriptors in the range from start_fd and higher + * Do not close any in the sorted py_fds_to_keep list. * * This version is async signal safe as it does not make any unsafe C library * calls, malloc calls or handle any locks. It is _unfortunate_ to be forced -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 00:16:25 2014 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 2 Jun 2014 00:16:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Attempt_to_fix?= =?utf-8?q?_the_=22too_many_open_files=22_errors_on_several_of_the?= Message-ID: <3ghYkx16WNz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/72211640519e changeset: 90952:72211640519e branch: 3.4 parent: 90950:784bd2c37d52 user: Gregory P. Smith date: Sun Jun 01 15:15:44 2014 -0700 summary: Attempt to fix the "too many open files" errors on several of the buildbots that the new test_close_fds_when_max_fd_is_lowered test causes. It now leaves 10 more low fd's available. files: Lib/test/test_subprocess.py | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1933,13 +1933,15 @@ open_fds = set() # Add a bunch more fds to pass down. - for _ in range(10): + for _ in range(40): fd = os.open("/dev/null", os.O_RDONLY) open_fds.add(fd) # Leave a two pairs of low ones available for use by the # internal child error pipe and the stdout pipe. - for fd in sorted(open_fds)[:4]: + # We also leave 10 more open as some Python buildbots run into + # "too many open files" errors during the test if we do not. + for fd in sorted(open_fds)[:14]: os.close(fd) open_fds.remove(fd) @@ -1952,8 +1954,8 @@ import resource rlim_cur, rlim_max = resource.getrlimit(resource.RLIMIT_NOFILE) try: - # 9 is lower than the highest fds we are leaving open. - resource.setrlimit(resource.RLIMIT_NOFILE, (9, rlim_max)) + # 29 is lower than the highest fds we are leaving open. + resource.setrlimit(resource.RLIMIT_NOFILE, (29, rlim_max)) # Launch a new Python interpreter with our low fd rlim_cur that # inherits open fds above that limit. It then uses subprocess # with close_fds=True to get a report of open fds in the child. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 00:16:26 2014 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 2 Jun 2014 00:16:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Attempt_to_fix_the_=22too_many_open_files=22_errors_on_s?= =?utf-8?q?everal_of_the?= Message-ID: <3ghYky2vMwz7LjX@mail.python.org> http://hg.python.org/cpython/rev/04f6487ad324 changeset: 90953:04f6487ad324 parent: 90951:e0bc42883ecd parent: 90952:72211640519e user: Gregory P. Smith date: Sun Jun 01 15:16:16 2014 -0700 summary: Attempt to fix the "too many open files" errors on several of the buildbots that the new test_close_fds_when_max_fd_is_lowered test causes. It now leaves 10 more low fd's available. files: Lib/test/test_subprocess.py | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1933,13 +1933,15 @@ open_fds = set() # Add a bunch more fds to pass down. - for _ in range(10): + for _ in range(40): fd = os.open("/dev/null", os.O_RDONLY) open_fds.add(fd) # Leave a two pairs of low ones available for use by the # internal child error pipe and the stdout pipe. - for fd in sorted(open_fds)[:4]: + # We also leave 10 more open as some Python buildbots run into + # "too many open files" errors during the test if we do not. + for fd in sorted(open_fds)[:14]: os.close(fd) open_fds.remove(fd) @@ -1952,8 +1954,8 @@ import resource rlim_cur, rlim_max = resource.getrlimit(resource.RLIMIT_NOFILE) try: - # 9 is lower than the highest fds we are leaving open. - resource.setrlimit(resource.RLIMIT_NOFILE, (9, rlim_max)) + # 29 is lower than the highest fds we are leaving open. + resource.setrlimit(resource.RLIMIT_NOFILE, (29, rlim_max)) # Launch a new Python interpreter with our low fd rlim_cur that # inherits open fds above that limit. It then uses subprocess # with close_fds=True to get a report of open fds in the child. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 00:28:20 2014 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 2 Jun 2014 00:28:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogU2tpcCB0ZXN0X3N1?= =?utf-8?q?bprocess_test=5Fclose=5Ffds=5Fwhen=5Fmax=5Ffd=5Fis=5Flowered_on?= =?utf-8?q?_FreeBSD?= Message-ID: <3ghZ0h1Xllz7LjX@mail.python.org> http://hg.python.org/cpython/rev/84e86aa85eb4 changeset: 90954:84e86aa85eb4 branch: 3.4 parent: 90952:72211640519e user: Gregory P. Smith date: Sun Jun 01 15:27:28 2014 -0700 summary: Skip test_subprocess test_close_fds_when_max_fd_is_lowered on FreeBSD when fdescfs is not mounted on /dev/fd. files: Lib/test/test_subprocess.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1927,6 +1927,9 @@ self.assertIn(1, remaining_fds, "Subprocess failed") + @unittest.skipIf(sys.platform.startswith("freebsd") and + os.stat("/dev").st_dev == os.stat("/dev/fd").st_dev, + "Requires fdescfs mounted on /dev/fd on FreeBSD.") def test_close_fds_when_max_fd_is_lowered(self): """Confirm that issue21618 is fixed (may fail under valgrind).""" fd_status = support.findfile("fd_status.py", subdir="subprocessdata") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 00:28:21 2014 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 2 Jun 2014 00:28:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Skip_test=5Fsubprocess_test=5Fclose=5Ffds=5Fwhen=5Fmax?= =?utf-8?q?=5Ffd=5Fis=5Flowered_on_FreeBSD?= Message-ID: <3ghZ0j3T2fz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/9df126ee7005 changeset: 90955:9df126ee7005 parent: 90953:04f6487ad324 parent: 90954:84e86aa85eb4 user: Gregory P. Smith date: Sun Jun 01 15:28:11 2014 -0700 summary: Skip test_subprocess test_close_fds_when_max_fd_is_lowered on FreeBSD when fdescfs is not mounted on /dev/fd. files: Lib/test/test_subprocess.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1927,6 +1927,9 @@ self.assertIn(1, remaining_fds, "Subprocess failed") + @unittest.skipIf(sys.platform.startswith("freebsd") and + os.stat("/dev").st_dev == os.stat("/dev/fd").st_dev, + "Requires fdescfs mounted on /dev/fd on FreeBSD.") def test_close_fds_when_max_fd_is_lowered(self): """Confirm that issue21618 is fixed (may fail under valgrind).""" fd_status = support.findfile("fd_status.py", subdir="subprocessdata") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 01:31:56 2014 From: python-checkins at python.org (vinay.sajip) Date: Mon, 2 Jun 2014 01:31:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Updated_loggin?= =?utf-8?q?g_HOWTO_section_on_optimization=2E?= Message-ID: <3ghbQ43VR8z7LjR@mail.python.org> http://hg.python.org/cpython/rev/98bc1c590838 changeset: 90956:98bc1c590838 branch: 2.7 parent: 90946:6bd21268876e user: Vinay Sajip date: Mon Jun 02 00:24:47 2014 +0100 summary: Updated logging HOWTO section on optimization. files: Doc/howto/logging.rst | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-) diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -996,6 +996,15 @@ so that if the logger's threshold is set above ``DEBUG``, the calls to :func:`expensive_func1` and :func:`expensive_func2` are never made. +.. note:: In some cases, :meth:`~Logger.isEnabledFor` can iself be more + expensive than you'd like (e.g. for deeply nested loggers where an explicit + level is only set high up in the logger hierarchy). In such cases (or if you + want to avoid calling a method in tight loops), you can cache the result of a + call to :meth:`~Logger.isEnabledFor` in a local or instance variable, and use + that instead of calling the method each time. Such a cached value would only + need to be recomputed when the logging configuration changes dynamically + while the application is running (which is not all that common). + There are other optimizations which can be made for specific applications which need more precise control over what logging information is collected. Here's a list of things you can do to avoid processing during logging which you don't @@ -1005,6 +1014,11 @@ | What you don't want to collect | How to avoid collecting it | +===============================================+========================================+ | Information about where calls were made from. | Set ``logging._srcfile`` to ``None``. | +| | This avoids calling | +| | :func:`sys._getframe`, which may help | +| | to speed up your code in environments | +| | like PyPy (which can't speed up code | +| | that uses :func:`sys._getframe`). | +-----------------------------------------------+----------------------------------------+ | Threading information. | Set ``logging.logThreads`` to ``0``. | +-----------------------------------------------+----------------------------------------+ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 01:31:57 2014 From: python-checkins at python.org (vinay.sajip) Date: Mon, 2 Jun 2014 01:31:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Updated_loggin?= =?utf-8?q?g_HOWTO_section_on_optimization=2E?= Message-ID: <3ghbQ5534Kz7LkH@mail.python.org> http://hg.python.org/cpython/rev/e7ebf94629d2 changeset: 90957:e7ebf94629d2 branch: 3.4 parent: 90954:84e86aa85eb4 user: Vinay Sajip date: Mon Jun 02 00:30:48 2014 +0100 summary: Updated logging HOWTO section on optimization. files: Doc/howto/logging.rst | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-) diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -1027,6 +1027,15 @@ so that if the logger's threshold is set above ``DEBUG``, the calls to :func:`expensive_func1` and :func:`expensive_func2` are never made. +.. note:: In some cases, :meth:`~Logger.isEnabledFor` can iself be more + expensive than you'd like (e.g. for deeply nested loggers where an explicit + level is only set high up in the logger hierarchy). In such cases (or if you + want to avoid calling a method in tight loops), you can cache the result of a + call to :meth:`~Logger.isEnabledFor` in a local or instance variable, and use + that instead of calling the method each time. Such a cached value would only + need to be recomputed when the logging configuration changes dynamically + while the application is running (which is not all that common). + There are other optimizations which can be made for specific applications which need more precise control over what logging information is collected. Here's a list of things you can do to avoid processing during logging which you don't @@ -1036,6 +1045,12 @@ | What you don't want to collect | How to avoid collecting it | +===============================================+========================================+ | Information about where calls were made from. | Set ``logging._srcfile`` to ``None``. | +| | This avoids calling | +| | :func:`sys._getframe`, which may help | +| | to speed up your code in environments | +| | like PyPy (which can't speed up code | +| | that uses :func:`sys._getframe`), if | +| | and when PyPy supports Python 3.x. | +-----------------------------------------------+----------------------------------------+ | Threading information. | Set ``logging.logThreads`` to ``0``. | +-----------------------------------------------+----------------------------------------+ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 01:31:58 2014 From: python-checkins at python.org (vinay.sajip) Date: Mon, 2 Jun 2014 01:31:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merged_documentation_update_from_3=2E4=2E?= Message-ID: <3ghbQ66pQzz7LjX@mail.python.org> http://hg.python.org/cpython/rev/110b95446ec2 changeset: 90958:110b95446ec2 parent: 90955:9df126ee7005 parent: 90957:e7ebf94629d2 user: Vinay Sajip date: Mon Jun 02 00:31:44 2014 +0100 summary: Merged documentation update from 3.4. files: Doc/howto/logging.rst | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-) diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -1027,6 +1027,15 @@ so that if the logger's threshold is set above ``DEBUG``, the calls to :func:`expensive_func1` and :func:`expensive_func2` are never made. +.. note:: In some cases, :meth:`~Logger.isEnabledFor` can iself be more + expensive than you'd like (e.g. for deeply nested loggers where an explicit + level is only set high up in the logger hierarchy). In such cases (or if you + want to avoid calling a method in tight loops), you can cache the result of a + call to :meth:`~Logger.isEnabledFor` in a local or instance variable, and use + that instead of calling the method each time. Such a cached value would only + need to be recomputed when the logging configuration changes dynamically + while the application is running (which is not all that common). + There are other optimizations which can be made for specific applications which need more precise control over what logging information is collected. Here's a list of things you can do to avoid processing during logging which you don't @@ -1036,6 +1045,12 @@ | What you don't want to collect | How to avoid collecting it | +===============================================+========================================+ | Information about where calls were made from. | Set ``logging._srcfile`` to ``None``. | +| | This avoids calling | +| | :func:`sys._getframe`, which may help | +| | to speed up your code in environments | +| | like PyPy (which can't speed up code | +| | that uses :func:`sys._getframe`), if | +| | and when PyPy supports Python 3.x. | +-----------------------------------------------+----------------------------------------+ | Threading information. | Set ``logging.logThreads`` to ``0``. | +-----------------------------------------------+----------------------------------------+ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 08:40:08 2014 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 2 Jun 2014 08:40:08 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Minor_clean-ups=2E?= Message-ID: <3ghmw85CTpz7LjV@mail.python.org> http://hg.python.org/cpython/rev/ca2539818a6a changeset: 90959:ca2539818a6a user: Raymond Hettinger date: Sun Jun 01 23:40:01 2014 -0700 summary: Minor clean-ups. files: Lib/heapq.py | 8 +++++--- 1 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/heapq.py b/Lib/heapq.py --- a/Lib/heapq.py +++ b/Lib/heapq.py @@ -397,7 +397,7 @@ except StopIteration: _heappop(h) if h: - key_value, order, value, next = h[0] + key_value, order, value, next = h[0] yield value yield from next.__self__ @@ -413,7 +413,7 @@ # number of comparisons # n inputs k-extreme values (average of 5 trials) % more than min() # ------------- ---------------- --------------------- ----------------- -# 1,000 100 3,317 133.2% +# 1,000 100 3,317 233.2% # 10,000 100 14,046 40.5% # 100,000 100 105,749 5.7% # 1,000,000 100 1,007,751 0.8% @@ -496,6 +496,8 @@ # When key is none, use simpler decoration if key is None: it = iter(iterable) + # put the range(n) first so that zip() doesn't + # consume one too many elements from the iterator result = [(elem, i) for i, elem in zip(range(n), it)] if not result: return result @@ -594,4 +596,4 @@ if __name__ == "__main__": import doctest - doctest.testmod() + print(doctest.testmod()) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Jun 2 09:51:47 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 02 Jun 2014 09:51:47 +0200 Subject: [Python-checkins] Daily reference leaks (110b95446ec2): sum=9 Message-ID: results for 110b95446ec2 on branch "default" -------------------------------------------- test_collections leaked [0, 4, 0] references, sum=4 test_collections leaked [0, 2, 0] memory blocks, sum=2 test_functools leaked [0, 0, 3] memory blocks, sum=3 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/reflogpQCeFc', '-x'] From python-checkins at python.org Mon Jun 2 10:16:35 2014 From: python-checkins at python.org (ned.deily) Date: Mon, 2 Jun 2014 10:16:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE3MDk1?= =?utf-8?q?=3A_Fix_Modules/Setup_*shared*_support=2E?= Message-ID: <3ghq3R270vz7LkR@mail.python.org> http://hg.python.org/cpython/rev/6c468df214dc changeset: 90960:6c468df214dc branch: 3.4 parent: 90957:e7ebf94629d2 user: Ned Deily date: Mon Jun 02 01:05:29 2014 -0700 summary: Issue #17095: Fix Modules/Setup *shared* support. Original patch by Thomas Wouters. files: Misc/NEWS | 5 +++++ Modules/getpath.c | 14 ++++++++++++++ Modules/makesetup | 8 ++------ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -58,6 +58,11 @@ - Issue #21538: The plistlib module now supports loading of binary plist files when reference or offset size is not a power of two. +Build +----- + +- Issue #17095: Fix Modules/Setup *shared* support. + Tests ----- diff --git a/Modules/getpath.c b/Modules/getpath.c --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -734,6 +734,11 @@ bufsz += wcslen(zip_path) + 1; bufsz += wcslen(exec_prefix) + 1; + /* When running from the build directory, add room for the Modules + * subdirectory too. + */ + if (efound == -1) + bufsz += wcslen(argv0_path) + wcslen(L"Modules") + 2; buf = (wchar_t *)PyMem_Malloc(bufsz * sizeof(wchar_t)); if (buf == NULL) { @@ -781,6 +786,15 @@ /* Finally, on goes the directory for dynamic-load modules */ wcscat(buf, exec_prefix); + /* And, if we run from a build directory, the Modules directory (for + * modules built with Modules/Setup.) + */ + if (efound == -1) { + wcscat(buf, delimiter); + wcscat(buf, argv0_path); + wcscat(buf, separator); + wcscat(buf, L"Modules"); + } /* And publish the results */ module_search_path = buf; diff --git a/Modules/makesetup b/Modules/makesetup --- a/Modules/makesetup +++ b/Modules/makesetup @@ -217,7 +217,7 @@ *) src='$(srcdir)/'"$srcdir/$src";; esac case $doconfig in - no) cc="$cc \$(CCSHARED) \$(CFLAGS) \$(CPPFLAGS)";; + no) cc="$cc \$(CCSHARED) \$(PY_CFLAGS) \$(PY_CPPFLAGS)";; *) cc="$cc \$(PY_CORE_CFLAGS)";; esac @@ -229,11 +229,7 @@ esac for mod in $mods do - case $objs in - *$mod.o*) base=$mod;; - *) base=${mod}module;; - esac - file="$srcdir/$base\$(SO)" + file="$srcdir/$mod\$(EXT_SUFFIX)" case $doconfig in no) SHAREDMODS="$SHAREDMODS $file";; esac -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 10:16:36 2014 From: python-checkins at python.org (ned.deily) Date: Mon, 2 Jun 2014 10:16:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2317095=3A_Fix_Modules/Setup_*shared*_support=2E?= Message-ID: <3ghq3S3bMdz7Ll0@mail.python.org> http://hg.python.org/cpython/rev/227ce85bdbe0 changeset: 90961:227ce85bdbe0 parent: 90959:ca2539818a6a parent: 90960:6c468df214dc user: Ned Deily date: Mon Jun 02 01:15:32 2014 -0700 summary: Issue #17095: Fix Modules/Setup *shared* support. Original patch by Thomas Wouters. files: Misc/NEWS | 2 ++ Modules/getpath.c | 14 ++++++++++++++ Modules/makesetup | 8 ++------ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -498,6 +498,8 @@ - Issue #15968: Incorporated Tcl, Tk, and Tix builds into the Windows build solution. +- Issue #17095: Fix Modules/Setup *shared* support. + C API ----- - Issue #20942: PyImport_ImportFrozenModuleObject() no longer sets __file__ to diff --git a/Modules/getpath.c b/Modules/getpath.c --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -734,6 +734,11 @@ bufsz += wcslen(zip_path) + 1; bufsz += wcslen(exec_prefix) + 1; + /* When running from the build directory, add room for the Modules + * subdirectory too. + */ + if (efound == -1) + bufsz += wcslen(argv0_path) + wcslen(L"Modules") + 2; buf = (wchar_t *)PyMem_Malloc(bufsz * sizeof(wchar_t)); if (buf == NULL) { @@ -781,6 +786,15 @@ /* Finally, on goes the directory for dynamic-load modules */ wcscat(buf, exec_prefix); + /* And, if we run from a build directory, the Modules directory (for + * modules built with Modules/Setup.) + */ + if (efound == -1) { + wcscat(buf, delimiter); + wcscat(buf, argv0_path); + wcscat(buf, separator); + wcscat(buf, L"Modules"); + } /* And publish the results */ module_search_path = buf; diff --git a/Modules/makesetup b/Modules/makesetup --- a/Modules/makesetup +++ b/Modules/makesetup @@ -217,7 +217,7 @@ *) src='$(srcdir)/'"$srcdir/$src";; esac case $doconfig in - no) cc="$cc \$(CCSHARED) \$(CFLAGS) \$(CPPFLAGS)";; + no) cc="$cc \$(CCSHARED) \$(PY_CFLAGS) \$(PY_CPPFLAGS)";; *) cc="$cc \$(PY_CORE_CFLAGS)";; esac @@ -229,11 +229,7 @@ esac for mod in $mods do - case $objs in - *$mod.o*) base=$mod;; - *) base=${mod}module;; - esac - file="$srcdir/$base\$(SO)" + file="$srcdir/$mod\$(EXT_SUFFIX)" case $doconfig in no) SHAREDMODS="$SHAREDMODS $file";; esac -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 10:32:30 2014 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 2 Jun 2014 10:32:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_comment?= Message-ID: <3ghqPp4zX4z7Ljc@mail.python.org> http://hg.python.org/cpython/rev/dc3afbee4ad1 changeset: 90962:dc3afbee4ad1 user: Raymond Hettinger date: Mon Jun 02 01:32:23 2014 -0700 summary: Fix comment files: Lib/heapq.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/heapq.py b/Lib/heapq.py --- a/Lib/heapq.py +++ b/Lib/heapq.py @@ -413,7 +413,7 @@ # number of comparisons # n inputs k-extreme values (average of 5 trials) % more than min() # ------------- ---------------- --------------------- ----------------- -# 1,000 100 3,317 233.2% +# 1,000 100 3,317 231.7% # 10,000 100 14,046 40.5% # 100,000 100 105,749 5.7% # 1,000,000 100 1,007,751 0.8% -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 14:13:16 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 14:13:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Initialize_base_types_befo?= =?utf-8?q?re_child_types?= Message-ID: <3ghwJX72GWz7Ll1@mail.python.org> http://hg.python.org/cpython/rev/e4255fa0fdac changeset: 90963:e4255fa0fdac user: Victor Stinner date: Mon Jun 02 14:10:59 2014 +0200 summary: Initialize base types before child types object (PyBaseObject_Type) is the base type of type (PyType_Type), int (PyLong_Type) is the base type of bool (PyBool_Type). files: Objects/object.c | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Objects/object.c b/Objects/object.c --- a/Objects/object.c +++ b/Objects/object.c @@ -1543,6 +1543,9 @@ void _Py_ReadyTypes(void) { + if (PyType_Ready(&PyBaseObject_Type) < 0) + Py_FatalError("Can't initialize object type"); + if (PyType_Ready(&PyType_Type) < 0) Py_FatalError("Can't initialize type type"); @@ -1555,6 +1558,9 @@ if (PyType_Ready(&_PyWeakref_ProxyType) < 0) Py_FatalError("Can't initialize weakref proxy type"); + if (PyType_Ready(&PyLong_Type) < 0) + Py_FatalError("Can't initialize int type"); + if (PyType_Ready(&PyBool_Type) < 0) Py_FatalError("Can't initialize bool type"); @@ -1579,9 +1585,6 @@ if (PyType_Ready(&PySuper_Type) < 0) Py_FatalError("Can't initialize super type"); - if (PyType_Ready(&PyBaseObject_Type) < 0) - Py_FatalError("Can't initialize object type"); - if (PyType_Ready(&PyRange_Type) < 0) Py_FatalError("Can't initialize range type"); @@ -1606,9 +1609,6 @@ if (PyType_Ready(&PyFloat_Type) < 0) Py_FatalError("Can't initialize float type"); - if (PyType_Ready(&PyLong_Type) < 0) - Py_FatalError("Can't initialize int type"); - if (PyType_Ready(&PyFrozenSet_Type) < 0) Py_FatalError("Can't initialize frozenset type"); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 14:18:26 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 14:18:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjM0?= =?utf-8?q?=3A_Fix_pystone_micro-benchmark=3A_use_floor_division_instead_o?= =?utf-8?q?f_true?= Message-ID: <3ghwQV5h4qz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/1318324aa93a changeset: 90964:1318324aa93a branch: 3.4 parent: 90960:6c468df214dc user: Victor Stinner date: Mon Jun 02 14:16:44 2014 +0200 summary: Issue #21634: Fix pystone micro-benchmark: use floor division instead of true division to benchmark integers instead of floating point numbers. Set pystone version to 1.2. Patch written by Lennart Regebro. files: Lib/test/pystone.py | 13 ++++++++++--- Misc/NEWS | 6 +++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Lib/test/pystone.py b/Lib/test/pystone.py --- a/Lib/test/pystone.py +++ b/Lib/test/pystone.py @@ -3,7 +3,7 @@ """ "PYSTONE" Benchmark Program -Version: Python/1.1 (corresponds to C/1.1 plus 2 Pystone fixes) +Version: Python/1.2 (corresponds to C/1.1 plus 3 Pystone fixes) Author: Reinhold P. Weicker, CACM Vol 27, No 10, 10/84 pg. 1013. @@ -30,13 +30,20 @@ percent faster than version 1.0, so benchmark figures of different versions can't be compared directly. + Version 1.2 changes the division to floor division. + + Under Python 3 version 1.1 would use the normal division + operator, resulting in some of the operations mistakenly + yielding floats. Version 1.2 instead uses floor division + making the benchmark a integer benchmark again. + """ LOOPS = 50000 from time import clock -__version__ = "1.1" +__version__ = "1.2" [Ident1, Ident2, Ident3, Ident4, Ident5] = range(1, 6) @@ -123,7 +130,7 @@ EnumLoc = Proc6(Ident1) CharIndex = chr(ord(CharIndex)+1) IntLoc3 = IntLoc2 * IntLoc1 - IntLoc2 = IntLoc3 / IntLoc1 + IntLoc2 = IntLoc3 // IntLoc1 IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1 IntLoc1 = Proc2(IntLoc1) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -46,7 +46,7 @@ - Issue #14710: pkgutil.find_loader() no longer raises an exception when a module doesn't exist. -- Issue #21481: Argparse equality and inequality tests now return +- Issue #21481: Argparse equality and inequality tests now return NotImplemented when comparing to an unknown type. - Issue #8743: Fix interoperability between set objects and the @@ -66,6 +66,10 @@ Tests ----- +- Issue #21634: Fix pystone micro-benchmark: use floor division instead of true + division to benchmark integers instead of floating point numbers. Set pystone + version to 1.2. Patch written by Lennart Regebro. + - Issue #21605: Added tests for Tkinter images. - Issue #21493: Added test for ntpath.expanduser(). Original patch by -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 14:18:28 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 14:18:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321634=3A_Fix_pystone_micro-be?= =?utf-8?q?nchmark=3A_use_floor_division?= Message-ID: <3ghwQX0c30z7Lk2@mail.python.org> http://hg.python.org/cpython/rev/95b7acdc0f24 changeset: 90965:95b7acdc0f24 parent: 90963:e4255fa0fdac parent: 90964:1318324aa93a user: Victor Stinner date: Mon Jun 02 14:18:15 2014 +0200 summary: (Merge 3.4) Issue #21634: Fix pystone micro-benchmark: use floor division instead of true division to benchmark integers instead of floating point numbers. Set pystone version to 1.2. Patch written by Lennart Regebro. files: Lib/test/pystone.py | 13 ++++++++++--- Misc/NEWS | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Lib/test/pystone.py b/Lib/test/pystone.py --- a/Lib/test/pystone.py +++ b/Lib/test/pystone.py @@ -3,7 +3,7 @@ """ "PYSTONE" Benchmark Program -Version: Python/1.1 (corresponds to C/1.1 plus 2 Pystone fixes) +Version: Python/1.2 (corresponds to C/1.1 plus 3 Pystone fixes) Author: Reinhold P. Weicker, CACM Vol 27, No 10, 10/84 pg. 1013. @@ -30,13 +30,20 @@ percent faster than version 1.0, so benchmark figures of different versions can't be compared directly. + Version 1.2 changes the division to floor division. + + Under Python 3 version 1.1 would use the normal division + operator, resulting in some of the operations mistakenly + yielding floats. Version 1.2 instead uses floor division + making the benchmark a integer benchmark again. + """ LOOPS = 50000 from time import clock -__version__ = "1.1" +__version__ = "1.2" [Ident1, Ident2, Ident3, Ident4, Ident5] = range(1, 6) @@ -123,7 +130,7 @@ EnumLoc = Proc6(Ident1) CharIndex = chr(ord(CharIndex)+1) IntLoc3 = IntLoc2 * IntLoc1 - IntLoc2 = IntLoc3 / IntLoc1 + IntLoc2 = IntLoc3 // IntLoc1 IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1 IntLoc1 = Proc2(IntLoc1) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -532,6 +532,10 @@ Tests ----- +- Issue #21634: Fix pystone micro-benchmark: use floor division instead of true + division to benchmark integers instead of floating point numbers. Set pystone + version to 1.2. Patch written by Lennart Regebro. + - Issue #21605: Added tests for Tkinter images. - Issue #21493: Added test for ntpath.expanduser(). Original patch by -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 14:43:35 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 14:43:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjM2?= =?utf-8?q?=3A_Fix_test=5Flogging=2C_skip_UNIX_stream_=28AF=5FUNIX=29_test?= =?utf-8?q?s_on_Windows=2E?= Message-ID: <3ghwzW6Dnlz7LjP@mail.python.org> http://hg.python.org/cpython/rev/d7a491c6cbdb changeset: 90966:d7a491c6cbdb branch: 3.4 parent: 90964:1318324aa93a user: Victor Stinner date: Mon Jun 02 14:41:51 2014 +0200 summary: Issue #21636: Fix test_logging, skip UNIX stream (AF_UNIX) tests on Windows. Patch written by Claudiu Popa. files: Lib/test/test_logging.py | 22 ++++++++++++---------- 1 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -865,9 +865,6 @@ super(TestTCPServer, self).server_bind() self.port = self.socket.getsockname()[1] - class TestUnixStreamServer(TestTCPServer): - address_family = socket.AF_UNIX - class TestUDPServer(ControlMixin, ThreadingUDPServer): """ A UDP server which is controllable using :class:`ControlMixin`. @@ -915,8 +912,12 @@ super(TestUDPServer, self).server_close() self._closed = True - class TestUnixDatagramServer(TestUDPServer): - address_family = socket.AF_UNIX + if hasattr(socket, "AF_UNIX"): + class TestUnixStreamServer(TestTCPServer): + address_family = socket.AF_UNIX + + class TestUnixDatagramServer(TestUDPServer): + address_family = socket.AF_UNIX # - end of server_helper section @@ -1457,12 +1458,13 @@ os.remove(fn) return fn + at unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") @unittest.skipUnless(threading, 'Threading required for this test.') class UnixSocketHandlerTest(SocketHandlerTest): """Test for SocketHandler with unix sockets.""" - if threading: + if threading and hasattr(socket, "AF_UNIX"): server_class = TestUnixStreamServer def setUp(self): @@ -1528,13 +1530,13 @@ self.handled.wait() self.assertEqual(self.log_output, "spam\neggs\n") - + at unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") @unittest.skipUnless(threading, 'Threading required for this test.') class UnixDatagramHandlerTest(DatagramHandlerTest): """Test for DatagramHandler using Unix sockets.""" - if threading: + if threading and hasattr(socket, "AF_UNIX"): server_class = TestUnixDatagramServer def setUp(self): @@ -1603,13 +1605,13 @@ self.handled.wait() self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m') - + at unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") @unittest.skipUnless(threading, 'Threading required for this test.') class UnixSysLogHandlerTest(SysLogHandlerTest): """Test for SysLogHandler with Unix sockets.""" - if threading: + if threading and hasattr(socket, "AF_UNIX"): server_class = TestUnixDatagramServer def setUp(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 14:43:37 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 14:43:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogKE1lcmdlIDMuNCkgSXNzdWUgIzIxNjM2OiBGaXggdGVzdF9sb2dnaW5n?= =?utf-8?q?=2C_skip_UNIX_stream_=28AF=5FUNIX=29_tests_on?= Message-ID: <3ghwzY0ZNvz7Lkr@mail.python.org> http://hg.python.org/cpython/rev/b49e366556ba changeset: 90967:b49e366556ba parent: 90965:95b7acdc0f24 parent: 90966:d7a491c6cbdb user: Victor Stinner date: Mon Jun 02 14:43:24 2014 +0200 summary: (Merge 3.4) Issue #21636: Fix test_logging, skip UNIX stream (AF_UNIX) tests on Windows. Patch written by Claudiu Popa. files: Lib/test/test_logging.py | 22 ++++++++++++---------- 1 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -860,9 +860,6 @@ super(TestTCPServer, self).server_bind() self.port = self.socket.getsockname()[1] - class TestUnixStreamServer(TestTCPServer): - address_family = socket.AF_UNIX - class TestUDPServer(ControlMixin, ThreadingUDPServer): """ A UDP server which is controllable using :class:`ControlMixin`. @@ -910,8 +907,12 @@ super(TestUDPServer, self).server_close() self._closed = True - class TestUnixDatagramServer(TestUDPServer): - address_family = socket.AF_UNIX + if hasattr(socket, "AF_UNIX"): + class TestUnixStreamServer(TestTCPServer): + address_family = socket.AF_UNIX + + class TestUnixDatagramServer(TestUDPServer): + address_family = socket.AF_UNIX # - end of server_helper section @@ -1452,12 +1453,13 @@ os.remove(fn) return fn + at unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") @unittest.skipUnless(threading, 'Threading required for this test.') class UnixSocketHandlerTest(SocketHandlerTest): """Test for SocketHandler with unix sockets.""" - if threading: + if threading and hasattr(socket, "AF_UNIX"): server_class = TestUnixStreamServer def setUp(self): @@ -1523,13 +1525,13 @@ self.handled.wait() self.assertEqual(self.log_output, "spam\neggs\n") - + at unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") @unittest.skipUnless(threading, 'Threading required for this test.') class UnixDatagramHandlerTest(DatagramHandlerTest): """Test for DatagramHandler using Unix sockets.""" - if threading: + if threading and hasattr(socket, "AF_UNIX"): server_class = TestUnixDatagramServer def setUp(self): @@ -1598,13 +1600,13 @@ self.handled.wait() self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m') - + at unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") @unittest.skipUnless(threading, 'Threading required for this test.') class UnixSysLogHandlerTest(SysLogHandlerTest): """Test for SysLogHandler with Unix sockets.""" - if threading: + if threading and hasattr(socket, "AF_UNIX"): server_class = TestUnixDatagramServer def setUp(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 15:54:11 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 2 Jun 2014 15:54:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzYxODE6?= =?utf-8?q?_Fixed_errors_in_tkinter=2EListbox_docstrings=2E?= Message-ID: <3ghyXz5Q8qz7Lyf@mail.python.org> http://hg.python.org/cpython/rev/5ab8ec8b5b5b changeset: 90968:5ab8ec8b5b5b branch: 2.7 parent: 90956:98bc1c590838 user: Serhiy Storchaka date: Mon Jun 02 16:50:03 2014 +0300 summary: Issue #6181: Fixed errors in tkinter.Listbox docstrings. Based on patch by Guilherme Polo. files: Lib/lib-tk/Tkinter.py | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/lib-tk/Tkinter.py b/Lib/lib-tk/Tkinter.py --- a/Lib/lib-tk/Tkinter.py +++ b/Lib/lib-tk/Tkinter.py @@ -2580,10 +2580,10 @@ return self.tk.splitlist(self.tk.call( self._w, 'curselection')) def delete(self, first, last=None): - """Delete items from FIRST to LAST (not included).""" + """Delete items from FIRST to LAST (included).""" self.tk.call(self._w, 'delete', first, last) def get(self, first, last=None): - """Get list of items from FIRST to LAST (not included).""" + """Get list of items from FIRST to LAST (included).""" if last: return self.tk.splitlist(self.tk.call( self._w, 'get', first, last)) @@ -2617,7 +2617,7 @@ self.tk.call(self._w, 'selection', 'anchor', index) select_anchor = selection_anchor def selection_clear(self, first, last=None): - """Clear the selection from FIRST to LAST (not included).""" + """Clear the selection from FIRST to LAST (included).""" self.tk.call(self._w, 'selection', 'clear', first, last) select_clear = selection_clear @@ -2627,7 +2627,7 @@ self._w, 'selection', 'includes', index)) select_includes = selection_includes def selection_set(self, first, last=None): - """Set the selection from FIRST to LAST (not included) without + """Set the selection from FIRST to LAST (included) without changing the currently selected elements.""" self.tk.call(self._w, 'selection', 'set', first, last) select_set = selection_set -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 15:54:13 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 2 Jun 2014 15:54:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzYxODE6?= =?utf-8?q?_Fixed_errors_in_tkinter=2EListbox_docstrings=2E?= Message-ID: <3ghyY103xwz7Lyv@mail.python.org> http://hg.python.org/cpython/rev/932532360578 changeset: 90969:932532360578 branch: 3.4 parent: 90966:d7a491c6cbdb user: Serhiy Storchaka date: Mon Jun 02 16:51:44 2014 +0300 summary: Issue #6181: Fixed errors in tkinter.Listbox docstrings. Based on patch by Guilherme Polo. files: Lib/tkinter/__init__.py | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2597,10 +2597,10 @@ return self.tk.splitlist(self.tk.call( self._w, 'curselection')) def delete(self, first, last=None): - """Delete items from FIRST to LAST (not included).""" + """Delete items from FIRST to LAST (included).""" self.tk.call(self._w, 'delete', first, last) def get(self, first, last=None): - """Get list of items from FIRST to LAST (not included).""" + """Get list of items from FIRST to LAST (included).""" if last: return self.tk.splitlist(self.tk.call( self._w, 'get', first, last)) @@ -2634,7 +2634,7 @@ self.tk.call(self._w, 'selection', 'anchor', index) select_anchor = selection_anchor def selection_clear(self, first, last=None): - """Clear the selection from FIRST to LAST (not included).""" + """Clear the selection from FIRST to LAST (included).""" self.tk.call(self._w, 'selection', 'clear', first, last) select_clear = selection_clear @@ -2644,7 +2644,7 @@ self._w, 'selection', 'includes', index)) select_includes = selection_includes def selection_set(self, first, last=None): - """Set the selection from FIRST to LAST (not included) without + """Set the selection from FIRST to LAST (included) without changing the currently selected elements.""" self.tk.call(self._w, 'selection', 'set', first, last) select_set = selection_set -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 15:54:14 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 2 Jun 2014 15:54:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=236181=3A_Fixed_errors_in_tkinter=2EListbox_docst?= =?utf-8?q?rings=2E?= Message-ID: <3ghyY21bmTz7Ln4@mail.python.org> http://hg.python.org/cpython/rev/bc0ac2f10aa0 changeset: 90970:bc0ac2f10aa0 parent: 90967:b49e366556ba parent: 90969:932532360578 user: Serhiy Storchaka date: Mon Jun 02 16:52:16 2014 +0300 summary: Issue #6181: Fixed errors in tkinter.Listbox docstrings. Based on patch by Guilherme Polo. files: Lib/tkinter/__init__.py | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2602,10 +2602,10 @@ return self.tk.splitlist(self.tk.call( self._w, 'curselection')) def delete(self, first, last=None): - """Delete items from FIRST to LAST (not included).""" + """Delete items from FIRST to LAST (included).""" self.tk.call(self._w, 'delete', first, last) def get(self, first, last=None): - """Get list of items from FIRST to LAST (not included).""" + """Get list of items from FIRST to LAST (included).""" if last: return self.tk.splitlist(self.tk.call( self._w, 'get', first, last)) @@ -2639,7 +2639,7 @@ self.tk.call(self._w, 'selection', 'anchor', index) select_anchor = selection_anchor def selection_clear(self, first, last=None): - """Clear the selection from FIRST to LAST (not included).""" + """Clear the selection from FIRST to LAST (included).""" self.tk.call(self._w, 'selection', 'clear', first, last) select_clear = selection_clear @@ -2649,7 +2649,7 @@ self._w, 'selection', 'includes', index)) select_includes = selection_includes def selection_set(self, first, last=None): - """Set the selection from FIRST to LAST (not included) without + """Set the selection from FIRST to LAST (included) without changing the currently selected elements.""" self.tk.call(self._w, 'selection', 'set', first, last) select_set = selection_set -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 19:16:30 2014 From: python-checkins at python.org (zach.ware) Date: Mon, 2 Jun 2014 19:16:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjIz?= =?utf-8?q?=3A_open_pyproject=2Eprops_with_an_explicit_encoding?= Message-ID: <3gj32Q3Tdrz7LjW@mail.python.org> http://hg.python.org/cpython/rev/1d36bd258ee8 changeset: 90971:1d36bd258ee8 branch: 3.4 parent: 90969:932532360578 user: Zachary Ware date: Mon Jun 02 12:09:13 2014 -0500 summary: Issue #21623: open pyproject.props with an explicit encoding files: PCbuild/build_ssl.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/PCbuild/build_ssl.py b/PCbuild/build_ssl.py --- a/PCbuild/build_ssl.py +++ b/PCbuild/build_ssl.py @@ -66,7 +66,7 @@ # Fetch SSL directory from VC properties def get_ssl_dir(): propfile = (os.path.join(os.path.dirname(__file__), 'pyproject.props')) - with open(propfile) as f: + with open(propfile, encoding='utf-8-sig') as f: m = re.search('openssl-([^<]+)<', f.read()) return "..\..\openssl-"+m.group(1) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 19:16:31 2014 From: python-checkins at python.org (zach.ware) Date: Mon, 2 Jun 2014 19:16:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Null_merge?= Message-ID: <3gj32R5BB3z7LkT@mail.python.org> http://hg.python.org/cpython/rev/751603cbea7f changeset: 90972:751603cbea7f parent: 90970:bc0ac2f10aa0 parent: 90971:1d36bd258ee8 user: Zachary Ware date: Mon Jun 02 12:11:09 2014 -0500 summary: Null merge files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 20:52:34 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 2 Jun 2014 20:52:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzYxODE6?= =?utf-8?q?_Fixed_minor_bugs_in_tkinter=2EListbox_methods=3A?= Message-ID: <3gj59G4jsnz7Ljf@mail.python.org> http://hg.python.org/cpython/rev/8cd7eb00894e changeset: 90973:8cd7eb00894e branch: 2.7 parent: 90968:5ab8ec8b5b5b user: Serhiy Storchaka date: Mon Jun 02 21:30:53 2014 +0300 summary: Issue #6181: Fixed minor bugs in tkinter.Listbox methods: bbox(), curselection() and get(). files: Lib/lib-tk/Tkinter.py | 15 +- Lib/lib-tk/test/test_tkinter/test_widgets.py | 52 ++++++++- Lib/lib-tk/test/test_ttk/test_widgets.py | 12 +- Lib/lib-tk/test/widget_tests.py | 10 + 4 files changed, 60 insertions(+), 29 deletions(-) diff --git a/Lib/lib-tk/Tkinter.py b/Lib/lib-tk/Tkinter.py --- a/Lib/lib-tk/Tkinter.py +++ b/Lib/lib-tk/Tkinter.py @@ -2569,22 +2569,19 @@ def activate(self, index): """Activate item identified by INDEX.""" self.tk.call(self._w, 'activate', index) - def bbox(self, *args): + def bbox(self, index): """Return a tuple of X1,Y1,X2,Y2 coordinates for a rectangle - which encloses the item identified by index in ARGS.""" - return self._getints( - self.tk.call((self._w, 'bbox') + args)) or None + which encloses the item identified by the given index.""" + return self._getints(self.tk.call(self._w, 'bbox', index)) or None def curselection(self): - """Return list of indices of currently selected item.""" - # XXX Ought to apply self._getints()... - return self.tk.splitlist(self.tk.call( - self._w, 'curselection')) + """Return the indices of currently selected item.""" + return self._getints(self.tk.call(self._w, 'curselection')) or () def delete(self, first, last=None): """Delete items from FIRST to LAST (included).""" self.tk.call(self._w, 'delete', first, last) def get(self, first, last=None): """Get list of items from FIRST to LAST (included).""" - if last: + if last is not None: return self.tk.splitlist(self.tk.call( self._w, 'get', first, last)) else: 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 @@ -464,11 +464,7 @@ def test_bbox(self): widget = self.create() - bbox = widget.bbox(0) - self.assertEqual(len(bbox), 4) - for item in bbox: - self.assertIsInstance(item, int) - + self.assertIsBoundingBox(widget.bbox(0)) self.assertRaises(Tkinter.TclError, widget.bbox, 'noindex') self.assertRaises(Tkinter.TclError, widget.bbox, None) self.assertRaises(TypeError, widget.bbox) @@ -621,11 +617,7 @@ def test_bbox(self): widget = self.create() - bbox = widget.bbox('1.1') - self.assertEqual(len(bbox), 4) - for item in bbox: - self.assertIsInstance(item, int) - + self.assertIsBoundingBox(widget.bbox('1.1')) self.assertIsNone(widget.bbox('end')) self.assertRaises(Tkinter.TclError, widget.bbox, 'noindex') self.assertRaises(Tkinter.TclError, widget.bbox, None) @@ -782,6 +774,46 @@ def test_itemconfigure_selectforeground(self): self.check_itemconfigure('selectforeground', '#654321') + def test_box(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + lb.pack() + self.assertIsBoundingBox(lb.bbox(0)) + self.assertIsNone(lb.bbox(-1)) + self.assertIsNone(lb.bbox(10)) + self.assertRaises(TclError, lb.bbox, 'noindex') + self.assertRaises(TclError, lb.bbox, None) + self.assertRaises(TypeError, lb.bbox) + self.assertRaises(TypeError, lb.bbox, 0, 1) + + def test_curselection(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + lb.selection_clear(0, Tkinter.END) + lb.selection_set(2, 4) + lb.selection_set(6) + self.assertEqual(lb.curselection(), (2, 3, 4, 6)) + self.assertRaises(TypeError, lb.curselection, 0) + + def test_get(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + self.assertEqual(lb.get(0), 'el0') + self.assertEqual(lb.get(3), 'el3') + self.assertEqual(lb.get('end'), 'el7') + self.assertEqual(lb.get(8), '') + self.assertEqual(lb.get(-1), '') + self.assertEqual(lb.get(3, 5), ('el3', 'el4', 'el5')) + self.assertEqual(lb.get(5, 'end'), ('el5', 'el6', 'el7')) + self.assertEqual(lb.get(5, 0), ()) + self.assertEqual(lb.get(0, 0), ('el0',)) + self.assertRaises(TclError, lb.get, 'noindex') + self.assertRaises(TclError, lb.get, None) + self.assertRaises(TypeError, lb.get) + self.assertRaises(TclError, lb.get, 'end', 'noindex') + self.assertRaises(TypeError, lb.get, 1, 2, 3) + self.assertRaises(TclError, lb.get, 2.4) + @add_standard_options(PixelSizeTests, StandardOptionsTests) class ScaleTest(AbstractWidgetTest, unittest.TestCase): diff --git a/Lib/lib-tk/test/test_ttk/test_widgets.py b/Lib/lib-tk/test/test_ttk/test_widgets.py --- a/Lib/lib-tk/test/test_ttk/test_widgets.py +++ b/Lib/lib-tk/test/test_ttk/test_widgets.py @@ -461,10 +461,7 @@ def test_bbox(self): - self.assertEqual(len(self.entry.bbox(0)), 4) - for item in self.entry.bbox(0): - self.assertIsInstance(item, int) - + self.assertIsBoundingBox(self.entry.bbox(0)) self.assertRaises(Tkinter.TclError, self.entry.bbox, 'noindex') self.assertRaises(Tkinter.TclError, self.entry.bbox, None) @@ -1217,12 +1214,7 @@ self.assertTrue(children) bbox = self.tv.bbox(children[0]) - self.assertEqual(len(bbox), 4) - self.assertIsInstance(bbox, tuple) - for item in bbox: - if not isinstance(item, int): - self.fail("Invalid bounding box: %s" % bbox) - break + self.assertIsBoundingBox(bbox) # compare width in bboxes self.tv['columns'] = ['test'] 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 @@ -221,6 +221,16 @@ def checkVariableParam(self, widget, name, var): self.checkParam(widget, name, var, conv=str) + def assertIsBoundingBox(self, bbox): + self.assertIsNotNone(bbox) + self.assertIsInstance(bbox, tuple) + if len(bbox) != 4: + self.fail('Invalid bounding box: %r' % (bbox,)) + for item in bbox: + if not isinstance(item, int): + self.fail('Invalid bounding box: %r' % (bbox,)) + break + class StandardOptionsTests(object): STANDARD_OPTIONS = ( -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 20:52:36 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 2 Jun 2014 20:52:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzYxODE6?= =?utf-8?q?_Fixed_minor_bugs_in_tkinter=2EListbox_methods=3A?= Message-ID: <3gj59J0F1wz7Lkm@mail.python.org> http://hg.python.org/cpython/rev/54a2ceacac05 changeset: 90974:54a2ceacac05 branch: 3.4 parent: 90971:1d36bd258ee8 user: Serhiy Storchaka date: Mon Jun 02 21:31:07 2014 +0300 summary: Issue #6181: Fixed minor bugs in tkinter.Listbox methods: bbox(), curselection() and get(). files: Lib/tkinter/__init__.py | 15 +- Lib/tkinter/test/test_tkinter/test_widgets.py | 52 ++++++++- Lib/tkinter/test/test_ttk/test_widgets.py | 12 +- Lib/tkinter/test/widget_tests.py | 10 + 4 files changed, 60 insertions(+), 29 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2586,22 +2586,19 @@ def activate(self, index): """Activate item identified by INDEX.""" self.tk.call(self._w, 'activate', index) - def bbox(self, *args): + def bbox(self, index): """Return a tuple of X1,Y1,X2,Y2 coordinates for a rectangle - which encloses the item identified by index in ARGS.""" - return self._getints( - self.tk.call((self._w, 'bbox') + args)) or None + which encloses the item identified by the given index.""" + return self._getints(self.tk.call(self._w, 'bbox', index)) or None def curselection(self): - """Return list of indices of currently selected item.""" - # XXX Ought to apply self._getints()... - return self.tk.splitlist(self.tk.call( - self._w, 'curselection')) + """Return the indices of currently selected item.""" + return self._getints(self.tk.call(self._w, 'curselection')) or () def delete(self, first, last=None): """Delete items from FIRST to LAST (included).""" self.tk.call(self._w, 'delete', first, last) def get(self, first, last=None): """Get list of items from FIRST to LAST (included).""" - if last: + if last is not None: return self.tk.splitlist(self.tk.call( self._w, 'get', first, last)) else: 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 @@ -467,11 +467,7 @@ def test_bbox(self): widget = self.create() - bbox = widget.bbox(0) - self.assertEqual(len(bbox), 4) - for item in bbox: - self.assertIsInstance(item, int) - + self.assertIsBoundingBox(widget.bbox(0)) self.assertRaises(tkinter.TclError, widget.bbox, 'noindex') self.assertRaises(tkinter.TclError, widget.bbox, None) self.assertRaises(TypeError, widget.bbox) @@ -624,11 +620,7 @@ def test_bbox(self): widget = self.create() - bbox = widget.bbox('1.1') - self.assertEqual(len(bbox), 4) - for item in bbox: - self.assertIsInstance(item, int) - + self.assertIsBoundingBox(widget.bbox('1.1')) self.assertIsNone(widget.bbox('end')) self.assertRaises(tkinter.TclError, widget.bbox, 'noindex') self.assertRaises(tkinter.TclError, widget.bbox, None) @@ -785,6 +777,46 @@ def test_itemconfigure_selectforeground(self): self.check_itemconfigure('selectforeground', '#654321') + def test_box(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + lb.pack() + self.assertIsBoundingBox(lb.bbox(0)) + self.assertIsNone(lb.bbox(-1)) + self.assertIsNone(lb.bbox(10)) + self.assertRaises(TclError, lb.bbox, 'noindex') + self.assertRaises(TclError, lb.bbox, None) + self.assertRaises(TypeError, lb.bbox) + self.assertRaises(TypeError, lb.bbox, 0, 1) + + def test_curselection(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + lb.selection_clear(0, tkinter.END) + lb.selection_set(2, 4) + lb.selection_set(6) + self.assertEqual(lb.curselection(), (2, 3, 4, 6)) + self.assertRaises(TypeError, lb.curselection, 0) + + def test_get(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + self.assertEqual(lb.get(0), 'el0') + self.assertEqual(lb.get(3), 'el3') + self.assertEqual(lb.get('end'), 'el7') + self.assertEqual(lb.get(8), '') + self.assertEqual(lb.get(-1), '') + self.assertEqual(lb.get(3, 5), ('el3', 'el4', 'el5')) + self.assertEqual(lb.get(5, 'end'), ('el5', 'el6', 'el7')) + self.assertEqual(lb.get(5, 0), ()) + self.assertEqual(lb.get(0, 0), ('el0',)) + self.assertRaises(TclError, lb.get, 'noindex') + self.assertRaises(TclError, lb.get, None) + self.assertRaises(TypeError, lb.get) + self.assertRaises(TclError, lb.get, 'end', 'noindex') + self.assertRaises(TypeError, lb.get, 1, 2, 3) + self.assertRaises(TclError, lb.get, 2.4) + @add_standard_options(PixelSizeTests, StandardOptionsTests) class ScaleTest(AbstractWidgetTest, unittest.TestCase): diff --git a/Lib/tkinter/test/test_ttk/test_widgets.py b/Lib/tkinter/test/test_ttk/test_widgets.py --- a/Lib/tkinter/test/test_ttk/test_widgets.py +++ b/Lib/tkinter/test/test_ttk/test_widgets.py @@ -460,10 +460,7 @@ def test_bbox(self): - self.assertEqual(len(self.entry.bbox(0)), 4) - for item in self.entry.bbox(0): - self.assertIsInstance(item, int) - + self.assertIsBoundingBox(self.entry.bbox(0)) self.assertRaises(tkinter.TclError, self.entry.bbox, 'noindex') self.assertRaises(tkinter.TclError, self.entry.bbox, None) @@ -1216,12 +1213,7 @@ self.assertTrue(children) bbox = self.tv.bbox(children[0]) - self.assertEqual(len(bbox), 4) - self.assertIsInstance(bbox, tuple) - for item in bbox: - if not isinstance(item, int): - self.fail("Invalid bounding box: %s" % bbox) - break + self.assertIsBoundingBox(bbox) # compare width in bboxes self.tv['columns'] = ['test'] 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 @@ -202,6 +202,16 @@ def checkVariableParam(self, widget, name, var): self.checkParam(widget, name, var, conv=str) + def assertIsBoundingBox(self, bbox): + self.assertIsNotNone(bbox) + self.assertIsInstance(bbox, tuple) + if len(bbox) != 4: + self.fail('Invalid bounding box: %r' % (bbox,)) + for item in bbox: + if not isinstance(item, int): + self.fail('Invalid bounding box: %r' % (bbox,)) + break + class StandardOptionsTests: STANDARD_OPTIONS = ( -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 20:52:37 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 2 Jun 2014 20:52:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=236181=3A_Fixed_minor_bugs_in_tkinter=2EListbox_m?= =?utf-8?q?ethods=3A?= Message-ID: <3gj59K36N4z7Llk@mail.python.org> http://hg.python.org/cpython/rev/3a923156ca05 changeset: 90975:3a923156ca05 parent: 90972:751603cbea7f parent: 90974:54a2ceacac05 user: Serhiy Storchaka date: Mon Jun 02 21:32:49 2014 +0300 summary: Issue #6181: Fixed minor bugs in tkinter.Listbox methods: bbox(), curselection() and get(). files: Lib/tkinter/__init__.py | 15 +- Lib/tkinter/test/test_tkinter/test_widgets.py | 52 ++++++++- Lib/tkinter/test/test_ttk/test_widgets.py | 12 +- Lib/tkinter/test/widget_tests.py | 10 + 4 files changed, 60 insertions(+), 29 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2591,22 +2591,19 @@ def activate(self, index): """Activate item identified by INDEX.""" self.tk.call(self._w, 'activate', index) - def bbox(self, *args): + def bbox(self, index): """Return a tuple of X1,Y1,X2,Y2 coordinates for a rectangle - which encloses the item identified by index in ARGS.""" - return self._getints( - self.tk.call((self._w, 'bbox') + args)) or None + which encloses the item identified by the given index.""" + return self._getints(self.tk.call(self._w, 'bbox', index)) or None def curselection(self): - """Return list of indices of currently selected item.""" - # XXX Ought to apply self._getints()... - return self.tk.splitlist(self.tk.call( - self._w, 'curselection')) + """Return the indices of currently selected item.""" + return self._getints(self.tk.call(self._w, 'curselection')) or () def delete(self, first, last=None): """Delete items from FIRST to LAST (included).""" self.tk.call(self._w, 'delete', first, last) def get(self, first, last=None): """Get list of items from FIRST to LAST (included).""" - if last: + if last is not None: return self.tk.splitlist(self.tk.call( self._w, 'get', first, last)) else: 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 @@ -467,11 +467,7 @@ def test_bbox(self): widget = self.create() - bbox = widget.bbox(0) - self.assertEqual(len(bbox), 4) - for item in bbox: - self.assertIsInstance(item, int) - + self.assertIsBoundingBox(widget.bbox(0)) self.assertRaises(tkinter.TclError, widget.bbox, 'noindex') self.assertRaises(tkinter.TclError, widget.bbox, None) self.assertRaises(TypeError, widget.bbox) @@ -624,11 +620,7 @@ def test_bbox(self): widget = self.create() - bbox = widget.bbox('1.1') - self.assertEqual(len(bbox), 4) - for item in bbox: - self.assertIsInstance(item, int) - + self.assertIsBoundingBox(widget.bbox('1.1')) self.assertIsNone(widget.bbox('end')) self.assertRaises(tkinter.TclError, widget.bbox, 'noindex') self.assertRaises(tkinter.TclError, widget.bbox, None) @@ -785,6 +777,46 @@ def test_itemconfigure_selectforeground(self): self.check_itemconfigure('selectforeground', '#654321') + def test_box(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + lb.pack() + self.assertIsBoundingBox(lb.bbox(0)) + self.assertIsNone(lb.bbox(-1)) + self.assertIsNone(lb.bbox(10)) + self.assertRaises(TclError, lb.bbox, 'noindex') + self.assertRaises(TclError, lb.bbox, None) + self.assertRaises(TypeError, lb.bbox) + self.assertRaises(TypeError, lb.bbox, 0, 1) + + def test_curselection(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + lb.selection_clear(0, tkinter.END) + lb.selection_set(2, 4) + lb.selection_set(6) + self.assertEqual(lb.curselection(), (2, 3, 4, 6)) + self.assertRaises(TypeError, lb.curselection, 0) + + def test_get(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + self.assertEqual(lb.get(0), 'el0') + self.assertEqual(lb.get(3), 'el3') + self.assertEqual(lb.get('end'), 'el7') + self.assertEqual(lb.get(8), '') + self.assertEqual(lb.get(-1), '') + self.assertEqual(lb.get(3, 5), ('el3', 'el4', 'el5')) + self.assertEqual(lb.get(5, 'end'), ('el5', 'el6', 'el7')) + self.assertEqual(lb.get(5, 0), ()) + self.assertEqual(lb.get(0, 0), ('el0',)) + self.assertRaises(TclError, lb.get, 'noindex') + self.assertRaises(TclError, lb.get, None) + self.assertRaises(TypeError, lb.get) + self.assertRaises(TclError, lb.get, 'end', 'noindex') + self.assertRaises(TypeError, lb.get, 1, 2, 3) + self.assertRaises(TclError, lb.get, 2.4) + @add_standard_options(PixelSizeTests, StandardOptionsTests) class ScaleTest(AbstractWidgetTest, unittest.TestCase): diff --git a/Lib/tkinter/test/test_ttk/test_widgets.py b/Lib/tkinter/test/test_ttk/test_widgets.py --- a/Lib/tkinter/test/test_ttk/test_widgets.py +++ b/Lib/tkinter/test/test_ttk/test_widgets.py @@ -460,10 +460,7 @@ def test_bbox(self): - self.assertEqual(len(self.entry.bbox(0)), 4) - for item in self.entry.bbox(0): - self.assertIsInstance(item, int) - + self.assertIsBoundingBox(self.entry.bbox(0)) self.assertRaises(tkinter.TclError, self.entry.bbox, 'noindex') self.assertRaises(tkinter.TclError, self.entry.bbox, None) @@ -1216,12 +1213,7 @@ self.assertTrue(children) bbox = self.tv.bbox(children[0]) - self.assertEqual(len(bbox), 4) - self.assertIsInstance(bbox, tuple) - for item in bbox: - if not isinstance(item, int): - self.fail("Invalid bounding box: %s" % bbox) - break + self.assertIsBoundingBox(bbox) # compare width in bboxes self.tv['columns'] = ['test'] 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 @@ -202,6 +202,16 @@ def checkVariableParam(self, widget, name, var): self.checkParam(widget, name, var, conv=str) + def assertIsBoundingBox(self, bbox): + self.assertIsNotNone(bbox) + self.assertIsInstance(bbox, tuple) + if len(bbox) != 4: + self.fail('Invalid bounding box: %r' % (bbox,)) + for item in bbox: + if not isinstance(item, int): + self.fail('Invalid bounding box: %r' % (bbox,)) + break + class StandardOptionsTests: STANDARD_OPTIONS = ( -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 21:41:01 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 21:41:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjM5?= =?utf-8?q?=3A_Fix_name_of_=5Ftestcapi_test_functions?= Message-ID: <3gj6F93jVZz7LjW@mail.python.org> http://hg.python.org/cpython/rev/d87ede8da3c8 changeset: 90976:d87ede8da3c8 branch: 3.4 parent: 90974:54a2ceacac05 user: Victor Stinner date: Mon Jun 02 21:29:07 2014 +0200 summary: Issue #21639: Fix name of _testcapi test functions files: Modules/_testcapimodule.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3104,9 +3104,9 @@ {"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS}, {"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS}, {"with_tp_del", with_tp_del, METH_VARARGS}, - {"test_pymem", + {"test_pymem_alloc0", (PyCFunction)test_pymem_alloc0, METH_NOARGS}, - {"test_pymem_alloc0", + {"test_pymem_setrawallocators", (PyCFunction)test_pymem_setrawallocators, METH_NOARGS}, {"test_pymem_setallocators", (PyCFunction)test_pymem_setallocators, METH_NOARGS}, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 21:41:02 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 21:41:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321639=3A_Fix_name_of_=5Ftestc?= =?utf-8?q?api_test_functions?= Message-ID: <3gj6FB5CbBz7LjW@mail.python.org> http://hg.python.org/cpython/rev/7083634065c9 changeset: 90977:7083634065c9 parent: 90975:3a923156ca05 parent: 90976:d87ede8da3c8 user: Victor Stinner date: Mon Jun 02 21:29:28 2014 +0200 summary: (Merge 3.4) Issue #21639: Fix name of _testcapi test functions files: Modules/_testcapimodule.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3167,9 +3167,9 @@ {"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS}, {"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS}, {"with_tp_del", with_tp_del, METH_VARARGS}, - {"test_pymem", + {"test_pymem_alloc0", (PyCFunction)test_pymem_alloc0, METH_NOARGS}, - {"test_pymem_alloc0", + {"test_pymem_setrawallocators", (PyCFunction)test_pymem_setrawallocators, METH_NOARGS}, {"test_pymem_setallocators", (PyCFunction)test_pymem_setallocators, METH_NOARGS}, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 21:41:03 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 21:41:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjM5?= =?utf-8?q?=3A_Add_a_test_to_check_that_PyMem=5FMalloc=280=29_with_tracema?= =?utf-8?q?lloc_enabled?= Message-ID: <3gj6FC7116z7Lkm@mail.python.org> http://hg.python.org/cpython/rev/6f362a702242 changeset: 90978:6f362a702242 branch: 3.4 parent: 90976:d87ede8da3c8 user: Victor Stinner date: Mon Jun 02 21:36:59 2014 +0200 summary: Issue #21639: Add a test to check that PyMem_Malloc(0) with tracemalloc enabled does not crash. files: Lib/test/test_tracemalloc.py | 6 ++++++ 1 files changed, 6 insertions(+), 0 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 @@ -807,6 +807,12 @@ b'number of frames', stderr) + def test_pymem_alloc0(self): + # Issue #21639: Check that PyMem_Malloc(0) with tracemalloc enabled + # does not crash. + code = 'import _testcapi; _testcapi.test_pymem_alloc0(); 1' + assert_python_ok('-X', 'tracemalloc', '-c', code) + def test_main(): support.run_unittest( -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 21:41:05 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 21:41:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321639=3A_Fix_a_division_by_zero_in_tracemalloc_?= =?utf-8?q?on_calloc=280=2C_0=29=2E_The?= Message-ID: <3gj6FF1Kfcz7Ll0@mail.python.org> http://hg.python.org/cpython/rev/1505124c0df4 changeset: 90979:1505124c0df4 parent: 90977:7083634065c9 parent: 90978:6f362a702242 user: Victor Stinner date: Mon Jun 02 21:40:22 2014 +0200 summary: Issue #21639: Fix a division by zero in tracemalloc on calloc(0, 0). The regression was introduced recently with the introduction of the new "calloc" functions (PyMem_RawCalloc, PyMem_Calloc, PyObject_Calloc). Add also a unit test to check for the non-regression. files: Lib/test/test_tracemalloc.py | 6 ++++++ Modules/_tracemalloc.c | 2 +- 2 files changed, 7 insertions(+), 1 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 @@ -807,6 +807,12 @@ b'number of frames', stderr) + def test_pymem_alloc0(self): + # Issue #21639: Check that PyMem_Malloc(0) with tracemalloc enabled + # does not crash. + code = 'import _testcapi; _testcapi.test_pymem_alloc0(); 1' + assert_python_ok('-X', 'tracemalloc', '-c', code) + def test_main(): support.run_unittest( diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -478,7 +478,7 @@ PyMemAllocator *alloc = (PyMemAllocator *)ctx; void *ptr; - assert(nelem <= PY_SIZE_MAX / elsize); + assert(elsize == 0 || nelem <= PY_SIZE_MAX / elsize); if (use_calloc) ptr = alloc->calloc(alloc->ctx, nelem, elsize); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 21:57:41 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 21:57:41 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321233=3A_Rename_t?= =?utf-8?q?he_C_structure_=22PyMemAllocator=22_to_=22PyMemAllocatorEx=22_t?= =?utf-8?q?o?= Message-ID: <3gj6cP1XYMz7LjW@mail.python.org> http://hg.python.org/cpython/rev/6374c2d957a9 changeset: 90980:6374c2d957a9 user: Victor Stinner date: Mon Jun 02 21:57:10 2014 +0200 summary: Issue #21233: Rename the C structure "PyMemAllocator" to "PyMemAllocatorEx" to make sure that the code using it will be adapted for the new "calloc" field (instead of crashing). files: Doc/c-api/memory.rst | 10 ++++++---- Doc/whatsnew/3.5.rst | 3 ++- Include/pymem.h | 8 ++++---- Modules/_testcapimodule.c | 4 ++-- Modules/_tracemalloc.c | 22 +++++++++++----------- Objects/obmalloc.c | 14 +++++++------- 6 files changed, 32 insertions(+), 29 deletions(-) diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -232,7 +232,7 @@ .. versionadded:: 3.4 -.. c:type:: PyMemAllocator +.. c:type:: PyMemAllocatorEx Structure used to describe a memory block allocator. The structure has four fields: @@ -253,7 +253,9 @@ +----------------------------------------------------------+---------------------------------------+ .. versionchanged:: 3.5 - Add a new field ``calloc``. + The :c:type:`PyMemAllocator` structure was renamed to + :c:type:`PyMemAllocatorEx` and a new ``calloc`` field was added. + .. c:type:: PyMemAllocatorDomain @@ -267,12 +269,12 @@ :c:func:`PyObject_Realloc` and :c:func:`PyObject_Free` -.. c:function:: void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator) +.. c:function:: void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) Get the memory block allocator of the specified domain. -.. c:function:: void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator) +.. c:function:: void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) Set the memory block allocator of the specified domain. diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -271,4 +271,5 @@ Changes in the C API -------------------- -* The :c:type:`PyMemAllocator` structure has a new ``calloc`` field. +* The :c:type:`PyMemAllocator` structure was renamed to + :c:type:`PyMemAllocatorEx` and a new ``calloc`` field was added. diff --git a/Include/pymem.h b/Include/pymem.h --- a/Include/pymem.h +++ b/Include/pymem.h @@ -128,7 +128,7 @@ } PyMemAllocatorDomain; typedef struct { - /* user context passed as the first argument to the 3 functions */ + /* user context passed as the first argument to the 4 functions */ void *ctx; /* allocate a memory block */ @@ -142,11 +142,11 @@ /* release a memory block */ void (*free) (void *ctx, void *ptr); -} PyMemAllocator; +} PyMemAllocatorEx; /* Get the memory block allocator of the specified domain. */ PyAPI_FUNC(void) PyMem_GetAllocator(PyMemAllocatorDomain domain, - PyMemAllocator *allocator); + PyMemAllocatorEx *allocator); /* Set the memory block allocator of the specified domain. @@ -160,7 +160,7 @@ PyMem_SetupDebugHooks() function must be called to reinstall the debug hooks on top on the new allocator. */ PyAPI_FUNC(void) PyMem_SetAllocator(PyMemAllocatorDomain domain, - PyMemAllocator *allocator); + PyMemAllocatorEx *allocator); /* Setup hooks to detect bugs in the following Python memory allocator functions: diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2756,7 +2756,7 @@ } typedef struct { - PyMemAllocator alloc; + PyMemAllocatorEx alloc; size_t malloc_size; size_t calloc_nelem; @@ -2802,7 +2802,7 @@ PyObject *res = NULL; const char *error_msg; alloc_hook_t hook; - PyMemAllocator alloc; + PyMemAllocatorEx alloc; size_t size, size2, nelem, elsize; void *ptr, *ptr2; diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -18,9 +18,9 @@ /* Protected by the GIL */ static struct { - PyMemAllocator mem; - PyMemAllocator raw; - PyMemAllocator obj; + PyMemAllocatorEx mem; + PyMemAllocatorEx raw; + PyMemAllocatorEx obj; } allocators; static struct { @@ -475,7 +475,7 @@ static void* tracemalloc_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize) { - PyMemAllocator *alloc = (PyMemAllocator *)ctx; + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; void *ptr; assert(elsize == 0 || nelem <= PY_SIZE_MAX / elsize); @@ -501,7 +501,7 @@ static void* tracemalloc_realloc(void *ctx, void *ptr, size_t new_size) { - PyMemAllocator *alloc = (PyMemAllocator *)ctx; + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; void *ptr2; ptr2 = alloc->realloc(alloc->ctx, ptr, new_size); @@ -546,7 +546,7 @@ static void tracemalloc_free(void *ctx, void *ptr) { - PyMemAllocator *alloc = (PyMemAllocator *)ctx; + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; if (ptr == NULL) return; @@ -567,7 +567,7 @@ void *ptr; if (get_reentrant()) { - PyMemAllocator *alloc = (PyMemAllocator *)ctx; + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; if (use_calloc) return alloc->calloc(alloc->ctx, nelem, elsize); else @@ -607,7 +607,7 @@ Example: PyMem_RawRealloc() is called internally by pymalloc (_PyObject_Malloc() and _PyObject_Realloc()) to allocate a new arena (new_arena()). */ - PyMemAllocator *alloc = (PyMemAllocator *)ctx; + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; ptr2 = alloc->realloc(alloc->ctx, ptr, new_size); if (ptr2 != NULL && ptr != NULL) { @@ -639,7 +639,7 @@ void *ptr; if (get_reentrant()) { - PyMemAllocator *alloc = (PyMemAllocator *)ctx; + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; if (use_calloc) return alloc->calloc(alloc->ctx, nelem, elsize); else @@ -685,7 +685,7 @@ if (get_reentrant()) { /* Reentrant call to PyMem_RawRealloc(). */ - PyMemAllocator *alloc = (PyMemAllocator *)ctx; + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; ptr2 = alloc->realloc(alloc->ctx, ptr, new_size); @@ -863,7 +863,7 @@ static int tracemalloc_start(int max_nframe) { - PyMemAllocator alloc; + PyMemAllocatorEx alloc; size_t size; if (tracemalloc_init() < 0) diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -151,7 +151,7 @@ typedef struct { /* We tag each block with an API ID in order to tag API violations */ char api_id; - PyMemAllocator alloc; + PyMemAllocatorEx alloc; } debug_alloc_api_t; static struct { debug_alloc_api_t raw; @@ -166,7 +166,7 @@ #define PYDBG_FUNCS _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree #endif -static PyMemAllocator _PyMem_Raw = { +static PyMemAllocatorEx _PyMem_Raw = { #ifdef PYMALLOC_DEBUG &_PyMem_Debug.raw, PYDBG_FUNCS #else @@ -174,7 +174,7 @@ #endif }; -static PyMemAllocator _PyMem = { +static PyMemAllocatorEx _PyMem = { #ifdef PYMALLOC_DEBUG &_PyMem_Debug.mem, PYDBG_FUNCS #else @@ -182,7 +182,7 @@ #endif }; -static PyMemAllocator _PyObject = { +static PyMemAllocatorEx _PyObject = { #ifdef PYMALLOC_DEBUG &_PyMem_Debug.obj, PYDBG_FUNCS #else @@ -209,7 +209,7 @@ PyMem_SetupDebugHooks(void) { #ifdef PYMALLOC_DEBUG - PyMemAllocator alloc; + PyMemAllocatorEx alloc; alloc.malloc = _PyMem_DebugMalloc; alloc.calloc = _PyMem_DebugCalloc; @@ -237,7 +237,7 @@ } void -PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator) +PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) { switch(domain) { @@ -255,7 +255,7 @@ } void -PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator) +PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) { switch(domain) { -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 22:23:26 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 22:23:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321233=3A_Revert_b?= =?utf-8?q?ytearray=28int=29_optimization_using_calloc=28=29?= Message-ID: <3gj7B663rjz7LjW@mail.python.org> http://hg.python.org/cpython/rev/dff6b4b61cac changeset: 90981:dff6b4b61cac user: Victor Stinner date: Mon Jun 02 22:22:42 2014 +0200 summary: Issue #21233: Revert bytearray(int) optimization using calloc() files: Doc/whatsnew/3.5.rst | 5 ++--- Misc/NEWS | 7 +++---- Objects/bytearrayobject.c | 16 ++-------------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -196,9 +196,8 @@ The following performance enhancements have been added: -* Construction of ``bytes(int)`` and ``bytearray(int)`` (filled by zero bytes) - is faster and use less memory (until the bytearray buffer is filled with - data) for large objects. ``calloc()`` is used instead of ``malloc()`` to +* Construction of ``bytes(int)`` (filled by zero bytes) is faster and use less + memory for large objects. ``calloc()`` is used instead of ``malloc()`` to allocate memory for these objects. * Some operations on :class:`~ipaddress.IPv4Network` and diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -26,10 +26,9 @@ internal iteration logic. - Issue #21233: Add new C functions: PyMem_RawCalloc(), PyMem_Calloc(), - PyObject_Calloc(), _PyObject_GC_Calloc(). bytes(int) and bytearray(int) - are now using ``calloc()`` instead of ``malloc()`` for large objects which - is faster and use less memory (until the bytearray buffer is filled with - data). + PyObject_Calloc(), _PyObject_GC_Calloc(). bytes(int) is now using + ``calloc()`` instead of ``malloc()`` for large objects which is faster and + use less memory. - Issue #21377: PyBytes_Concat() now tries to concatenate in-place when the first argument has a reference count of 1. Patch by Nikolaus Rath. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -813,21 +813,9 @@ } else { if (count > 0) { - void *sval; - Py_ssize_t alloc; - - assert (Py_SIZE(self) == 0); - - alloc = count + 1; - sval = PyObject_Calloc(1, alloc); - if (sval == NULL) + if (PyByteArray_Resize((PyObject *)self, count)) return -1; - - PyObject_Free(self->ob_bytes); - - self->ob_bytes = self->ob_start = sval; - Py_SIZE(self) = count; - self->ob_alloc = alloc; + memset(PyByteArray_AS_STRING(self), 0, count); } return 0; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 23:05:46 2014 From: python-checkins at python.org (zach.ware) Date: Mon, 2 Jun 2014 23:05:46 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4NDky?= =?utf-8?q?=3A_Allow_all_resources_when_tests_are_not_run_by_regrtest=2Epy?= =?utf-8?q?=2E?= Message-ID: <3gj86y6JVYz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/eabff2a97b5b changeset: 90982:eabff2a97b5b branch: 2.7 parent: 90973:8cd7eb00894e user: Zachary Ware date: Mon Jun 02 16:01:16 2014 -0500 summary: Issue #18492: Allow all resources when tests are not run by regrtest.py. This changeset also includes cleanup allowed by this behavior change. files: Lib/idlelib/FormatParagraph.py | 1 - Lib/idlelib/IdleHistory.py | 2 - Lib/idlelib/SearchEngine.py | 1 - Lib/idlelib/idle_test/README.txt | 37 +++++++++++-------- Lib/lib-tk/test/runtktests.py | 1 - Lib/test/test_codecmaps_hk.py | 1 - Lib/test/test_decimal.py | 4 +- Lib/test/test_idle.py | 4 -- Lib/test/test_imaplib.py | 1 - Lib/test/test_support.py | 18 +++----- Lib/test/test_tk.py | 10 +---- Lib/test/test_ttk_guionly.py | 10 +---- Misc/NEWS | 3 + 13 files changed, 37 insertions(+), 56 deletions(-) diff --git a/Lib/idlelib/FormatParagraph.py b/Lib/idlelib/FormatParagraph.py --- a/Lib/idlelib/FormatParagraph.py +++ b/Lib/idlelib/FormatParagraph.py @@ -188,7 +188,6 @@ return m.group(1) if __name__ == "__main__": - from test import support; support.use_resources = ['gui'] import unittest unittest.main('idlelib.idle_test.test_formatparagraph', verbosity=2, exit=False) diff --git a/Lib/idlelib/IdleHistory.py b/Lib/idlelib/IdleHistory.py --- a/Lib/idlelib/IdleHistory.py +++ b/Lib/idlelib/IdleHistory.py @@ -100,7 +100,5 @@ self.prefix = None if __name__ == "__main__": - from test import test_support as support - support.use_resources = ['gui'] from unittest import main main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False) diff --git a/Lib/idlelib/SearchEngine.py b/Lib/idlelib/SearchEngine.py --- a/Lib/idlelib/SearchEngine.py +++ b/Lib/idlelib/SearchEngine.py @@ -229,6 +229,5 @@ return line, col if __name__ == "__main__": - from test import test_support; test_support.use_resources = ['gui'] import unittest unittest.main('idlelib.idle_test.test_searchengine', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/README.txt b/Lib/idlelib/idle_test/README.txt --- a/Lib/idlelib/idle_test/README.txt +++ b/Lib/idlelib/idle_test/README.txt @@ -26,7 +26,6 @@ with xyz (lowercased) added after 'test_'. --- if __name__ == "__main__": - from test import support; support.use_resources = ['gui'] import unittest unittest.main('idlelib.idle_test.test_', verbosity=2, exit=False) --- @@ -34,12 +33,12 @@ 2. Gui Tests -Gui tests need 'requires' and 'use_resources' from test.support -(test.test_support in 2.7). A test is a gui test if it creates a Tk root or -master object either directly or indirectly by instantiating a tkinter or -idle class. For the benefit of buildbot machines that do not have a graphics -screen, gui tests must be 'guarded' by "requires('gui')" in a setUp -function or method. This will typically be setUpClass. +Gui tests need 'requires' from test.support (test.test_support in 2.7). A +test is a gui test if it creates a Tk root or master object either directly +or indirectly by instantiating a tkinter or idle class. For the benefit of +test processes that either have no graphical environment available or are not +allowed to use it, gui tests must be 'guarded' by "requires('gui')" in a +setUp function or method. This will typically be setUpClass. To avoid interfering with other gui tests, all gui objects must be destroyed and deleted by the end of the test. If a widget, such as a Tk root, is created @@ -57,11 +56,17 @@ del cls.root --- -Support.requires('gui') returns true if it is either called in a main module -(which never happens on buildbots) or if use_resources contains 'gui'. -Use_resources is set by test.regrtest but not by unittest. So when running -tests in another module with unittest, we set it ourselves, as in the xyz.py -template above. +Support.requires('gui') causes the test(s) it guards to be skipped if any of +a few conditions are met: + - The tests are being run by regrtest.py, and it was started without + enabling the "gui" resource with the "-u" command line option. + - The tests are being run on Windows by a service that is not allowed to + interact with the graphical environment. + - The tests are being run on Mac OSX in a process that cannot make a window + manager connection. + - tkinter.Tk cannot be successfully instantiated for some reason. + - test.support.use_resources has been set by something other than + regrtest.py and does not contain "gui". Since non-gui tests always run, but gui tests only sometimes, tests of non-gui operations should best avoid needing a gui. Methods that make incidental use of @@ -88,8 +93,8 @@ To run all idle_test/test_*.py tests, either interactively ('>>>', with unittest imported) or from a command line, use one of the -following. (Notes: unittest does not run gui tests; in 2.7, 'test ' (with the -space) is 'test.regrtest '; where present, -v and -ugui can be omitted.) +following. (Notes: in 2.7, 'test ' (with the space) is 'test.regrtest '; +where present, -v and -ugui can be omitted.) >>> unittest.main('idlelib.idle_test', verbosity=2, exit=False) python -m unittest -v idlelib.idle_test @@ -98,13 +103,13 @@ The idle tests are 'discovered' by idlelib.idle_test.__init__.load_tests, which is also imported into test.test_idle. Normally, neither file should be -changed when working on individual test modules. The third command runs runs +changed when working on individual test modules. The third command runs unittest indirectly through regrtest. The same happens when the entire test suite is run with 'python -m test'. So that command must work for buildbots to stay green. Idle tests must not disturb the environment in a way that makes other tests fail (issue 18081). To run an individual Testcase or test method, extend the dotted name given to -unittest on the command line. (But gui tests will not this way.) +unittest on the command line. python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth diff --git a/Lib/lib-tk/test/runtktests.py b/Lib/lib-tk/test/runtktests.py --- a/Lib/lib-tk/test/runtktests.py +++ b/Lib/lib-tk/test/runtktests.py @@ -67,5 +67,4 @@ yield test if __name__ == "__main__": - test.test_support.use_resources = ['gui'] test.test_support.run_unittest(*get_tests()) diff --git a/Lib/test/test_codecmaps_hk.py b/Lib/test/test_codecmaps_hk.py --- a/Lib/test/test_codecmaps_hk.py +++ b/Lib/test/test_codecmaps_hk.py @@ -16,5 +16,4 @@ test_support.run_unittest(__name__) if __name__ == "__main__": - test_support.use_resources = ['urlfetch'] test_main() diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -2271,7 +2271,7 @@ "operation raises different flags depending on flags set: " + "expected %s, got %s" % (expected_flags, new_flags)) -def test_main(arith=False, verbose=None, todo_tests=None, debug=None): +def test_main(arith=None, verbose=None, todo_tests=None, debug=None): """ Execute the tests. Runs all arithmetic tests if arith is True or if the "decimal" resource @@ -2280,7 +2280,7 @@ init() global TEST_ALL, DEBUG - TEST_ALL = arith or is_resource_enabled('decimal') + TEST_ALL = arith if arith is not None else is_resource_enabled('decimal') DEBUG = debug if todo_tests is None: diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -17,8 +17,4 @@ support.run_unittest(unittest.TestLoader().loadTestsFromModule(idletest)) if __name__ == '__main__': - # Until unittest supports resources, we emulate regrtest's -ugui - # so loaded tests run the same as if textually present here. - # If any Idle test ever needs another resource, add it to the list. - support.use_resources = ['gui'] # use_resources is initially None unittest.main(verbosity=2, exit=False) diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -249,5 +249,4 @@ if __name__ == "__main__": - support.use_resources = ['network'] test_main() 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 @@ -346,21 +346,17 @@ return _is_gui_available.result def is_resource_enabled(resource): - """Test whether a resource is enabled. Known resources are set by - regrtest.py.""" - return use_resources is not None and resource in use_resources + """Test whether a resource is enabled. + + Known resources are set by regrtest.py. If not running under regrtest.py, + all resources are assumed enabled unless use_resources has been set. + """ + return use_resources is None or resource in use_resources def requires(resource, msg=None): - """Raise ResourceDenied if the specified resource is not available. - - If the caller's module is __main__ then automatically return True. The - possibility of False being returned occurs when regrtest.py is executing.""" + """Raise ResourceDenied if the specified resource is not available.""" if resource == 'gui' and not _is_gui_available(): raise ResourceDenied(_is_gui_available.reason) - # see if the caller's module is __main__ - if so, treat as if - # the resource was set - if sys._getframe(1).f_globals.get("__name__") == "__main__": - return if not is_resource_enabled(resource): if msg is None: msg = "Use of the `%s' resource not enabled" % resource diff --git a/Lib/test/test_tk.py b/Lib/test/test_tk.py --- a/Lib/test/test_tk.py +++ b/Lib/test/test_tk.py @@ -12,16 +12,10 @@ with test_support.DirsOnSysPath(lib_tk_test): import runtktests -def test_main(enable_gui=False): - if enable_gui: - if test_support.use_resources is None: - test_support.use_resources = ['gui'] - elif 'gui' not in test_support.use_resources: - test_support.use_resources.append('gui') - +def test_main(): with test_support.DirsOnSysPath(lib_tk_test): test_support.run_unittest( *runtktests.get_tests(text=False, packages=['test_tkinter'])) if __name__ == '__main__': - test_main(enable_gui=True) + test_main() diff --git a/Lib/test/test_ttk_guionly.py b/Lib/test/test_ttk_guionly.py --- a/Lib/test/test_ttk_guionly.py +++ b/Lib/test/test_ttk_guionly.py @@ -22,13 +22,7 @@ # assuming ttk is not available raise unittest.SkipTest("ttk not available: %s" % msg) -def test_main(enable_gui=False): - if enable_gui: - if test_support.use_resources is None: - test_support.use_resources = ['gui'] - elif 'gui' not in test_support.use_resources: - test_support.use_resources.append('gui') - +def test_main(): with test_support.DirsOnSysPath(lib_tk_test): from test_ttk.support import get_tk_root try: @@ -38,4 +32,4 @@ get_tk_root().destroy() if __name__ == '__main__': - test_main(enable_gui=True) + test_main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -46,6 +46,9 @@ Tests ----- +- Issue #18492: All resources are now allowed when tests are not run by + regrtest.py. + - Issue #21605: Added tests for Tkinter images. - Issue #21493: Added test for ntpath.expanduser(). Original patch by -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 23:05:48 2014 From: python-checkins at python.org (zach.ware) Date: Mon, 2 Jun 2014 23:05:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE4NDky?= =?utf-8?q?=3A_Allow_all_resources_when_tests_are_not_run_by_regrtest=2Epy?= =?utf-8?q?=2E?= Message-ID: <3gj8702RPFz7Lkb@mail.python.org> http://hg.python.org/cpython/rev/8519576b0dc5 changeset: 90983:8519576b0dc5 branch: 3.4 parent: 90978:6f362a702242 user: Zachary Ware date: Mon Jun 02 16:01:29 2014 -0500 summary: Issue #18492: Allow all resources when tests are not run by regrtest.py. This changeset also includes cleanup allowed by this behavior change. files: Lib/idlelib/FormatParagraph.py | 1 - Lib/idlelib/IdleHistory.py | 2 - Lib/idlelib/SearchEngine.py | 1 - Lib/idlelib/idle_test/README.txt | 37 +++++++++++-------- Lib/test/support/__init__.py | 20 +++------- Lib/test/test_codecmaps_hk.py | 1 - Lib/test/test_decimal.py | 4 +- Lib/test/test_idle.py | 4 -- Lib/test/test_imaplib.py | 1 - Lib/test/test_robotparser.py | 1 - Lib/test/test_tk.py | 10 +---- Lib/test/test_ttk_guionly.py | 10 +---- Lib/tkinter/test/runtktests.py | 1 - Misc/NEWS | 3 + 14 files changed, 37 insertions(+), 59 deletions(-) diff --git a/Lib/idlelib/FormatParagraph.py b/Lib/idlelib/FormatParagraph.py --- a/Lib/idlelib/FormatParagraph.py +++ b/Lib/idlelib/FormatParagraph.py @@ -188,7 +188,6 @@ return m.group(1) if __name__ == "__main__": - from test import support; support.use_resources = ['gui'] import unittest unittest.main('idlelib.idle_test.test_formatparagraph', verbosity=2, exit=False) diff --git a/Lib/idlelib/IdleHistory.py b/Lib/idlelib/IdleHistory.py --- a/Lib/idlelib/IdleHistory.py +++ b/Lib/idlelib/IdleHistory.py @@ -100,7 +100,5 @@ self.prefix = None if __name__ == "__main__": - from test import support - support.use_resources = ['gui'] from unittest import main main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False) diff --git a/Lib/idlelib/SearchEngine.py b/Lib/idlelib/SearchEngine.py --- a/Lib/idlelib/SearchEngine.py +++ b/Lib/idlelib/SearchEngine.py @@ -229,6 +229,5 @@ return line, col if __name__ == "__main__": - from test import support; support.use_resources = ['gui'] import unittest unittest.main('idlelib.idle_test.test_searchengine', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/README.txt b/Lib/idlelib/idle_test/README.txt --- a/Lib/idlelib/idle_test/README.txt +++ b/Lib/idlelib/idle_test/README.txt @@ -26,7 +26,6 @@ with xyz (lowercased) added after 'test_'. --- if __name__ == "__main__": - from test import support; support.use_resources = ['gui'] import unittest unittest.main('idlelib.idle_test.test_', verbosity=2, exit=False) --- @@ -34,12 +33,12 @@ 2. Gui Tests -Gui tests need 'requires' and 'use_resources' from test.support -(test.test_support in 2.7). A test is a gui test if it creates a Tk root or -master object either directly or indirectly by instantiating a tkinter or -idle class. For the benefit of buildbot machines that do not have a graphics -screen, gui tests must be 'guarded' by "requires('gui')" in a setUp -function or method. This will typically be setUpClass. +Gui tests need 'requires' from test.support (test.test_support in 2.7). A +test is a gui test if it creates a Tk root or master object either directly +or indirectly by instantiating a tkinter or idle class. For the benefit of +test processes that either have no graphical environment available or are not +allowed to use it, gui tests must be 'guarded' by "requires('gui')" in a +setUp function or method. This will typically be setUpClass. To avoid interfering with other gui tests, all gui objects must be destroyed and deleted by the end of the test. If a widget, such as a Tk root, is created @@ -57,11 +56,17 @@ del cls.root --- -Support.requires('gui') returns true if it is either called in a main module -(which never happens on buildbots) or if use_resources contains 'gui'. -Use_resources is set by test.regrtest but not by unittest. So when running -tests in another module with unittest, we set it ourselves, as in the xyz.py -template above. +Support.requires('gui') causes the test(s) it guards to be skipped if any of +a few conditions are met: + - The tests are being run by regrtest.py, and it was started without + enabling the "gui" resource with the "-u" command line option. + - The tests are being run on Windows by a service that is not allowed to + interact with the graphical environment. + - The tests are being run on Mac OSX in a process that cannot make a window + manager connection. + - tkinter.Tk cannot be successfully instantiated for some reason. + - test.support.use_resources has been set by something other than + regrtest.py and does not contain "gui". Since non-gui tests always run, but gui tests only sometimes, tests of non-gui operations should best avoid needing a gui. Methods that make incidental use of @@ -88,8 +93,8 @@ To run all idle_test/test_*.py tests, either interactively ('>>>', with unittest imported) or from a command line, use one of the -following. (Notes: unittest does not run gui tests; in 2.7, 'test ' (with the -space) is 'test.regrtest '; where present, -v and -ugui can be omitted.) +following. (Notes: in 2.7, 'test ' (with the space) is 'test.regrtest '; +where present, -v and -ugui can be omitted.) >>> unittest.main('idlelib.idle_test', verbosity=2, exit=False) python -m unittest -v idlelib.idle_test @@ -98,13 +103,13 @@ The idle tests are 'discovered' by idlelib.idle_test.__init__.load_tests, which is also imported into test.test_idle. Normally, neither file should be -changed when working on individual test modules. The third command runs runs +changed when working on individual test modules. The third command runs unittest indirectly through regrtest. The same happens when the entire test suite is run with 'python -m test'. So that command must work for buildbots to stay green. Idle tests must not disturb the environment in a way that makes other tests fail (issue 18081). To run an individual Testcase or test method, extend the dotted name given to -unittest on the command line. (But gui tests will not this way.) +unittest on the command line. python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth 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 @@ -454,23 +454,17 @@ return _is_gui_available.result def is_resource_enabled(resource): - """Test whether a resource is enabled. Known resources are set by - regrtest.py.""" - return use_resources is not None and resource in use_resources + """Test whether a resource is enabled. + + Known resources are set by regrtest.py. If not running under regrtest.py, + all resources are assumed enabled unless use_resources has been set. + """ + return use_resources is None or resource in use_resources def requires(resource, msg=None): - """Raise ResourceDenied if the specified resource is not available. - - If the caller's module is __main__ then automatically return True. The - possibility of False being returned occurs when regrtest.py is - executing. - """ + """Raise ResourceDenied if the specified resource is not available.""" if resource == 'gui' and not _is_gui_available(): raise ResourceDenied(_is_gui_available.reason) - # see if the caller's module is __main__ - if so, treat as if - # the resource was set - if sys._getframe(1).f_globals.get("__name__") == "__main__": - return if not is_resource_enabled(resource): if msg is None: msg = "Use of the %r resource not enabled" % resource diff --git a/Lib/test/test_codecmaps_hk.py b/Lib/test/test_codecmaps_hk.py --- a/Lib/test/test_codecmaps_hk.py +++ b/Lib/test/test_codecmaps_hk.py @@ -16,5 +16,4 @@ support.run_unittest(__name__) if __name__ == "__main__": - support.use_resources = ['urlfetch'] test_main() diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -5429,7 +5429,7 @@ all_tests.insert(0, CheckAttributes) -def test_main(arith=False, verbose=None, todo_tests=None, debug=None): +def test_main(arith=None, verbose=None, todo_tests=None, debug=None): """ Execute the tests. Runs all arithmetic tests if arith is True or if the "decimal" resource @@ -5439,7 +5439,7 @@ init(C) init(P) global TEST_ALL, DEBUG - TEST_ALL = arith or is_resource_enabled('decimal') + TEST_ALL = arith if arith is not None else is_resource_enabled('decimal') DEBUG = debug if todo_tests is None: diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -13,8 +13,4 @@ load_tests = idletest.load_tests if __name__ == '__main__': - # Until unittest supports resources, we emulate regrtest's -ugui - # so loaded tests run the same as if textually present here. - # If any Idle test ever needs another resource, add it to the list. - support.use_resources = ['gui'] # use_resources is initially None unittest.main(verbosity=2, exit=False) diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -501,5 +501,4 @@ if __name__ == "__main__": - support.use_resources = ['network'] unittest.main() diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -291,5 +291,4 @@ return suite if __name__=='__main__': - support.use_resources = ['network'] unittest.main() diff --git a/Lib/test/test_tk.py b/Lib/test/test_tk.py --- a/Lib/test/test_tk.py +++ b/Lib/test/test_tk.py @@ -10,15 +10,9 @@ from tkinter.test import runtktests -def test_main(enable_gui=False): - if enable_gui: - if support.use_resources is None: - support.use_resources = ['gui'] - elif 'gui' not in support.use_resources: - support.use_resources.append('gui') - +def test_main(): support.run_unittest( *runtktests.get_tests(text=False, packages=['test_tkinter'])) if __name__ == '__main__': - test_main(enable_gui=True) + test_main() diff --git a/Lib/test/test_ttk_guionly.py b/Lib/test/test_ttk_guionly.py --- a/Lib/test/test_ttk_guionly.py +++ b/Lib/test/test_ttk_guionly.py @@ -22,13 +22,7 @@ # assuming ttk is not available raise unittest.SkipTest("ttk not available: %s" % msg) -def test_main(enable_gui=False): - if enable_gui: - if support.use_resources is None: - support.use_resources = ['gui'] - elif 'gui' not in support.use_resources: - support.use_resources.append('gui') - +def test_main(): try: support.run_unittest( *runtktests.get_tests(text=False, packages=['test_ttk'])) @@ -36,4 +30,4 @@ get_tk_root().destroy() if __name__ == '__main__': - test_main(enable_gui=True) + test_main() diff --git a/Lib/tkinter/test/runtktests.py b/Lib/tkinter/test/runtktests.py --- a/Lib/tkinter/test/runtktests.py +++ b/Lib/tkinter/test/runtktests.py @@ -68,5 +68,4 @@ yield test if __name__ == "__main__": - test.support.use_resources = ['gui'] test.support.run_unittest(*get_tests()) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -66,6 +66,9 @@ Tests ----- +- Issue #18492: All resources are now allowed when tests are not run by + regrtest.py. + - Issue #21634: Fix pystone micro-benchmark: use floor division instead of true division to benchmark integers instead of floating point numbers. Set pystone version to 1.2. Patch written by Lennart Regebro. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 23:05:49 2014 From: python-checkins at python.org (zach.ware) Date: Mon, 2 Jun 2014 23:05:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2318492=3A_Merge_with_3=2E4?= Message-ID: <3gj8715Dlmz7Lkr@mail.python.org> http://hg.python.org/cpython/rev/118e427808ce changeset: 90984:118e427808ce parent: 90981:dff6b4b61cac parent: 90983:8519576b0dc5 user: Zachary Ware date: Mon Jun 02 16:04:25 2014 -0500 summary: Issue #18492: Merge with 3.4 files: Lib/idlelib/FormatParagraph.py | 1 - Lib/idlelib/IdleHistory.py | 2 - Lib/idlelib/SearchEngine.py | 1 - Lib/idlelib/idle_test/README.txt | 37 +++++++++++-------- Lib/test/support/__init__.py | 20 +++------- Lib/test/test_codecmaps_hk.py | 1 - Lib/test/test_decimal.py | 4 +- Lib/test/test_idle.py | 4 -- Lib/test/test_imaplib.py | 1 - Lib/test/test_robotparser.py | 1 - Lib/test/test_tk.py | 10 +---- Lib/test/test_ttk_guionly.py | 10 +---- Lib/tkinter/test/runtktests.py | 1 - Misc/NEWS | 3 + 14 files changed, 37 insertions(+), 59 deletions(-) diff --git a/Lib/idlelib/FormatParagraph.py b/Lib/idlelib/FormatParagraph.py --- a/Lib/idlelib/FormatParagraph.py +++ b/Lib/idlelib/FormatParagraph.py @@ -188,7 +188,6 @@ return m.group(1) if __name__ == "__main__": - from test import support; support.use_resources = ['gui'] import unittest unittest.main('idlelib.idle_test.test_formatparagraph', verbosity=2, exit=False) diff --git a/Lib/idlelib/IdleHistory.py b/Lib/idlelib/IdleHistory.py --- a/Lib/idlelib/IdleHistory.py +++ b/Lib/idlelib/IdleHistory.py @@ -100,7 +100,5 @@ self.prefix = None if __name__ == "__main__": - from test import support - support.use_resources = ['gui'] from unittest import main main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False) diff --git a/Lib/idlelib/SearchEngine.py b/Lib/idlelib/SearchEngine.py --- a/Lib/idlelib/SearchEngine.py +++ b/Lib/idlelib/SearchEngine.py @@ -229,6 +229,5 @@ return line, col if __name__ == "__main__": - from test import support; support.use_resources = ['gui'] import unittest unittest.main('idlelib.idle_test.test_searchengine', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/README.txt b/Lib/idlelib/idle_test/README.txt --- a/Lib/idlelib/idle_test/README.txt +++ b/Lib/idlelib/idle_test/README.txt @@ -26,7 +26,6 @@ with xyz (lowercased) added after 'test_'. --- if __name__ == "__main__": - from test import support; support.use_resources = ['gui'] import unittest unittest.main('idlelib.idle_test.test_', verbosity=2, exit=False) --- @@ -34,12 +33,12 @@ 2. Gui Tests -Gui tests need 'requires' and 'use_resources' from test.support -(test.test_support in 2.7). A test is a gui test if it creates a Tk root or -master object either directly or indirectly by instantiating a tkinter or -idle class. For the benefit of buildbot machines that do not have a graphics -screen, gui tests must be 'guarded' by "requires('gui')" in a setUp -function or method. This will typically be setUpClass. +Gui tests need 'requires' from test.support (test.test_support in 2.7). A +test is a gui test if it creates a Tk root or master object either directly +or indirectly by instantiating a tkinter or idle class. For the benefit of +test processes that either have no graphical environment available or are not +allowed to use it, gui tests must be 'guarded' by "requires('gui')" in a +setUp function or method. This will typically be setUpClass. To avoid interfering with other gui tests, all gui objects must be destroyed and deleted by the end of the test. If a widget, such as a Tk root, is created @@ -57,11 +56,17 @@ del cls.root --- -Support.requires('gui') returns true if it is either called in a main module -(which never happens on buildbots) or if use_resources contains 'gui'. -Use_resources is set by test.regrtest but not by unittest. So when running -tests in another module with unittest, we set it ourselves, as in the xyz.py -template above. +Support.requires('gui') causes the test(s) it guards to be skipped if any of +a few conditions are met: + - The tests are being run by regrtest.py, and it was started without + enabling the "gui" resource with the "-u" command line option. + - The tests are being run on Windows by a service that is not allowed to + interact with the graphical environment. + - The tests are being run on Mac OSX in a process that cannot make a window + manager connection. + - tkinter.Tk cannot be successfully instantiated for some reason. + - test.support.use_resources has been set by something other than + regrtest.py and does not contain "gui". Since non-gui tests always run, but gui tests only sometimes, tests of non-gui operations should best avoid needing a gui. Methods that make incidental use of @@ -88,8 +93,8 @@ To run all idle_test/test_*.py tests, either interactively ('>>>', with unittest imported) or from a command line, use one of the -following. (Notes: unittest does not run gui tests; in 2.7, 'test ' (with the -space) is 'test.regrtest '; where present, -v and -ugui can be omitted.) +following. (Notes: in 2.7, 'test ' (with the space) is 'test.regrtest '; +where present, -v and -ugui can be omitted.) >>> unittest.main('idlelib.idle_test', verbosity=2, exit=False) python -m unittest -v idlelib.idle_test @@ -98,13 +103,13 @@ The idle tests are 'discovered' by idlelib.idle_test.__init__.load_tests, which is also imported into test.test_idle. Normally, neither file should be -changed when working on individual test modules. The third command runs runs +changed when working on individual test modules. The third command runs unittest indirectly through regrtest. The same happens when the entire test suite is run with 'python -m test'. So that command must work for buildbots to stay green. Idle tests must not disturb the environment in a way that makes other tests fail (issue 18081). To run an individual Testcase or test method, extend the dotted name given to -unittest on the command line. (But gui tests will not this way.) +unittest on the command line. python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth 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 @@ -454,23 +454,17 @@ return _is_gui_available.result def is_resource_enabled(resource): - """Test whether a resource is enabled. Known resources are set by - regrtest.py.""" - return use_resources is not None and resource in use_resources + """Test whether a resource is enabled. + + Known resources are set by regrtest.py. If not running under regrtest.py, + all resources are assumed enabled unless use_resources has been set. + """ + return use_resources is None or resource in use_resources def requires(resource, msg=None): - """Raise ResourceDenied if the specified resource is not available. - - If the caller's module is __main__ then automatically return True. The - possibility of False being returned occurs when regrtest.py is - executing. - """ + """Raise ResourceDenied if the specified resource is not available.""" if resource == 'gui' and not _is_gui_available(): raise ResourceDenied(_is_gui_available.reason) - # see if the caller's module is __main__ - if so, treat as if - # the resource was set - if sys._getframe(1).f_globals.get("__name__") == "__main__": - return if not is_resource_enabled(resource): if msg is None: msg = "Use of the %r resource not enabled" % resource diff --git a/Lib/test/test_codecmaps_hk.py b/Lib/test/test_codecmaps_hk.py --- a/Lib/test/test_codecmaps_hk.py +++ b/Lib/test/test_codecmaps_hk.py @@ -16,5 +16,4 @@ support.run_unittest(__name__) if __name__ == "__main__": - support.use_resources = ['urlfetch'] test_main() diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -5556,7 +5556,7 @@ all_tests.insert(1, SignatureTest) -def test_main(arith=False, verbose=None, todo_tests=None, debug=None): +def test_main(arith=None, verbose=None, todo_tests=None, debug=None): """ Execute the tests. Runs all arithmetic tests if arith is True or if the "decimal" resource @@ -5566,7 +5566,7 @@ init(C) init(P) global TEST_ALL, DEBUG - TEST_ALL = arith or is_resource_enabled('decimal') + TEST_ALL = arith if arith is not None else is_resource_enabled('decimal') DEBUG = debug if todo_tests is None: diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -13,8 +13,4 @@ load_tests = idletest.load_tests if __name__ == '__main__': - # Until unittest supports resources, we emulate regrtest's -ugui - # so loaded tests run the same as if textually present here. - # If any Idle test ever needs another resource, add it to the list. - support.use_resources = ['gui'] # use_resources is initially None unittest.main(verbosity=2, exit=False) diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -501,5 +501,4 @@ if __name__ == "__main__": - support.use_resources = ['network'] unittest.main() diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -291,5 +291,4 @@ return suite if __name__=='__main__': - support.use_resources = ['network'] unittest.main() diff --git a/Lib/test/test_tk.py b/Lib/test/test_tk.py --- a/Lib/test/test_tk.py +++ b/Lib/test/test_tk.py @@ -10,15 +10,9 @@ from tkinter.test import runtktests -def test_main(enable_gui=False): - if enable_gui: - if support.use_resources is None: - support.use_resources = ['gui'] - elif 'gui' not in support.use_resources: - support.use_resources.append('gui') - +def test_main(): support.run_unittest( *runtktests.get_tests(text=False, packages=['test_tkinter'])) if __name__ == '__main__': - test_main(enable_gui=True) + test_main() diff --git a/Lib/test/test_ttk_guionly.py b/Lib/test/test_ttk_guionly.py --- a/Lib/test/test_ttk_guionly.py +++ b/Lib/test/test_ttk_guionly.py @@ -22,13 +22,7 @@ # assuming ttk is not available raise unittest.SkipTest("ttk not available: %s" % msg) -def test_main(enable_gui=False): - if enable_gui: - if support.use_resources is None: - support.use_resources = ['gui'] - elif 'gui' not in support.use_resources: - support.use_resources.append('gui') - +def test_main(): try: support.run_unittest( *runtktests.get_tests(text=False, packages=['test_ttk'])) @@ -36,4 +30,4 @@ get_tk_root().destroy() if __name__ == '__main__': - test_main(enable_gui=True) + test_main() diff --git a/Lib/tkinter/test/runtktests.py b/Lib/tkinter/test/runtktests.py --- a/Lib/tkinter/test/runtktests.py +++ b/Lib/tkinter/test/runtktests.py @@ -68,5 +68,4 @@ yield test if __name__ == "__main__": - test.support.use_resources = ['gui'] test.support.run_unittest(*get_tests()) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -531,6 +531,9 @@ Tests ----- +- Issue #18492: All resources are now allowed when tests are not run by + regrtest.py. + - Issue #21634: Fix pystone micro-benchmark: use floor division instead of true division to benchmark integers instead of floating point numbers. Set pystone version to 1.2. Patch written by Lennart Regebro. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 23:09:05 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 23:09:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjAx?= =?utf-8?q?=3A_Document_asyncio=2ETask=2Ecancel=28=29=2E_Initial_patch_wri?= =?utf-8?q?tten_by_Vajrasky?= Message-ID: <3gj8Bn6q16z7Lln@mail.python.org> http://hg.python.org/cpython/rev/c2384ca7fc3b changeset: 90985:c2384ca7fc3b branch: 3.4 parent: 90983:8519576b0dc5 user: Victor Stinner date: Mon Jun 02 23:06:46 2014 +0200 summary: Issue #21601: Document asyncio.Task.cancel(). Initial patch written by Vajrasky Kok. files: Doc/library/asyncio-task.rst | 22 +++++++++++++++++++++- Lib/asyncio/tasks.py | 4 ++-- 2 files changed, 23 insertions(+), 3 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 @@ -327,7 +327,27 @@ ``None`` is returned when called not in the context of a :class:`Task`. - .. method:: get_stack(self, \*, limit=None) + .. method:: cancel() + + Request this task to cancel itself. + + This arranges for a :exc:`~concurrent.futures.CancelledError` to be + thrown into the wrapped coroutine on the next cycle through the event + loop. The coroutine then has a chance to clean up or even deny the + request using try/except/finally. + + Contrary to :meth:`Future.cancel`, this does not guarantee that the task + will be cancelled: the exception might be caught and acted upon, delaying + cancellation of the task or preventing it completely. The task may also + return a value or raise a different exception. + + Immediately after this method is called, :meth:`~Future.cancelled` will + not return ``True`` (unless the task was already cancelled). A task will + be marked as cancelled when the wrapped coroutine terminates with a + :exc:`~concurrent.futures.CancelledError` exception (even if + :meth:`cancel` was not called). + + .. method:: get_stack(\*, limit=None) Return the list of stack frames for this task's coroutine. diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -269,9 +269,9 @@ print(line, file=file, end='') def cancel(self): - """Request that a task to cancel itself. + """Request this task to cancel itself. - This arranges for a CancellationError to be thrown into the + This arranges for a CancelledError to be thrown into the wrapped coroutine on the next cycle through the event loop. The coroutine then has a chance to clean up or even deny the request using try/except/finally. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 2 23:09:07 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 2 Jun 2014 23:09:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321601=3A_Document_asyncio=2ET?= =?utf-8?q?ask=2Ecancel=28=29=2E_Initial_patch_written?= Message-ID: <3gj8Bq1HLmz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/0ee47d3d2664 changeset: 90986:0ee47d3d2664 parent: 90984:118e427808ce parent: 90985:c2384ca7fc3b user: Victor Stinner date: Mon Jun 02 23:08:52 2014 +0200 summary: (Merge 3.4) Issue #21601: Document asyncio.Task.cancel(). Initial patch written by Vajrasky Kok. files: Doc/library/asyncio-task.rst | 22 +++++++++++++++++++++- Lib/asyncio/tasks.py | 4 ++-- 2 files changed, 23 insertions(+), 3 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 @@ -327,7 +327,27 @@ ``None`` is returned when called not in the context of a :class:`Task`. - .. method:: get_stack(self, \*, limit=None) + .. method:: cancel() + + Request this task to cancel itself. + + This arranges for a :exc:`~concurrent.futures.CancelledError` to be + thrown into the wrapped coroutine on the next cycle through the event + loop. The coroutine then has a chance to clean up or even deny the + request using try/except/finally. + + Contrary to :meth:`Future.cancel`, this does not guarantee that the task + will be cancelled: the exception might be caught and acted upon, delaying + cancellation of the task or preventing it completely. The task may also + return a value or raise a different exception. + + Immediately after this method is called, :meth:`~Future.cancelled` will + not return ``True`` (unless the task was already cancelled). A task will + be marked as cancelled when the wrapped coroutine terminates with a + :exc:`~concurrent.futures.CancelledError` exception (even if + :meth:`cancel` was not called). + + .. method:: get_stack(\*, limit=None) Return the list of stack frames for this task's coroutine. diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -269,9 +269,9 @@ print(line, file=file, end='') def cancel(self): - """Request that a task to cancel itself. + """Request this task to cancel itself. - This arranges for a CancellationError to be thrown into the + This arranges for a CancelledError to be thrown into the wrapped coroutine on the next cycle through the event loop. The coroutine then has a chance to clean up or even deny the request using try/except/finally. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 02:43:35 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 3 Jun 2014 02:43:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgMjE1MzM6?= =?utf-8?q?_Dicts_take_iterables=2C_not_iterators=2E_Patch_by_Wolfgang_Mai?= =?utf-8?b?ZXIu?= Message-ID: <3gjDyH2bLrz7LkS@mail.python.org> http://hg.python.org/cpython/rev/78da27d6f28f changeset: 90987:78da27d6f28f branch: 2.7 parent: 90982:eabff2a97b5b user: Terry Jan Reedy date: Mon Jun 02 20:42:48 2014 -0400 summary: Issue 21533: Dicts take iterables, not iterators. Patch by Wolfgang Maier. files: Doc/library/stdtypes.rst | 4 ++-- Misc/ACKS | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1989,8 +1989,8 @@ If no positional argument is given, an empty dictionary is created. If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, - the positional argument must be an :term:`iterator` object. Each item in - the iterable must itself be an iterator with exactly two objects. The + the positional argument must be an :term:`iterable` object. Each item in + the iterable must itself be an iterable with exactly two objects. The first object of each item becomes a key in the new dictionary, and the second object the corresponding value. If a key occurs more than once, the last value for that key becomes the corresponding value in the new diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -819,6 +819,7 @@ Nick Maclaren Don MacMillen Tomasz Ma?kowiak +Wolfgang Maier Steve Majewski Marek Majkowski Grzegorz Makarewicz -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 02:43:36 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 3 Jun 2014 02:43:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgMjE1MzM6?= =?utf-8?q?_Dicts_take_iterables=2C_not_iterators=2E_Patch_by_Wolfgang_Mai?= =?utf-8?b?ZXIu?= Message-ID: <3gjDyJ4TBCz7LkS@mail.python.org> http://hg.python.org/cpython/rev/28665dc3a696 changeset: 90988:28665dc3a696 branch: 3.4 parent: 90985:c2384ca7fc3b user: Terry Jan Reedy date: Mon Jun 02 20:42:56 2014 -0400 summary: Issue 21533: Dicts take iterables, not iterators. Patch by Wolfgang Maier. files: Doc/library/stdtypes.rst | 4 ++-- Misc/ACKS | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3031,8 +3031,8 @@ If no positional argument is given, an empty dictionary is created. If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, - the positional argument must be an :term:`iterator` object. Each item in - the iterable must itself be an iterator with exactly two objects. The + the positional argument must be an :term:`iterable` object. Each item in + the iterable must itself be an iterable with exactly two objects. The first object of each item becomes a key in the new dictionary, and the second object the corresponding value. If a key occurs more than once, the last value for that key becomes the corresponding value in the new diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -818,6 +818,7 @@ Nick Maclaren Don MacMillen Tomasz Ma?kowiak +Wolfgang Maier Steve Majewski Marek Majkowski Grzegorz Makarewicz -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 02:43:37 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 3 Jun 2014 02:43:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gjDyK60pNz7LkS@mail.python.org> http://hg.python.org/cpython/rev/f59afe34fe50 changeset: 90989:f59afe34fe50 parent: 90986:0ee47d3d2664 parent: 90988:28665dc3a696 user: Terry Jan Reedy date: Mon Jun 02 20:43:13 2014 -0400 summary: Merge with 3.4 files: Doc/library/stdtypes.rst | 4 ++-- Misc/ACKS | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3031,8 +3031,8 @@ If no positional argument is given, an empty dictionary is created. If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, - the positional argument must be an :term:`iterator` object. Each item in - the iterable must itself be an iterator with exactly two objects. The + the positional argument must be an :term:`iterable` object. Each item in + the iterable must itself be an iterable with exactly two objects. The first object of each item becomes a key in the new dictionary, and the second object the corresponding value. If a key occurs more than once, the last value for that key becomes the corresponding value in the new diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -824,6 +824,7 @@ Nick Maclaren Don MacMillen Tomasz Ma?kowiak +Wolfgang Maier Steve Majewski Marek Majkowski Grzegorz Makarewicz -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 08:00:51 2014 From: python-checkins at python.org (senthil.kumaran) Date: Tue, 3 Jun 2014 08:00:51 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Update_referen?= =?utf-8?q?ces_to_Python_docs_in_the_sidebar_index=2E?= Message-ID: <3gjN0M6v3yz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/b8655be522d4 changeset: 90990:b8655be522d4 branch: 2.7 parent: 90987:78da27d6f28f user: Senthil Kumaran date: Mon Jun 02 22:57:07 2014 -0700 summary: Update references to Python docs in the sidebar index. Patch contributed by Auke Willem Oosterhoff. files: Doc/tools/sphinxext/indexsidebar.html | 32 +++++++------- 1 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Doc/tools/sphinxext/indexsidebar.html b/Doc/tools/sphinxext/indexsidebar.html --- a/Doc/tools/sphinxext/indexsidebar.html +++ b/Doc/tools/sphinxext/indexsidebar.html @@ -1,17 +1,17 @@ -

Download

-

Download these documents

-

Docs for other versions

- +

Download

+

Download these documents

+

Docs for other versions

+ -

Other resources

- +

Other resources

+ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 08:00:53 2014 From: python-checkins at python.org (senthil.kumaran) Date: Tue, 3 Jun 2014 08:00:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Update_referen?= =?utf-8?q?ces_to_Python_docs_in_the_sidebar_index=2E?= Message-ID: <3gjN0P1ysQz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/e6dce5611dae changeset: 90991:e6dce5611dae branch: 3.4 parent: 90988:28665dc3a696 user: Senthil Kumaran date: Mon Jun 02 22:58:13 2014 -0700 summary: Update references to Python docs in the sidebar index. Patch contributed by Auke Willem Oosterhoff. files: Doc/tools/sphinxext/indexsidebar.html | 33 +++++++------- 1 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Doc/tools/sphinxext/indexsidebar.html b/Doc/tools/sphinxext/indexsidebar.html --- a/Doc/tools/sphinxext/indexsidebar.html +++ b/Doc/tools/sphinxext/indexsidebar.html @@ -1,17 +1,18 @@ -

Download

-

Download these documents

-

Docs for other versions

- +

Download

+

Download these documents

+

Docs for other versions

+ -

Other resources

- +

Other resources

+ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 08:00:54 2014 From: python-checkins at python.org (senthil.kumaran) Date: Tue, 3 Jun 2014 08:00:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E4?= Message-ID: <3gjN0Q3Nzkz7Lm0@mail.python.org> http://hg.python.org/cpython/rev/50c9df76bb77 changeset: 90992:50c9df76bb77 parent: 90989:f59afe34fe50 parent: 90991:e6dce5611dae user: Senthil Kumaran date: Mon Jun 02 23:00:43 2014 -0700 summary: merge from 3.4 Update references to Python docs in the sidebar index. Patch contributed by Auke Willem Oosterhoff. files: Doc/tools/sphinxext/indexsidebar.html | 32 +++++++------- 1 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Doc/tools/sphinxext/indexsidebar.html b/Doc/tools/sphinxext/indexsidebar.html --- a/Doc/tools/sphinxext/indexsidebar.html +++ b/Doc/tools/sphinxext/indexsidebar.html @@ -1,17 +1,17 @@ -

Download

-

Download these documents

-

Docs for other versions

- +

Download

+

Download these documents

+

Docs for other versions

+ -

Other resources

- +

Other resources

+ -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Tue Jun 3 09:54:07 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 03 Jun 2014 09:54:07 +0200 Subject: [Python-checkins] Daily reference leaks (f59afe34fe50): sum=4 Message-ID: results for f59afe34fe50 on branch "default" -------------------------------------------- test_asyncio leaked [0, 4, 0] memory blocks, sum=4 test_collections leaked [2, -4, 0] references, sum=-2 test_collections leaked [1, -2, 0] memory blocks, sum=-1 test_functools leaked [0, 0, 3] memory blocks, sum=3 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/refloglOQM9f', '-x'] From python-checkins at python.org Tue Jun 3 16:25:49 2014 From: python-checkins at python.org (senthil.kumaran) Date: Tue, 3 Jun 2014 16:25:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogRml4IGlzc3VlICMy?= =?utf-8?q?1641=3A_Close_the_socket_before_raising_the_SMTPResponseExcepti?= =?utf-8?b?b24u?= Message-ID: <3gjbC1460qz7N9m@mail.python.org> http://hg.python.org/cpython/rev/d5c76646168d changeset: 90993:d5c76646168d branch: 3.4 parent: 90991:e6dce5611dae user: Senthil Kumaran date: Tue Jun 03 07:24:54 2014 -0700 summary: Fix issue #21641: Close the socket before raising the SMTPResponseException. Fixes the ResourceWarning in the test run. Patch by Claudiu.Popa. files: Lib/smtplib.py | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Lib/smtplib.py b/Lib/smtplib.py --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -377,6 +377,7 @@ if self.debuglevel > 0: print('reply:', repr(line), file=stderr) if len(line) > _MAXLINE: + self.close() raise SMTPResponseException(500, "Line too long.") resp.append(line[4:].strip(b' \t\r\n')) code = line[:3] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 16:25:50 2014 From: python-checkins at python.org (senthil.kumaran) Date: Tue, 3 Jun 2014 16:25:50 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E4?= Message-ID: <3gjbC25nbCz7NBw@mail.python.org> http://hg.python.org/cpython/rev/7ea84a25d863 changeset: 90994:7ea84a25d863 parent: 90992:50c9df76bb77 parent: 90993:d5c76646168d user: Senthil Kumaran date: Tue Jun 03 07:25:40 2014 -0700 summary: merge from 3.4 Fix issue #21641: Close the socket before raising the SMTPResponseException. Fixes the ResourceWarning in the test run. Patch by Claudiu.Popa. files: Lib/smtplib.py | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Lib/smtplib.py b/Lib/smtplib.py --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -377,6 +377,7 @@ if self.debuglevel > 0: print('reply:', repr(line), file=stderr) if len(line) > _MAXLINE: + self.close() raise SMTPResponseException(500, "Line too long.") resp.append(line[4:].strip(b' \t\r\n')) code = line[:3] -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 16:35:02 2014 From: python-checkins at python.org (zach.ware) Date: Tue, 3 Jun 2014 16:35:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNDM5?= =?utf-8?q?=3A_Fix_a_couple_of_typos=2E?= Message-ID: <3gjbPf19mZz7Ljk@mail.python.org> http://hg.python.org/cpython/rev/be8492101251 changeset: 90995:be8492101251 branch: 3.4 parent: 90993:d5c76646168d user: Zachary Ware date: Tue Jun 03 09:32:40 2014 -0500 summary: Issue #21439: Fix a couple of typos. files: Doc/reference/compound_stmts.rst | 2 +- Doc/reference/expressions.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -180,7 +180,7 @@ for i in range(10): print(i) i = 5 # this will not affect the for-loop - # be i will be overwritten with the next + # because i will be overwritten with the next # index in the range diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -522,7 +522,7 @@ The primary must evaluate to an object of a type that supports attribute references, which most objects do. This object is then asked to produce the attribute whose name is the identifier. This production can be customized by -overriding the :meth:`__getattr__` method). If this attribute is not available, +overriding the :meth:`__getattr__` method. If this attribute is not available, the exception :exc:`AttributeError` is raised. Otherwise, the type and value of the object produced is determined by the object. Multiple evaluations of the same attribute reference may yield different objects. @@ -1245,7 +1245,7 @@ lambda_expr: "lambda" [`parameter_list`]: `expression` lambda_expr_nocond: "lambda" [`parameter_list`]: `expression_nocond` -Lambda expressions (sometimes called lambda forms) are create anonymous +Lambda expressions (sometimes called lambda forms) are used to create anonymous functions. The expression ``lambda arguments: expression`` yields a function object. The unnamed object behaves like a function object defined with :: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 16:35:03 2014 From: python-checkins at python.org (zach.ware) Date: Tue, 3 Jun 2014 16:35:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321439=3A_Merge_with_3=2E4?= Message-ID: <3gjbPg2l2rz7N9F@mail.python.org> http://hg.python.org/cpython/rev/99b469758f49 changeset: 90996:99b469758f49 parent: 90994:7ea84a25d863 parent: 90995:be8492101251 user: Zachary Ware date: Tue Jun 03 09:34:39 2014 -0500 summary: Issue #21439: Merge with 3.4 files: Doc/reference/compound_stmts.rst | 2 +- Doc/reference/expressions.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -180,7 +180,7 @@ for i in range(10): print(i) i = 5 # this will not affect the for-loop - # be i will be overwritten with the next + # because i will be overwritten with the next # index in the range diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -522,7 +522,7 @@ The primary must evaluate to an object of a type that supports attribute references, which most objects do. This object is then asked to produce the attribute whose name is the identifier. This production can be customized by -overriding the :meth:`__getattr__` method). If this attribute is not available, +overriding the :meth:`__getattr__` method. If this attribute is not available, the exception :exc:`AttributeError` is raised. Otherwise, the type and value of the object produced is determined by the object. Multiple evaluations of the same attribute reference may yield different objects. @@ -1253,7 +1253,7 @@ lambda_expr: "lambda" [`parameter_list`]: `expression` lambda_expr_nocond: "lambda" [`parameter_list`]: `expression_nocond` -Lambda expressions (sometimes called lambda forms) are create anonymous +Lambda expressions (sometimes called lambda forms) are used to create anonymous functions. The expression ``lambda arguments: expression`` yields a function object. The unnamed object behaves like a function object defined with :: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 17:49:26 2014 From: python-checkins at python.org (vinay.sajip) Date: Tue, 3 Jun 2014 17:49:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjQz?= =?utf-8?q?=3A_Updated_test_and_fixed_logic_bug_in_lib64_symlink_creation?= =?utf-8?q?=2E?= Message-ID: <3gjd3V3xXfz7MmQ@mail.python.org> http://hg.python.org/cpython/rev/27e1b4a9de07 changeset: 90997:27e1b4a9de07 branch: 3.4 parent: 90995:be8492101251 user: Vinay Sajip date: Tue Jun 03 16:47:51 2014 +0100 summary: Issue #21643: Updated test and fixed logic bug in lib64 symlink creation. files: Lib/test/test_venv.py | 27 ++++++++++++++++----------- Lib/venv/__init__.py | 6 +++--- 2 files changed, 19 insertions(+), 14 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 @@ -203,17 +203,22 @@ """ Test upgrading an existing environment directory. """ - builder = venv.EnvBuilder(upgrade=True) - self.run_with_capture(builder.create, self.env_dir) - self.isdir(self.bindir) - self.isdir(self.include) - self.isdir(*self.lib) - fn = self.get_env_file(self.bindir, self.exe) - if not os.path.exists(fn): # diagnostics for Windows buildbot failures - bd = self.get_env_file(self.bindir) - print('Contents of %r:' % bd) - print(' %r' % os.listdir(bd)) - self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn) + # See Issue #21643: the loop needs to run twice to ensure + # that everything works on the upgrade (the first run just creates + # the venv). + for upgrade in (False, True): + builder = venv.EnvBuilder(upgrade=upgrade) + self.run_with_capture(builder.create, self.env_dir) + self.isdir(self.bindir) + self.isdir(self.include) + self.isdir(*self.lib) + fn = self.get_env_file(self.bindir, self.exe) + if not os.path.exists(fn): + # diagnostics for Windows buildbot failures + bd = self.get_env_file(self.bindir) + print('Contents of %r:' % bd) + print(' %r' % os.listdir(bd)) + self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn) def test_isolation(self): """ diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -30,7 +30,6 @@ import logging import os import shutil -import struct import subprocess import sys import types @@ -140,11 +139,12 @@ create_if_needed(path) create_if_needed(libpath) # Issue 21197: create lib64 as a symlink to lib on 64-bit non-OS X POSIX - if ((struct.calcsize('P') == 8) and (os.name == 'posix') and + if ((sys.maxsize > 2**32) and (os.name == 'posix') and (sys.platform != 'darwin')): p = os.path.join(env_dir, 'lib') link_path = os.path.join(env_dir, 'lib64') - os.symlink(p, link_path) + if not os.path.exists(link_path): # Issue #21643 + os.symlink(p, link_path) context.bin_path = binpath = os.path.join(env_dir, binname) context.bin_name = binname context.env_exe = os.path.join(binpath, exename) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 17:49:27 2014 From: python-checkins at python.org (vinay.sajip) Date: Tue, 3 Jun 2014 17:49:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Closes_=2321643=3A_Merged_fix_from_3=2E4=2E?= Message-ID: <3gjd3W5JZQz7LmF@mail.python.org> http://hg.python.org/cpython/rev/71eda9bd8875 changeset: 90998:71eda9bd8875 parent: 90996:99b469758f49 parent: 90997:27e1b4a9de07 user: Vinay Sajip date: Tue Jun 03 16:48:39 2014 +0100 summary: Closes #21643: Merged fix from 3.4. files: Lib/test/test_venv.py | 27 ++++++++++++++++----------- Lib/venv/__init__.py | 6 +++--- 2 files changed, 19 insertions(+), 14 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 @@ -203,17 +203,22 @@ """ Test upgrading an existing environment directory. """ - builder = venv.EnvBuilder(upgrade=True) - self.run_with_capture(builder.create, self.env_dir) - self.isdir(self.bindir) - self.isdir(self.include) - self.isdir(*self.lib) - fn = self.get_env_file(self.bindir, self.exe) - if not os.path.exists(fn): # diagnostics for Windows buildbot failures - bd = self.get_env_file(self.bindir) - print('Contents of %r:' % bd) - print(' %r' % os.listdir(bd)) - self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn) + # See Issue #21643: the loop needs to run twice to ensure + # that everything works on the upgrade (the first run just creates + # the venv). + for upgrade in (False, True): + builder = venv.EnvBuilder(upgrade=upgrade) + self.run_with_capture(builder.create, self.env_dir) + self.isdir(self.bindir) + self.isdir(self.include) + self.isdir(*self.lib) + fn = self.get_env_file(self.bindir, self.exe) + if not os.path.exists(fn): + # diagnostics for Windows buildbot failures + bd = self.get_env_file(self.bindir) + print('Contents of %r:' % bd) + print(' %r' % os.listdir(bd)) + self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn) def test_isolation(self): """ diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -30,7 +30,6 @@ import logging import os import shutil -import struct import subprocess import sys import types @@ -140,11 +139,12 @@ create_if_needed(path) create_if_needed(libpath) # Issue 21197: create lib64 as a symlink to lib on 64-bit non-OS X POSIX - if ((struct.calcsize('P') == 8) and (os.name == 'posix') and + if ((sys.maxsize > 2**32) and (os.name == 'posix') and (sys.platform != 'darwin')): p = os.path.join(env_dir, 'lib') link_path = os.path.join(env_dir, 'lib64') - os.symlink(p, link_path) + if not os.path.exists(link_path): # Issue #21643 + os.symlink(p, link_path) context.bin_path = binpath = os.path.join(env_dir, binname) context.bin_name = binname context.env_exe = os.path.join(binpath, exename) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 3 18:45:15 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 3 Jun 2014 18:45:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_All_modern_compilers_provi?= =?utf-8?q?de_a_offsetof=28=29_function?= Message-ID: <3gjfHv6lPvz7N9v@mail.python.org> http://hg.python.org/cpython/rev/88814d1f8c32 changeset: 90999:88814d1f8c32 user: Victor Stinner date: Tue Jun 03 18:45:05 2014 +0200 summary: All modern compilers provide a offsetof() function offsetof() is used directly in many other .c files without any issue. files: Modules/socketmodule.c | 4 ---- 1 files changed, 0 insertions(+), 4 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -288,10 +288,6 @@ #include -#ifndef offsetof -# define offsetof(type, member) ((size_t)(&((type *)0)->member)) -#endif - #ifndef O_NONBLOCK # define O_NONBLOCK O_NDELAY #endif -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 00:16:54 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 00:16:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxMTE5?= =?utf-8?q?=3A_asyncio_now_closes_sockets_on_errors?= Message-ID: <3gjnfZ52scz7NB0@mail.python.org> http://hg.python.org/cpython/rev/d0dd3eb5b5ef changeset: 91000:d0dd3eb5b5ef branch: 3.4 parent: 90997:27e1b4a9de07 user: Victor Stinner date: Wed Jun 04 00:11:52 2014 +0200 summary: Issue #21119: asyncio now closes sockets on errors Fix ResourceWarning: create_connection(), create_datagram_endpoint() and create_unix_server() methods of event loop now close the newly created socket on error. files: Lib/asyncio/base_events.py | 8 ++++ Lib/asyncio/unix_events.py | 3 + Lib/test/test_asyncio/test_base_events.py | 21 +++++++++++ Lib/test/test_asyncio/test_unix_events.py | 18 +++++++++ 4 files changed, 50 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 @@ -412,6 +412,10 @@ if sock is not None: sock.close() exceptions.append(exc) + except: + if sock is not None: + sock.close() + raise else: break else: @@ -512,6 +516,10 @@ if sock is not None: sock.close() exceptions.append(exc) + except: + if sock is not None: + sock.close() + raise else: break else: 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 @@ -223,6 +223,9 @@ raise OSError(errno.EADDRINUSE, msg) from None else: raise + except: + sock.close() + raise else: if sock is None: raise ValueError( 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 @@ -583,6 +583,27 @@ self.assertEqual(str(cm.exception), 'Multiple exceptions: err1, err2') + @mock.patch('asyncio.base_events.socket') + def test_create_connection_timeout(self, m_socket): + # Ensure that the socket is closed on timeout + sock = mock.Mock() + m_socket.socket.return_value = sock + + def getaddrinfo(*args, **kw): + fut = asyncio.Future(loop=self.loop) + addr = (socket.AF_INET, socket.SOCK_STREAM, 0, '', + ('127.0.0.1', 80)) + fut.set_result([addr]) + return fut + self.loop.getaddrinfo = getaddrinfo + + with mock.patch.object(self.loop, 'sock_connect', + side_effect=asyncio.TimeoutError): + coro = self.loop.create_connection(MyProto, '127.0.0.1', 80) + with self.assertRaises(asyncio.TimeoutError) as cm: + self.loop.run_until_complete(coro) + self.assertTrue(sock.close.called) + def test_create_connection_host_port_sock(self): coro = self.loop.create_connection( MyProto, 'example.com', 80, sock=object()) diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -256,6 +256,24 @@ 'A UNIX Domain Socket was expected'): self.loop.run_until_complete(coro) + @mock.patch('asyncio.unix_events.socket') + def test_create_unix_server_bind_error(self, m_socket): + # Ensure that the socket is closed on any bind error + sock = mock.Mock() + m_socket.socket.return_value = sock + + sock.bind.side_effect = OSError + coro = self.loop.create_unix_server(lambda: None, path="/test") + with self.assertRaises(OSError): + self.loop.run_until_complete(coro) + self.assertTrue(sock.close.called) + + sock.bind.side_effect = MemoryError + coro = self.loop.create_unix_server(lambda: None, path="/test") + with self.assertRaises(MemoryError): + self.loop.run_until_complete(coro) + self.assertTrue(sock.close.called) + def test_create_unix_connection_path_sock(self): coro = self.loop.create_unix_connection( lambda: None, '/dev/null', sock=object()) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 00:16:55 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 00:16:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxMTE5?= =?utf-8?q?=3A_asyncio=3A_Make_sure_that_socketpair=28=29_close_sockets_on?= =?utf-8?q?_error?= Message-ID: <3gjnfb6k1Wz7NB2@mail.python.org> http://hg.python.org/cpython/rev/bbd773ed9584 changeset: 91001:bbd773ed9584 branch: 3.4 user: Victor Stinner date: Wed Jun 04 00:12:28 2014 +0200 summary: Issue #21119: asyncio: Make sure that socketpair() close sockets on error Close the listening socket if sock.bind() raises an exception. files: Lib/asyncio/windows_utils.py | 32 +++++---- Lib/test/test_asyncio/test_windows_utils.py | 9 ++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/Lib/asyncio/windows_utils.py b/Lib/asyncio/windows_utils.py --- a/Lib/asyncio/windows_utils.py +++ b/Lib/asyncio/windows_utils.py @@ -51,23 +51,25 @@ # We create a connected TCP socket. Note the trick with setblocking(0) # that prevents us from having to create a thread. lsock = socket.socket(family, type, proto) - lsock.bind((host, 0)) - lsock.listen(1) - # On IPv6, ignore flow_info and scope_id - addr, port = lsock.getsockname()[:2] - csock = socket.socket(family, type, proto) - csock.setblocking(False) try: - csock.connect((addr, port)) - except (BlockingIOError, InterruptedError): - pass - except Exception: + lsock.bind((host, 0)) + lsock.listen(1) + # On IPv6, ignore flow_info and scope_id + addr, port = lsock.getsockname()[:2] + csock = socket.socket(family, type, proto) + try: + csock.setblocking(False) + try: + csock.connect((addr, port)) + except (BlockingIOError, InterruptedError): + pass + ssock, _ = lsock.accept() + csock.setblocking(True) + except: + csock.close() + raise + finally: lsock.close() - csock.close() - raise - ssock, _ = lsock.accept() - csock.setblocking(True) - lsock.close() return (ssock, csock) diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py --- a/Lib/test/test_asyncio/test_windows_utils.py +++ b/Lib/test/test_asyncio/test_windows_utils.py @@ -51,6 +51,15 @@ self.assertRaises(ValueError, windows_utils.socketpair, proto=1) + @mock.patch('asyncio.windows_utils.socket') + def test_winsocketpair_close(self, m_socket): + m_socket.AF_INET = socket.AF_INET + m_socket.SOCK_STREAM = socket.SOCK_STREAM + sock = mock.Mock() + m_socket.socket.return_value = sock + sock.bind.side_effect = OSError + self.assertRaises(OSError, windows_utils.socketpair) + self.assertTrue(sock.close.called) class PipeTests(unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 00:16:57 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 00:16:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_3=2E4=3A_Issue_=2321119=2C_fix_ResourceWarning_in_?= =?utf-8?q?asyncio?= Message-ID: <3gjnfd2Bzhz7NC0@mail.python.org> http://hg.python.org/cpython/rev/8b40483d9a08 changeset: 91002:8b40483d9a08 parent: 90999:88814d1f8c32 parent: 91001:bbd773ed9584 user: Victor Stinner date: Wed Jun 04 00:13:31 2014 +0200 summary: Merge 3.4: Issue #21119, fix ResourceWarning in asyncio * Make sure that socketpair() close sockets on error. Close the listening socket if sock.bind() raises an exception. * asyncio now closes sockets on errors. Fix ResourceWarning: create_connection(), create_datagram_endpoint() and create_unix_server() methods of event loop now close the newly created socket on error. files: Lib/asyncio/base_events.py | 8 ++ Lib/asyncio/unix_events.py | 3 + Lib/asyncio/windows_utils.py | 32 +++++---- Lib/test/test_asyncio/test_base_events.py | 21 ++++++ Lib/test/test_asyncio/test_unix_events.py | 18 +++++ Lib/test/test_asyncio/test_windows_utils.py | 9 ++ 6 files changed, 76 insertions(+), 15 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 @@ -412,6 +412,10 @@ if sock is not None: sock.close() exceptions.append(exc) + except: + if sock is not None: + sock.close() + raise else: break else: @@ -512,6 +516,10 @@ if sock is not None: sock.close() exceptions.append(exc) + except: + if sock is not None: + sock.close() + raise else: break else: 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 @@ -223,6 +223,9 @@ raise OSError(errno.EADDRINUSE, msg) from None else: raise + except: + sock.close() + raise else: if sock is None: raise ValueError( diff --git a/Lib/asyncio/windows_utils.py b/Lib/asyncio/windows_utils.py --- a/Lib/asyncio/windows_utils.py +++ b/Lib/asyncio/windows_utils.py @@ -51,23 +51,25 @@ # We create a connected TCP socket. Note the trick with setblocking(0) # that prevents us from having to create a thread. lsock = socket.socket(family, type, proto) - lsock.bind((host, 0)) - lsock.listen(1) - # On IPv6, ignore flow_info and scope_id - addr, port = lsock.getsockname()[:2] - csock = socket.socket(family, type, proto) - csock.setblocking(False) try: - csock.connect((addr, port)) - except (BlockingIOError, InterruptedError): - pass - except Exception: + lsock.bind((host, 0)) + lsock.listen(1) + # On IPv6, ignore flow_info and scope_id + addr, port = lsock.getsockname()[:2] + csock = socket.socket(family, type, proto) + try: + csock.setblocking(False) + try: + csock.connect((addr, port)) + except (BlockingIOError, InterruptedError): + pass + ssock, _ = lsock.accept() + csock.setblocking(True) + except: + csock.close() + raise + finally: lsock.close() - csock.close() - raise - ssock, _ = lsock.accept() - csock.setblocking(True) - lsock.close() return (ssock, csock) 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 @@ -583,6 +583,27 @@ self.assertEqual(str(cm.exception), 'Multiple exceptions: err1, err2') + @mock.patch('asyncio.base_events.socket') + def test_create_connection_timeout(self, m_socket): + # Ensure that the socket is closed on timeout + sock = mock.Mock() + m_socket.socket.return_value = sock + + def getaddrinfo(*args, **kw): + fut = asyncio.Future(loop=self.loop) + addr = (socket.AF_INET, socket.SOCK_STREAM, 0, '', + ('127.0.0.1', 80)) + fut.set_result([addr]) + return fut + self.loop.getaddrinfo = getaddrinfo + + with mock.patch.object(self.loop, 'sock_connect', + side_effect=asyncio.TimeoutError): + coro = self.loop.create_connection(MyProto, '127.0.0.1', 80) + with self.assertRaises(asyncio.TimeoutError) as cm: + self.loop.run_until_complete(coro) + self.assertTrue(sock.close.called) + def test_create_connection_host_port_sock(self): coro = self.loop.create_connection( MyProto, 'example.com', 80, sock=object()) diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -256,6 +256,24 @@ 'A UNIX Domain Socket was expected'): self.loop.run_until_complete(coro) + @mock.patch('asyncio.unix_events.socket') + def test_create_unix_server_bind_error(self, m_socket): + # Ensure that the socket is closed on any bind error + sock = mock.Mock() + m_socket.socket.return_value = sock + + sock.bind.side_effect = OSError + coro = self.loop.create_unix_server(lambda: None, path="/test") + with self.assertRaises(OSError): + self.loop.run_until_complete(coro) + self.assertTrue(sock.close.called) + + sock.bind.side_effect = MemoryError + coro = self.loop.create_unix_server(lambda: None, path="/test") + with self.assertRaises(MemoryError): + self.loop.run_until_complete(coro) + self.assertTrue(sock.close.called) + def test_create_unix_connection_path_sock(self): coro = self.loop.create_unix_connection( lambda: None, '/dev/null', sock=object()) diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py --- a/Lib/test/test_asyncio/test_windows_utils.py +++ b/Lib/test/test_asyncio/test_windows_utils.py @@ -51,6 +51,15 @@ self.assertRaises(ValueError, windows_utils.socketpair, proto=1) + @mock.patch('asyncio.windows_utils.socket') + def test_winsocketpair_close(self, m_socket): + m_socket.AF_INET = socket.AF_INET + m_socket.SOCK_STREAM = socket.SOCK_STREAM + sock = mock.Mock() + m_socket.socket.return_value = sock + sock.bind.side_effect = OSError + self.assertRaises(OSError, windows_utils.socketpair) + self.assertTrue(sock.close.called) class PipeTests(unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 00:20:29 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 00:20:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_cleanup_test?= =?utf-8?q?=5Fasyncio/test=5Fbase=5Fevents=2Epy=3A_cm_variable_was_unused?= Message-ID: <3gjnkj0J58z7LlP@mail.python.org> http://hg.python.org/cpython/rev/ebd5af6ed37c changeset: 91003:ebd5af6ed37c branch: 3.4 parent: 91001:bbd773ed9584 user: Victor Stinner date: Wed Jun 04 00:18:41 2014 +0200 summary: cleanup test_asyncio/test_base_events.py: cm variable was unused files: Lib/test/test_asyncio/test_base_events.py | 2 +- 1 files changed, 1 insertions(+), 1 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 @@ -600,7 +600,7 @@ with mock.patch.object(self.loop, 'sock_connect', side_effect=asyncio.TimeoutError): coro = self.loop.create_connection(MyProto, '127.0.0.1', 80) - with self.assertRaises(asyncio.TimeoutError) as cm: + with self.assertRaises(asyncio.TimeoutError): self.loop.run_until_complete(coro) self.assertTrue(sock.close.called) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 00:20:30 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 00:20:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_cleanup_test=5Fasyncio/test=5Fbase=5Fe?= =?utf-8?q?vents=2Epy=3A_cm_variable_was_unused?= Message-ID: <3gjnkk2H34z7NBB@mail.python.org> http://hg.python.org/cpython/rev/e7c438963c63 changeset: 91004:e7c438963c63 parent: 91002:8b40483d9a08 parent: 91003:ebd5af6ed37c user: Victor Stinner date: Wed Jun 04 00:19:21 2014 +0200 summary: (Merge 3.4) cleanup test_asyncio/test_base_events.py: cm variable was unused files: Lib/test/test_asyncio/test_base_events.py | 2 +- 1 files changed, 1 insertions(+), 1 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 @@ -600,7 +600,7 @@ with mock.patch.object(self.loop, 'sock_connect', side_effect=asyncio.TimeoutError): coro = self.loop.create_connection(MyProto, '127.0.0.1', 80) - with self.assertRaises(asyncio.TimeoutError) as cm: + with self.assertRaises(asyncio.TimeoutError): self.loop.run_until_complete(coro) self.assertTrue(sock.close.called) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 00:29:12 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 00:29:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjUx?= =?utf-8?q?=3A_Fix_ResourceWarning_when_running_asyncio_tests_on_Windows?= =?utf-8?q?=2E?= Message-ID: <3gjnwm6qjlz7N9C@mail.python.org> http://hg.python.org/cpython/rev/9d2c0b41c1d5 changeset: 91005:9d2c0b41c1d5 branch: 3.4 parent: 91003:ebd5af6ed37c user: Victor Stinner date: Wed Jun 04 00:23:26 2014 +0200 summary: Issue #21651: Fix ResourceWarning when running asyncio tests on Windows. Patch written by Claudiu Popa. files: Lib/test/test_asyncio/test_events.py | 1 + 1 files changed, 1 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 @@ -1070,6 +1070,7 @@ def test_internal_fds(self): loop = self.create_event_loop() if not isinstance(loop, selector_events.BaseSelectorEventLoop): + loop.close() self.skipTest('loop is not a BaseSelectorEventLoop') self.assertEqual(1, loop._internal_fds) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 00:29:14 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 00:29:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321651=3A_Fix_ResourceWarning_?= =?utf-8?q?when_running_asyncio_tests_on?= Message-ID: <3gjnwp168tz7NB0@mail.python.org> http://hg.python.org/cpython/rev/b2f329e9cd18 changeset: 91006:b2f329e9cd18 parent: 91004:e7c438963c63 parent: 91005:9d2c0b41c1d5 user: Victor Stinner date: Wed Jun 04 00:23:43 2014 +0200 summary: (Merge 3.4) Issue #21651: Fix ResourceWarning when running asyncio tests on Windows. Patch written by Claudiu Popa. files: Lib/test/test_asyncio/test_events.py | 1 + 1 files changed, 1 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 @@ -1070,6 +1070,7 @@ def test_internal_fds(self): loop = self.create_event_loop() if not isinstance(loop, selector_events.BaseSelectorEventLoop): + loop.close() self.skipTest('loop is not a BaseSelectorEventLoop') self.assertEqual(1, loop._internal_fds) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 00:42:43 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 00:42:43 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fix_asyncio_te?= =?utf-8?q?sts_on_Windows=3A_wait_for_the_subprocess_exit?= Message-ID: <3gjpDM20MHz7NC4@mail.python.org> http://hg.python.org/cpython/rev/c5186045395e changeset: 91007:c5186045395e branch: 3.4 parent: 91005:9d2c0b41c1d5 user: Victor Stinner date: Wed Jun 04 00:42:04 2014 +0200 summary: Fix asyncio tests on Windows: wait for the subprocess exit Before, regrtest failed to remove the temporary test directory because the process was still running in this directory. files: Lib/test/test_asyncio/test_windows_utils.py | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py --- a/Lib/test/test_asyncio/test_windows_utils.py +++ b/Lib/test/test_asyncio/test_windows_utils.py @@ -164,6 +164,8 @@ self.assertTrue(msg.upper().rstrip().startswith(out)) self.assertTrue(b"stderr".startswith(err)) + p.wait() + if __name__ == '__main__': unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 00:42:44 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 00:42:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Fix_asyncio_tests_on_Windows=3A_wait_f?= =?utf-8?q?or_the_subprocess_exit?= Message-ID: <3gjpDN3pCPz7NBr@mail.python.org> http://hg.python.org/cpython/rev/5f2cfafe6182 changeset: 91008:5f2cfafe6182 parent: 91006:b2f329e9cd18 parent: 91007:c5186045395e user: Victor Stinner date: Wed Jun 04 00:42:17 2014 +0200 summary: (Merge 3.4) Fix asyncio tests on Windows: wait for the subprocess exit Before, regrtest failed to remove the temporary test directory because the process was still running in this directory. files: Lib/test/test_asyncio/test_windows_utils.py | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py --- a/Lib/test/test_asyncio/test_windows_utils.py +++ b/Lib/test/test_asyncio/test_windows_utils.py @@ -164,6 +164,8 @@ self.assertTrue(msg.upper().rstrip().startswith(out)) self.assertTrue(b"stderr".startswith(err)) + p.wait() + if __name__ == '__main__': unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 01:08:04 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 4 Jun 2014 01:08:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321326=3A_Add_asyn?= =?utf-8?q?cio=2EBaseEventLoop=2Eis=5Fclosed=28=29_method?= Message-ID: <3gjpnc35Ktz7N9C@mail.python.org> http://hg.python.org/cpython/rev/690b6ddeee9c changeset: 91009:690b6ddeee9c user: Victor Stinner date: Wed Jun 04 01:06:24 2014 +0200 summary: Issue #21326: Add asyncio.BaseEventLoop.is_closed() method Add BaseEventLoop._closed attribute and use it to check if the event loop was closed or not, instead of checking different attributes in each subclass of BaseEventLoop. run_forever() and run_until_complete() now raises a RuntimeError('Event loop is closed') exception if the event loop was closed. BaseProactorEventLoop.close() now also cancels "accept futures". files: Doc/library/asyncio-eventloop.rst | 6 ++ Lib/asyncio/base_events.py | 19 ++++++++ Lib/asyncio/proactor_events.py | 23 ++++++--- Lib/asyncio/selector_events.py | 16 +++--- Lib/test/test_asyncio/test_base_events.py | 14 ++++++ Lib/test/test_asyncio/test_selector_events.py | 17 ++++++- 6 files changed, 76 insertions(+), 19 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 @@ -119,6 +119,12 @@ Callback scheduled after :meth:`stop` is called won't. However, those callbacks will run if :meth:`run_forever` is called again later. +.. method:: BaseEventLoop.is_closed() + + Returns ``True`` if the event loop was closed. + + .. versionadded:: 3.5 + .. method:: BaseEventLoop.close() Close the event loop. The loop should not be running. 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 @@ -119,6 +119,7 @@ class BaseEventLoop(events.AbstractEventLoop): def __init__(self): + self._closed = False self._ready = collections.deque() self._scheduled = [] self._default_executor = None @@ -128,6 +129,11 @@ self._exception_handler = None self._debug = False + def __repr__(self): + return ('<%s running=%s closed=%s debug=%s>' + % (self.__class__.__name__, self.is_running(), + self.is_closed(), self.get_debug())) + def _make_socket_transport(self, sock, protocol, waiter=None, *, extra=None, server=None): """Create socket transport.""" @@ -173,8 +179,13 @@ """Process selector events.""" raise NotImplementedError + def _check_closed(self): + if self._closed: + raise RuntimeError('Event loop is closed') + def run_forever(self): """Run until stop() is called.""" + self._check_closed() if self._running: raise RuntimeError('Event loop is running.') self._running = True @@ -198,6 +209,7 @@ Return the Future's result, or raise its exception. """ + self._check_closed() future = tasks.async(future, loop=self) future.add_done_callback(_raise_stop_error) self.run_forever() @@ -222,6 +234,9 @@ This clears the queues and shuts down the executor, but does not wait for the executor to finish. """ + if self._closed: + return + self._closed = True self._ready.clear() self._scheduled.clear() executor = self._default_executor @@ -229,6 +244,10 @@ self._default_executor = None executor.shutdown(wait=False) + def is_closed(self): + """Returns True if the event loop was closed.""" + return self._closed + def is_running(self): """Returns running status of event loop.""" return self._running 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 @@ -353,13 +353,14 @@ sock, protocol, waiter, extra) def close(self): - if self._proactor is not None: - self._close_self_pipe() - self._proactor.close() - self._proactor = None - self._selector = None - super().close() - self._accept_futures.clear() + if self.is_closed(): + return + self._stop_accept_futures() + self._close_self_pipe() + self._proactor.close() + self._proactor = None + self._selector = None + super().close() def sock_recv(self, sock, n): return self._proactor.recv(sock, n) @@ -428,6 +429,8 @@ self._make_socket_transport( conn, protocol, extra={'peername': addr}, server=server) + if self.is_closed(): + return f = self._proactor.accept(sock) except OSError as exc: if sock.fileno() != -1: @@ -448,8 +451,12 @@ def _process_events(self, event_list): pass # XXX hard work currently done in poll - def _stop_serving(self, sock): + def _stop_accept_futures(self): for future in self._accept_futures.values(): future.cancel() + self._accept_futures.clear() + + def _stop_serving(self, sock): + self._stop_accept_futures() self._proactor._stop_serving(sock) sock.close() 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 @@ -55,11 +55,13 @@ return _SelectorDatagramTransport(self, sock, protocol, address, extra) def close(self): + if self.is_closed(): + return + self._close_self_pipe() if self._selector is not None: - self._close_self_pipe() self._selector.close() self._selector = None - super().close() + super().close() def _socketpair(self): raise NotImplementedError @@ -143,8 +145,7 @@ def add_reader(self, fd, callback, *args): """Add a reader callback.""" - if self._selector is None: - raise RuntimeError('Event loop is closed') + self._check_closed() handle = events.Handle(callback, args, self) try: key = self._selector.get_key(fd) @@ -160,7 +161,7 @@ def remove_reader(self, fd): """Remove a reader callback.""" - if self._selector is None: + if self.is_closed(): return False try: key = self._selector.get_key(fd) @@ -182,8 +183,7 @@ def add_writer(self, fd, callback, *args): """Add a writer callback..""" - if self._selector is None: - raise RuntimeError('Event loop is closed') + self._check_closed() handle = events.Handle(callback, args, self) try: key = self._selector.get_key(fd) @@ -199,7 +199,7 @@ def remove_writer(self, fd): """Remove a writer callback.""" - if self._selector is None: + if self.is_closed(): return False try: key = self._selector.get_key(fd) 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 @@ -52,6 +52,20 @@ gen = self.loop._make_subprocess_transport(m, m, m, m, m, m, m) self.assertRaises(NotImplementedError, next, iter(gen)) + def test_close(self): + self.assertFalse(self.loop.is_closed()) + self.loop.close() + self.assertTrue(self.loop.is_closed()) + + # it should be possible to call close() more than once + self.loop.close() + self.loop.close() + + # operation blocked when the loop is closed + f = asyncio.Future(loop=self.loop) + self.assertRaises(RuntimeError, self.loop.run_forever) + self.assertRaises(RuntimeError, self.loop.run_until_complete, f) + def test__add_callback_handle(self): h = asyncio.Handle(lambda: False, (), self.loop) 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 @@ -80,7 +80,10 @@ self.loop._selector.close() self.loop._selector = selector = mock.Mock() + self.assertFalse(self.loop.is_closed()) + self.loop.close() + self.assertTrue(self.loop.is_closed()) self.assertIsNone(self.loop._selector) self.assertIsNone(self.loop._csock) self.assertIsNone(self.loop._ssock) @@ -89,9 +92,20 @@ csock.close.assert_called_with() remove_reader.assert_called_with(7) + # it should be possible to call close() more than once self.loop.close() self.loop.close() + # operation blocked when the loop is closed + f = asyncio.Future(loop=self.loop) + self.assertRaises(RuntimeError, self.loop.run_forever) + self.assertRaises(RuntimeError, self.loop.run_until_complete, f) + fd = 0 + def callback(): + pass + self.assertRaises(RuntimeError, self.loop.add_reader, fd, callback) + self.assertRaises(RuntimeError, self.loop.add_writer, fd, callback) + def test_close_no_selector(self): ssock = self.loop._ssock csock = self.loop._csock @@ -101,9 +115,6 @@ self.loop._selector = None self.loop.close() self.assertIsNone(self.loop._selector) - self.assertFalse(ssock.close.called) - self.assertFalse(csock.close.called) - self.assertFalse(remove_reader.called) def test_socketpair(self): self.assertRaises(NotImplementedError, self.loop._socketpair) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 02:57:42 2014 From: python-checkins at python.org (terry.reedy) Date: Wed, 4 Jun 2014 02:57:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4NDA5?= =?utf-8?q?=3A_Idle=3A_add_unittest_for_AutoComplete=2E_Patch_by_Phil_Webs?= =?utf-8?q?ter=2E?= Message-ID: <3gjsD64gyhz7N9t@mail.python.org> http://hg.python.org/cpython/rev/4f1abf87df12 changeset: 91010:4f1abf87df12 branch: 2.7 parent: 90990:b8655be522d4 user: Terry Jan Reedy date: Tue Jun 03 20:54:15 2014 -0400 summary: Issue #18409: Idle: add unittest for AutoComplete. Patch by Phil Webster. files: Lib/idlelib/AutoComplete.py | 5 + Lib/idlelib/idle_test/mock_idle.py | 17 + Lib/idlelib/idle_test/mock_tk.py | 27 +- Lib/idlelib/idle_test/test_autocomplete.py | 136 ++++++++++ Misc/NEWS | 5 + 5 files changed, 186 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -222,3 +222,8 @@ namespace = sys.modules.copy() namespace.update(__main__.__dict__) return eval(name, namespace) + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_autocomplete', verbosity=2) diff --git a/Lib/idlelib/idle_test/mock_idle.py b/Lib/idlelib/idle_test/mock_idle.py --- a/Lib/idlelib/idle_test/mock_idle.py +++ b/Lib/idlelib/idle_test/mock_idle.py @@ -5,6 +5,22 @@ from idlelib.idle_test.mock_tk import Text +class Func(object): + '''Mock function captures args and returns result set by test. + + Most common use will probably be to mock methods. + mock_tk.Var and Mbox_func are special cases of this. + ''' + def __init__(self, result=None): + self.result = result + self.args = None + self.kwds = None + def __call__(self, *args, **kwds): + self.args = args + self.kwds = kwds + return self.result + + class Editor(object): '''Minimally imitate EditorWindow.EditorWindow class. ''' @@ -17,6 +33,7 @@ last = self.text.index('end') return first, last + class UndoDelegator(object): '''Minimally imitate UndoDelegator,UndoDelegator class. ''' diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py --- a/Lib/idlelib/idle_test/mock_tk.py +++ b/Lib/idlelib/idle_test/mock_tk.py @@ -1,9 +1,27 @@ """Classes that replace tkinter gui objects used by an object being tested. -A gui object is anything with a master or parent paramenter, which is typically -required in spite of what the doc strings say. +A gui object is anything with a master or parent paramenter, which is +typically required in spite of what the doc strings say. """ +class Event(object): + '''Minimal mock with attributes for testing event handlers. + + This is not a gui object, but is used as an argument for callbacks + that access attributes of the event passed. If a callback ignores + the event, other than the fact that is happened, pass 'event'. + + Keyboard, mouse, window, and other sources generate Event instances. + Event instances have the following attributes: serial (number of + event), time (of event), type (of event as number), widget (in which + event occurred), and x,y (position of mouse). There are other + attributes for specific events, such as keycode for key events. + tkinter.Event.__doc__ has more but is still not complete. + ''' + def __init__(self, **kwds): + "Create event with attributes needed for test" + self.__dict__.update(kwds) + class Var(object): "Use for String/Int/BooleanVar: incomplete" def __init__(self, master=None, value=None, name=None): @@ -20,9 +38,10 @@ Instead of displaying a message box, the mock's call method saves the arguments as instance attributes, which test functions can then examime. + The test can set the result returned to ask function """ - def __init__(self): - self.result = None # The return for all show funcs + def __init__(self, result=None): + self.result = result # Return None for all show funcs def __call__(self, title, message, *args, **kwds): # Save all args for possible examination by tester self.title = title diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -0,0 +1,136 @@ +import unittest +from test.test_support import requires +from Tkinter import Tk, Text, TclError + +import idlelib.AutoComplete as ac +import idlelib.AutoCompleteWindow as acw +import idlelib.macosxSupport as mac +from idlelib.EditorWindow import EditorWindow +from idlelib.idle_test.mock_idle import Func +from idlelib.idle_test.mock_tk import Event + +class AutoCompleteWindow: + def complete(): + return + + +class AutoCompleteTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + mac.setupApp(cls.root, None) + cls.editor = EditorWindow(root=cls.root) + cls.text = cls.editor.text + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.text + del cls.editor + del cls.root + + def setUp(self): + self.editor.text.delete('1.0', 'end') + self.autocomplete = ac.AutoComplete(self.editor) + + def test_init(self): + self.assertEqual(self.autocomplete.editwin, self.editor) + + def test_make_autocomplete_window(self): + testwin = self.autocomplete._make_autocomplete_window() + self.assertIsInstance(testwin, acw.AutoCompleteWindow) + + def test_remove_autocomplete_window(self): + self.autocomplete.autocompletewindow = ( + self.autocomplete._make_autocomplete_window()) + self.autocomplete._remove_autocomplete_window() + self.assertIsNone(self.autocomplete.autocompletewindow) + + def test_force_open_completions_event(self): + # Test that force_open_completions_event calls _open_completions + o_cs = Func() + self.autocomplete.open_completions = o_cs + self.autocomplete.force_open_completions_event('event') + self.assertEqual(o_cs.args, (True, False, True)) + + def test_try_open_completions_event(self): + Equal = self.assertEqual + autocomplete = self.autocomplete + trycompletions = self.autocomplete.try_open_completions_event + o_c_l = Func() + autocomplete._open_completions_later = o_c_l + + # _open_completions_later should not be called with no text in editor + trycompletions('event') + Equal(o_c_l.args, None) + + # _open_completions_later should be called with COMPLETE_ATTRIBUTES (1) + self.text.insert('1.0', 're.') + trycompletions('event') + Equal(o_c_l.args, (False, False, False, 1)) + + # _open_completions_later should be called with COMPLETE_FILES (2) + self.text.delete('1.0', 'end') + self.text.insert('1.0', '"./Lib/') + trycompletions('event') + Equal(o_c_l.args, (False, False, False, 2)) + + def test_autocomplete_event(self): + Equal = self.assertEqual + autocomplete = self.autocomplete + + # Test that the autocomplete event is ignored if user is pressing a + # modifier key in addition to the tab key + ev = Event(mc_state=True) + self.assertIsNone(autocomplete.autocomplete_event(ev)) + del ev.mc_state + + # If autocomplete window is open, complete() method is called + testwin = self.autocomplete._make_autocomplete_window() + self.text.insert('1.0', 're.') + Equal(self.autocomplete.autocomplete_event(ev), 'break') + + # If autocomplete window is not active or does not exist, + # open_completions is called. Return depends on its return. + autocomplete._remove_autocomplete_window() + o_cs = Func() # .result = None + autocomplete.open_completions = o_cs + Equal(self.autocomplete.autocomplete_event(ev), None) + Equal(o_cs.args, (False, True, True)) + o_cs.result = True + Equal(self.autocomplete.autocomplete_event(ev), 'break') + Equal(o_cs.args, (False, True, True)) + + def test_open_completions_later(self): + # Test that autocomplete._delayed_completion_id is set + pass + + def test_delayed_open_completions(self): + # Test that autocomplete._delayed_completion_id set to None and that + # open_completions only called if insertion index is the same as + # _delayed_completion_index + pass + + def test_open_completions(self): + # Test completions of files and attributes as well as non-completion + # of errors + pass + + def test_fetch_completions(self): + # Test that fetch_completions returns 2 lists: + # For attribute completion, a large list containing all variables, and + # a small list containing non-private variables. + # For file completion, a large list containing all files in the path, + # and a small list containing files that do not start with '.' + pass + + def test_get_entity(self): + # Test that a name is in the namespace of sys.modules and + # __main__.__dict__ + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -42,6 +42,11 @@ - Issue #21481: Argparse equality and inequality tests now return NotImplemented when comparing to an unknown type. + +IDLE +---- + +- Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. Tests ----- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 02:57:44 2014 From: python-checkins at python.org (terry.reedy) Date: Wed, 4 Jun 2014 02:57:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE4NDA5?= =?utf-8?q?=3A_Idle=3A_add_unittest_for_AutoComplete=2E_Patch_by_Phil_Webs?= =?utf-8?q?ter=2E?= Message-ID: <3gjsD80kNNz7NBy@mail.python.org> http://hg.python.org/cpython/rev/bf8710cf896b changeset: 91011:bf8710cf896b branch: 3.4 parent: 91007:c5186045395e user: Terry Jan Reedy date: Tue Jun 03 20:54:21 2014 -0400 summary: Issue #18409: Idle: add unittest for AutoComplete. Patch by Phil Webster. files: Lib/idlelib/AutoComplete.py | 5 + Lib/idlelib/idle_test/mock_idle.py | 17 + Lib/idlelib/idle_test/mock_tk.py | 27 +- Lib/idlelib/idle_test/test_autocomplete.py | 136 ++++++++++ Misc/NEWS | 5 + 5 files changed, 186 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -226,3 +226,8 @@ namespace = sys.modules.copy() namespace.update(__main__.__dict__) return eval(name, namespace) + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_autocomplete', verbosity=2) diff --git a/Lib/idlelib/idle_test/mock_idle.py b/Lib/idlelib/idle_test/mock_idle.py --- a/Lib/idlelib/idle_test/mock_idle.py +++ b/Lib/idlelib/idle_test/mock_idle.py @@ -5,6 +5,22 @@ from idlelib.idle_test.mock_tk import Text +class Func: + '''Mock function captures args and returns result set by test. + + Most common use will probably be to mock methods. + mock_tk.Var and Mbox_func are special cases of this. + ''' + def __init__(self, result=None): + self.result = result + self.args = None + self.kwds = None + def __call__(self, *args, **kwds): + self.args = args + self.kwds = kwds + return self.result + + class Editor: '''Minimally imitate EditorWindow.EditorWindow class. ''' @@ -17,6 +33,7 @@ last = self.text.index('end') return first, last + class UndoDelegator: '''Minimally imitate UndoDelegator,UndoDelegator class. ''' diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py --- a/Lib/idlelib/idle_test/mock_tk.py +++ b/Lib/idlelib/idle_test/mock_tk.py @@ -1,9 +1,27 @@ """Classes that replace tkinter gui objects used by an object being tested. -A gui object is anything with a master or parent paramenter, which is typically -required in spite of what the doc strings say. +A gui object is anything with a master or parent paramenter, which is +typically required in spite of what the doc strings say. """ +class Event: + '''Minimal mock with attributes for testing event handlers. + + This is not a gui object, but is used as an argument for callbacks + that access attributes of the event passed. If a callback ignores + the event, other than the fact that is happened, pass 'event'. + + Keyboard, mouse, window, and other sources generate Event instances. + Event instances have the following attributes: serial (number of + event), time (of event), type (of event as number), widget (in which + event occurred), and x,y (position of mouse). There are other + attributes for specific events, such as keycode for key events. + tkinter.Event.__doc__ has more but is still not complete. + ''' + def __init__(self, **kwds): + "Create event with attributes needed for test" + self.__dict__.update(kwds) + class Var: "Use for String/Int/BooleanVar: incomplete" def __init__(self, master=None, value=None, name=None): @@ -20,9 +38,10 @@ Instead of displaying a message box, the mock's call method saves the arguments as instance attributes, which test functions can then examime. + The test can set the result returned to ask function """ - def __init__(self): - self.result = None # The return for all show funcs + def __init__(self, result=None): + self.result = result # Return None for all show funcs def __call__(self, title, message, *args, **kwds): # Save all args for possible examination by tester self.title = title diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -0,0 +1,136 @@ +import unittest +from test.support import requires +from tkinter import Tk, Text, TclError + +import idlelib.AutoComplete as ac +import idlelib.AutoCompleteWindow as acw +import idlelib.macosxSupport as mac +from idlelib.EditorWindow import EditorWindow +from idlelib.idle_test.mock_idle import Func +from idlelib.idle_test.mock_tk import Event + +class AutoCompleteWindow: + def complete(): + return + + +class AutoCompleteTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + mac.setupApp(cls.root, None) + cls.editor = EditorWindow(root=cls.root) + cls.text = cls.editor.text + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.text + del cls.editor + del cls.root + + def setUp(self): + self.editor.text.delete('1.0', 'end') + self.autocomplete = ac.AutoComplete(self.editor) + + def test_init(self): + self.assertEqual(self.autocomplete.editwin, self.editor) + + def test_make_autocomplete_window(self): + testwin = self.autocomplete._make_autocomplete_window() + self.assertIsInstance(testwin, acw.AutoCompleteWindow) + + def test_remove_autocomplete_window(self): + self.autocomplete.autocompletewindow = ( + self.autocomplete._make_autocomplete_window()) + self.autocomplete._remove_autocomplete_window() + self.assertIsNone(self.autocomplete.autocompletewindow) + + def test_force_open_completions_event(self): + # Test that force_open_completions_event calls _open_completions + o_cs = Func() + self.autocomplete.open_completions = o_cs + self.autocomplete.force_open_completions_event('event') + self.assertEqual(o_cs.args, (True, False, True)) + + def test_try_open_completions_event(self): + Equal = self.assertEqual + autocomplete = self.autocomplete + trycompletions = self.autocomplete.try_open_completions_event + o_c_l = Func() + autocomplete._open_completions_later = o_c_l + + # _open_completions_later should not be called with no text in editor + trycompletions('event') + Equal(o_c_l.args, None) + + # _open_completions_later should be called with COMPLETE_ATTRIBUTES (1) + self.text.insert('1.0', 're.') + trycompletions('event') + Equal(o_c_l.args, (False, False, False, 1)) + + # _open_completions_later should be called with COMPLETE_FILES (2) + self.text.delete('1.0', 'end') + self.text.insert('1.0', '"./Lib/') + trycompletions('event') + Equal(o_c_l.args, (False, False, False, 2)) + + def test_autocomplete_event(self): + Equal = self.assertEqual + autocomplete = self.autocomplete + + # Test that the autocomplete event is ignored if user is pressing a + # modifier key in addition to the tab key + ev = Event(mc_state=True) + self.assertIsNone(autocomplete.autocomplete_event(ev)) + del ev.mc_state + + # If autocomplete window is open, complete() method is called + testwin = self.autocomplete._make_autocomplete_window() + self.text.insert('1.0', 're.') + Equal(self.autocomplete.autocomplete_event(ev), 'break') + + # If autocomplete window is not active or does not exist, + # open_completions is called. Return depends on its return. + autocomplete._remove_autocomplete_window() + o_cs = Func() # .result = None + autocomplete.open_completions = o_cs + Equal(self.autocomplete.autocomplete_event(ev), None) + Equal(o_cs.args, (False, True, True)) + o_cs.result = True + Equal(self.autocomplete.autocomplete_event(ev), 'break') + Equal(o_cs.args, (False, True, True)) + + def test_open_completions_later(self): + # Test that autocomplete._delayed_completion_id is set + pass + + def test_delayed_open_completions(self): + # Test that autocomplete._delayed_completion_id set to None and that + # open_completions only called if insertion index is the same as + # _delayed_completion_index + pass + + def test_open_completions(self): + # Test completions of files and attributes as well as non-completion + # of errors + pass + + def test_fetch_completions(self): + # Test that fetch_completions returns 2 lists: + # For attribute completion, a large list containing all variables, and + # a small list containing non-private variables. + # For file completion, a large list containing all files in the path, + # and a small list containing files that do not start with '.' + pass + + def test_get_entity(self): + # Test that a name is in the namespace of sys.modules and + # __main__.__dict__ + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -63,6 +63,11 @@ - Issue #17095: Fix Modules/Setup *shared* support. +IDLE +---- + +- Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. + Tests ----- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 02:57:45 2014 From: python-checkins at python.org (terry.reedy) Date: Wed, 4 Jun 2014 02:57:45 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gjsD93mzMz7NBy@mail.python.org> http://hg.python.org/cpython/rev/3ce08b96f987 changeset: 91012:3ce08b96f987 parent: 91009:690b6ddeee9c parent: 91011:bf8710cf896b user: Terry Jan Reedy date: Tue Jun 03 20:57:15 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/AutoComplete.py | 5 + Lib/idlelib/idle_test/mock_idle.py | 17 + Lib/idlelib/idle_test/mock_tk.py | 27 +- Lib/idlelib/idle_test/test_autocomplete.py | 136 ++++++++++ Misc/NEWS | 5 + 5 files changed, 186 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -226,3 +226,8 @@ namespace = sys.modules.copy() namespace.update(__main__.__dict__) return eval(name, namespace) + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_autocomplete', verbosity=2) diff --git a/Lib/idlelib/idle_test/mock_idle.py b/Lib/idlelib/idle_test/mock_idle.py --- a/Lib/idlelib/idle_test/mock_idle.py +++ b/Lib/idlelib/idle_test/mock_idle.py @@ -5,6 +5,22 @@ from idlelib.idle_test.mock_tk import Text +class Func: + '''Mock function captures args and returns result set by test. + + Most common use will probably be to mock methods. + mock_tk.Var and Mbox_func are special cases of this. + ''' + def __init__(self, result=None): + self.result = result + self.args = None + self.kwds = None + def __call__(self, *args, **kwds): + self.args = args + self.kwds = kwds + return self.result + + class Editor: '''Minimally imitate EditorWindow.EditorWindow class. ''' @@ -17,6 +33,7 @@ last = self.text.index('end') return first, last + class UndoDelegator: '''Minimally imitate UndoDelegator,UndoDelegator class. ''' diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py --- a/Lib/idlelib/idle_test/mock_tk.py +++ b/Lib/idlelib/idle_test/mock_tk.py @@ -1,9 +1,27 @@ """Classes that replace tkinter gui objects used by an object being tested. -A gui object is anything with a master or parent paramenter, which is typically -required in spite of what the doc strings say. +A gui object is anything with a master or parent paramenter, which is +typically required in spite of what the doc strings say. """ +class Event: + '''Minimal mock with attributes for testing event handlers. + + This is not a gui object, but is used as an argument for callbacks + that access attributes of the event passed. If a callback ignores + the event, other than the fact that is happened, pass 'event'. + + Keyboard, mouse, window, and other sources generate Event instances. + Event instances have the following attributes: serial (number of + event), time (of event), type (of event as number), widget (in which + event occurred), and x,y (position of mouse). There are other + attributes for specific events, such as keycode for key events. + tkinter.Event.__doc__ has more but is still not complete. + ''' + def __init__(self, **kwds): + "Create event with attributes needed for test" + self.__dict__.update(kwds) + class Var: "Use for String/Int/BooleanVar: incomplete" def __init__(self, master=None, value=None, name=None): @@ -20,9 +38,10 @@ Instead of displaying a message box, the mock's call method saves the arguments as instance attributes, which test functions can then examime. + The test can set the result returned to ask function """ - def __init__(self): - self.result = None # The return for all show funcs + def __init__(self, result=None): + self.result = result # Return None for all show funcs def __call__(self, title, message, *args, **kwds): # Save all args for possible examination by tester self.title = title diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -0,0 +1,136 @@ +import unittest +from test.support import requires +from tkinter import Tk, Text, TclError + +import idlelib.AutoComplete as ac +import idlelib.AutoCompleteWindow as acw +import idlelib.macosxSupport as mac +from idlelib.EditorWindow import EditorWindow +from idlelib.idle_test.mock_idle import Func +from idlelib.idle_test.mock_tk import Event + +class AutoCompleteWindow: + def complete(): + return + + +class AutoCompleteTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + mac.setupApp(cls.root, None) + cls.editor = EditorWindow(root=cls.root) + cls.text = cls.editor.text + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.text + del cls.editor + del cls.root + + def setUp(self): + self.editor.text.delete('1.0', 'end') + self.autocomplete = ac.AutoComplete(self.editor) + + def test_init(self): + self.assertEqual(self.autocomplete.editwin, self.editor) + + def test_make_autocomplete_window(self): + testwin = self.autocomplete._make_autocomplete_window() + self.assertIsInstance(testwin, acw.AutoCompleteWindow) + + def test_remove_autocomplete_window(self): + self.autocomplete.autocompletewindow = ( + self.autocomplete._make_autocomplete_window()) + self.autocomplete._remove_autocomplete_window() + self.assertIsNone(self.autocomplete.autocompletewindow) + + def test_force_open_completions_event(self): + # Test that force_open_completions_event calls _open_completions + o_cs = Func() + self.autocomplete.open_completions = o_cs + self.autocomplete.force_open_completions_event('event') + self.assertEqual(o_cs.args, (True, False, True)) + + def test_try_open_completions_event(self): + Equal = self.assertEqual + autocomplete = self.autocomplete + trycompletions = self.autocomplete.try_open_completions_event + o_c_l = Func() + autocomplete._open_completions_later = o_c_l + + # _open_completions_later should not be called with no text in editor + trycompletions('event') + Equal(o_c_l.args, None) + + # _open_completions_later should be called with COMPLETE_ATTRIBUTES (1) + self.text.insert('1.0', 're.') + trycompletions('event') + Equal(o_c_l.args, (False, False, False, 1)) + + # _open_completions_later should be called with COMPLETE_FILES (2) + self.text.delete('1.0', 'end') + self.text.insert('1.0', '"./Lib/') + trycompletions('event') + Equal(o_c_l.args, (False, False, False, 2)) + + def test_autocomplete_event(self): + Equal = self.assertEqual + autocomplete = self.autocomplete + + # Test that the autocomplete event is ignored if user is pressing a + # modifier key in addition to the tab key + ev = Event(mc_state=True) + self.assertIsNone(autocomplete.autocomplete_event(ev)) + del ev.mc_state + + # If autocomplete window is open, complete() method is called + testwin = self.autocomplete._make_autocomplete_window() + self.text.insert('1.0', 're.') + Equal(self.autocomplete.autocomplete_event(ev), 'break') + + # If autocomplete window is not active or does not exist, + # open_completions is called. Return depends on its return. + autocomplete._remove_autocomplete_window() + o_cs = Func() # .result = None + autocomplete.open_completions = o_cs + Equal(self.autocomplete.autocomplete_event(ev), None) + Equal(o_cs.args, (False, True, True)) + o_cs.result = True + Equal(self.autocomplete.autocomplete_event(ev), 'break') + Equal(o_cs.args, (False, True, True)) + + def test_open_completions_later(self): + # Test that autocomplete._delayed_completion_id is set + pass + + def test_delayed_open_completions(self): + # Test that autocomplete._delayed_completion_id set to None and that + # open_completions only called if insertion index is the same as + # _delayed_completion_index + pass + + def test_open_completions(self): + # Test completions of files and attributes as well as non-completion + # of errors + pass + + def test_fetch_completions(self): + # Test that fetch_completions returns 2 lists: + # For attribute completion, a large list containing all variables, and + # a small list containing non-private variables. + # For file completion, a large list containing all files in the path, + # and a small list containing files that do not start with '.' + pass + + def test_get_entity(self): + # Test that a name is in the namespace of sys.modules and + # __main__.__dict__ + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -450,6 +450,11 @@ IDLE ---- +- Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. + +- Issue #21477: htest.py - Improve framework, complete set of tests. + Patches by Saimadhav Heblikar + - Issue #18104: Add idlelib/idle_test/htest.py with a few sample tests to begin consolidating and improving human-validated tests of Idle. Change other files as needed to work with htest. Running the module as __main__ runs all tests. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 09:10:19 2014 From: python-checkins at python.org (terry.reedy) Date: Wed, 4 Jun 2014 09:10:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNjU0?= =?utf-8?q?=3A_Fix_interaction_with_warnings=2E_Patch_by_Raymond_Hettinger?= =?utf-8?q?=2E?= Message-ID: <3gk1V30PFqz7Lk2@mail.python.org> http://hg.python.org/cpython/rev/09b33fc96a50 changeset: 91013:09b33fc96a50 branch: 2.7 parent: 91010:4f1abf87df12 user: Terry Jan Reedy date: Wed Jun 04 03:09:56 2014 -0400 summary: Issue #21654: Fix interaction with warnings. Patch by Raymond Hettinger. files: Lib/idlelib/CallTips.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -167,7 +167,7 @@ # bit of a hack for methods - turn it into a function # and drop the "self" param for bound methods fob = ob.im_func - if ob.im_self: + if ob.im_self is not None: arg_offset = 1 elif type(ob_call) == types.MethodType: # a callable class instance -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Wed Jun 4 09:52:23 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 04 Jun 2014 09:52:23 +0200 Subject: [Python-checkins] Daily reference leaks (3ce08b96f987): sum=5 Message-ID: results for 3ce08b96f987 on branch "default" -------------------------------------------- test_collections leaked [2, 0, 0] references, sum=2 test_functools leaked [0, 0, 3] memory blocks, sum=3 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogYaqgTC', '-x'] From python-checkins at python.org Wed Jun 4 20:14:16 2014 From: python-checkins at python.org (barry.warsaw) Date: Wed, 4 Jun 2014 20:14:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogRG9uJ3QgY2htb2Qo?= =?utf-8?q?=29_if_path_is_a_symlink=2E?= Message-ID: <3gkJD84zRmz7LjS@mail.python.org> http://hg.python.org/cpython/rev/36a4f8b60d57 changeset: 91014:36a4f8b60d57 branch: 3.4 parent: 91011:bf8710cf896b user: Barry Warsaw date: Wed Jun 04 14:11:46 2014 -0400 summary: Don't chmod() if path is a symlink. files: Lib/venv/__init__.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -215,7 +215,8 @@ # Issue 18807: make copies if # symlinks are not wanted copier(context.env_exe, path) - os.chmod(path, 0o755) + if not os.path.islink(path): + os.chmod(path, 0o755) else: subdir = 'DLLs' include = self.include_binary -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 20:14:17 2014 From: python-checkins at python.org (barry.warsaw) Date: Wed, 4 Jun 2014 20:14:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Don=27t_chmod=28=29_if_path_is_a_symlink=2E?= Message-ID: <3gkJD96TyBz7LjS@mail.python.org> http://hg.python.org/cpython/rev/c05d94f05a75 changeset: 91015:c05d94f05a75 parent: 91012:3ce08b96f987 parent: 91014:36a4f8b60d57 user: Barry Warsaw date: Wed Jun 04 14:13:13 2014 -0400 summary: Don't chmod() if path is a symlink. files: Lib/venv/__init__.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -215,7 +215,8 @@ # Issue 18807: make copies if # symlinks are not wanted copier(context.env_exe, path) - os.chmod(path, 0o755) + if not os.path.islink(path): + os.chmod(path, 0o755) else: subdir = 'DLLs' include = self.include_binary -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 4 20:45:07 2014 From: python-checkins at python.org (guido.van.rossum) Date: Wed, 4 Jun 2014 20:45:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Replace_deprecated_time=2E?= =?utf-8?b?Y2xvY2soKSB3aXRoIHRpbWUudGltZSgpLiBGaXhlcyBpc3N1ZSAjMjA0NzUu?= Message-ID: <3gkJvl6H30z7Lmn@mail.python.org> http://hg.python.org/cpython/rev/38acef3c3228 changeset: 91016:38acef3c3228 user: Guido van Rossum date: Wed Jun 04 11:45:05 2014 -0700 summary: Replace deprecated time.clock() with time.time(). Fixes issue #20475. files: Lib/test/pystone.py | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/test/pystone.py b/Lib/test/pystone.py --- a/Lib/test/pystone.py +++ b/Lib/test/pystone.py @@ -41,7 +41,7 @@ LOOPS = 50000 -from time import clock +from time import time __version__ = "1.2" @@ -93,10 +93,10 @@ global PtrGlb global PtrGlbNext - starttime = clock() + starttime = time() for i in range(loops): pass - nulltime = clock() - starttime + nulltime = time() - starttime PtrGlbNext = Record() PtrGlb = Record() @@ -108,7 +108,7 @@ String1Loc = "DHRYSTONE PROGRAM, 1'ST STRING" Array2Glob[8][7] = 10 - starttime = clock() + starttime = time() for i in range(loops): Proc5() @@ -134,7 +134,7 @@ IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1 IntLoc1 = Proc2(IntLoc1) - benchtime = clock() - starttime - nulltime + benchtime = time() - starttime - nulltime if benchtime == 0.0: loopsPerBenchtime = 0.0 else: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 03:21:34 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 03:21:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4Mjky?= =?utf-8?q?=3A_Idle_-_test_AutoExpand=2E_Patch_by_Saihadhav_Heblikar=2E?= Message-ID: <3gkTjB3lD1zQXg@mail.python.org> http://hg.python.org/cpython/rev/bdbcd0ae0bde changeset: 91017:bdbcd0ae0bde branch: 2.7 parent: 91013:09b33fc96a50 user: Terry Jan Reedy date: Wed Jun 04 20:50:49 2014 -0400 summary: Issue #18292: Idle - test AutoExpand. Patch by Saihadhav Heblikar. files: Lib/idlelib/AutoExpand.py | 21 + Lib/idlelib/idle_test/test_autoexpand.py | 141 +++++++++++ Misc/NEWS | 2 + 3 files changed, 164 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/AutoExpand.py b/Lib/idlelib/AutoExpand.py --- a/Lib/idlelib/AutoExpand.py +++ b/Lib/idlelib/AutoExpand.py @@ -1,3 +1,17 @@ +'''Complete the current word before the cursor with words in the editor. + +Each menu selection or shortcut key selection replaces the word with a +different word with the same prefix. The search for matches begins +before the target and moves toward the top of the editor. It then starts +after the cursor and moves down. It then returns to the original word and +the cycle starts again. + +Changing the current text line or leaving the cursor in a different +place before requesting the next selection causes AutoExpand to reset +its state. + +This is an extension file and there is only one instance of AutoExpand. +''' import string import re @@ -20,6 +34,7 @@ self.state = None def expand_word_event(self, event): + "Replace the current word with the next expansion." curinsert = self.text.index("insert") curline = self.text.get("insert linestart", "insert lineend") if not self.state: @@ -46,6 +61,7 @@ return "break" def getwords(self): + "Return a list of words that match the prefix before the cursor." word = self.getprevword() if not word: return [] @@ -76,8 +92,13 @@ return words def getprevword(self): + "Return the word prefix before the cursor." line = self.text.get("insert linestart", "insert") i = len(line) while i > 0 and line[i-1] in self.wordchars: i = i-1 return line[i:] + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_autoexpand.py @@ -0,0 +1,141 @@ +"""Unit tests for idlelib.AutoExpand""" +import unittest +from test.test_support import requires +from Tkinter import Text, Tk +#from idlelib.idle_test.mock_tk import Text +from idlelib.AutoExpand import AutoExpand + + +class Dummy_Editwin: + # AutoExpand.__init__ only needs .text + def __init__(self, text): + self.text = text + +class AutoExpandTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + if 'tkinter' in str(Text): + requires('gui') + cls.tk = Tk() + cls.text = Text(cls.tk) + else: + cls.text = Text() + cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text)) + + @classmethod + def tearDownClass(cls): + if hasattr(cls, 'tk'): + cls.tk.destroy() + del cls.tk + del cls.text, cls.auto_expand + + def tearDown(self): + self.text.delete('1.0', 'end') + + def test_get_prevword(self): + text = self.text + previous = self.auto_expand.getprevword + equal = self.assertEqual + + equal(previous(), '') + + text.insert('insert', 't') + equal(previous(), 't') + + text.insert('insert', 'his') + equal(previous(), 'this') + + text.insert('insert', ' ') + equal(previous(), '') + + text.insert('insert', 'is') + equal(previous(), 'is') + + text.insert('insert', '\nsample\nstring') + equal(previous(), 'string') + + text.delete('3.0', 'insert') + equal(previous(), '') + + text.delete('1.0', 'end') + equal(previous(), '') + + def test_before_only(self): + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + self.text.insert('insert', 'ab ac bx ad ab a') + equal(self.auto_expand.getwords(), ['ab', 'ad', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_after_only(self): + # Also add punctuation 'noise' that shoud be ignored. + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'a, [ab] ac: () bx"" cd ac= ad ya') + text.mark_set('insert', '1.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'ad', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'a') + + def test_both_before_after(self): + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'ab xy yz\n') + text.insert('insert', 'a ac by ac') + + text.mark_set('insert', '2.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_other_expand_cases(self): + text = self.text + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + # no expansion candidate found + equal(self.auto_expand.getwords(), []) + equal(expand('event'), 'break') + + text.insert('insert', 'bx cy dz a') + equal(self.auto_expand.getwords(), []) + + # reset state by successfully expanding once + # move cursor to another position and expand again + text.insert('insert', 'ac xy a ac ad a') + text.mark_set('insert', '1.7') + expand('event') + initial_state = self.auto_expand.state + text.mark_set('insert', '1.end') + expand('event') + new_state = self.auto_expand.state + self.assertNotEqual(initial_state, new_state) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -46,6 +46,8 @@ IDLE ---- +- Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. + - Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. Tests -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 03:21:35 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 03:21:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE4Mjky?= =?utf-8?q?=3A_Idle_-_test_AutoExpand=2E_Patch_by_Saihadhav_Heblikar=2E?= Message-ID: <3gkTjC6kGQz7LkF@mail.python.org> http://hg.python.org/cpython/rev/bbdcf09e3097 changeset: 91018:bbdcf09e3097 branch: 3.4 parent: 91011:bf8710cf896b user: Terry Jan Reedy date: Wed Jun 04 20:50:59 2014 -0400 summary: Issue #18292: Idle - test AutoExpand. Patch by Saihadhav Heblikar. files: Lib/idlelib/AutoExpand.py | 21 + Lib/idlelib/idle_test/test_autoexpand.py | 141 +++++++++++ Misc/NEWS | 2 + 3 files changed, 164 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/AutoExpand.py b/Lib/idlelib/AutoExpand.py --- a/Lib/idlelib/AutoExpand.py +++ b/Lib/idlelib/AutoExpand.py @@ -1,3 +1,17 @@ +'''Complete the current word before the cursor with words in the editor. + +Each menu selection or shortcut key selection replaces the word with a +different word with the same prefix. The search for matches begins +before the target and moves toward the top of the editor. It then starts +after the cursor and moves down. It then returns to the original word and +the cycle starts again. + +Changing the current text line or leaving the cursor in a different +place before requesting the next selection causes AutoExpand to reset +its state. + +This is an extension file and there is only one instance of AutoExpand. +''' import string import re @@ -20,6 +34,7 @@ self.state = None def expand_word_event(self, event): + "Replace the current word with the next expansion." curinsert = self.text.index("insert") curline = self.text.get("insert linestart", "insert lineend") if not self.state: @@ -46,6 +61,7 @@ return "break" def getwords(self): + "Return a list of words that match the prefix before the cursor." word = self.getprevword() if not word: return [] @@ -76,8 +92,13 @@ return words def getprevword(self): + "Return the word prefix before the cursor." line = self.text.get("insert linestart", "insert") i = len(line) while i > 0 and line[i-1] in self.wordchars: i = i-1 return line[i:] + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_autoexpand.py @@ -0,0 +1,141 @@ +"""Unit tests for idlelib.AutoExpand""" +import unittest +from test.support import requires +from tkinter import Text, Tk +#from idlelib.idle_test.mock_tk import Text +from idlelib.AutoExpand import AutoExpand + + +class Dummy_Editwin: + # AutoExpand.__init__ only needs .text + def __init__(self, text): + self.text = text + +class AutoExpandTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + if 'tkinter' in str(Text): + requires('gui') + cls.tk = Tk() + cls.text = Text(cls.tk) + else: + cls.text = Text() + cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text)) + + @classmethod + def tearDownClass(cls): + if hasattr(cls, 'tk'): + cls.tk.destroy() + del cls.tk + del cls.text, cls.auto_expand + + def tearDown(self): + self.text.delete('1.0', 'end') + + def test_get_prevword(self): + text = self.text + previous = self.auto_expand.getprevword + equal = self.assertEqual + + equal(previous(), '') + + text.insert('insert', 't') + equal(previous(), 't') + + text.insert('insert', 'his') + equal(previous(), 'this') + + text.insert('insert', ' ') + equal(previous(), '') + + text.insert('insert', 'is') + equal(previous(), 'is') + + text.insert('insert', '\nsample\nstring') + equal(previous(), 'string') + + text.delete('3.0', 'insert') + equal(previous(), '') + + text.delete('1.0', 'end') + equal(previous(), '') + + def test_before_only(self): + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + self.text.insert('insert', 'ab ac bx ad ab a') + equal(self.auto_expand.getwords(), ['ab', 'ad', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_after_only(self): + # Also add punctuation 'noise' that shoud be ignored. + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'a, [ab] ac: () bx"" cd ac= ad ya') + text.mark_set('insert', '1.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'ad', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'a') + + def test_both_before_after(self): + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'ab xy yz\n') + text.insert('insert', 'a ac by ac') + + text.mark_set('insert', '2.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_other_expand_cases(self): + text = self.text + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + # no expansion candidate found + equal(self.auto_expand.getwords(), []) + equal(expand('event'), 'break') + + text.insert('insert', 'bx cy dz a') + equal(self.auto_expand.getwords(), []) + + # reset state by successfully expanding once + # move cursor to another position and expand again + text.insert('insert', 'ac xy a ac ad a') + text.mark_set('insert', '1.7') + expand('event') + initial_state = self.auto_expand.state + text.mark_set('insert', '1.end') + expand('event') + new_state = self.auto_expand.state + self.assertNotEqual(initial_state, new_state) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -66,6 +66,8 @@ IDLE ---- +- Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. + - Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. Tests -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 03:21:37 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 03:21:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_merge_from_3=2E4?= Message-ID: <3gkTjF2ZMyz7Lnb@mail.python.org> http://hg.python.org/cpython/rev/ea64286c110d changeset: 91019:ea64286c110d parent: 91012:3ce08b96f987 user: Terry Jan Reedy date: Wed Jun 04 20:54:43 2014 -0400 summary: merge from 3.4 files: Lib/idlelib/AutoExpand.py | 21 + Lib/idlelib/idle_test/test_autoexpand.py | 141 +++++++++++ Misc/NEWS | 2 + 3 files changed, 164 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/AutoExpand.py b/Lib/idlelib/AutoExpand.py --- a/Lib/idlelib/AutoExpand.py +++ b/Lib/idlelib/AutoExpand.py @@ -1,3 +1,17 @@ +'''Complete the current word before the cursor with words in the editor. + +Each menu selection or shortcut key selection replaces the word with a +different word with the same prefix. The search for matches begins +before the target and moves toward the top of the editor. It then starts +after the cursor and moves down. It then returns to the original word and +the cycle starts again. + +Changing the current text line or leaving the cursor in a different +place before requesting the next selection causes AutoExpand to reset +its state. + +This is an extension file and there is only one instance of AutoExpand. +''' import string import re @@ -20,6 +34,7 @@ self.state = None def expand_word_event(self, event): + "Replace the current word with the next expansion." curinsert = self.text.index("insert") curline = self.text.get("insert linestart", "insert lineend") if not self.state: @@ -46,6 +61,7 @@ return "break" def getwords(self): + "Return a list of words that match the prefix before the cursor." word = self.getprevword() if not word: return [] @@ -76,8 +92,13 @@ return words def getprevword(self): + "Return the word prefix before the cursor." line = self.text.get("insert linestart", "insert") i = len(line) while i > 0 and line[i-1] in self.wordchars: i = i-1 return line[i:] + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_autoexpand.py @@ -0,0 +1,141 @@ +"""Unit tests for idlelib.AutoExpand""" +import unittest +from test.support import requires +from tkinter import Text, Tk +#from idlelib.idle_test.mock_tk import Text +from idlelib.AutoExpand import AutoExpand + + +class Dummy_Editwin: + # AutoExpand.__init__ only needs .text + def __init__(self, text): + self.text = text + +class AutoExpandTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + if 'tkinter' in str(Text): + requires('gui') + cls.tk = Tk() + cls.text = Text(cls.tk) + else: + cls.text = Text() + cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text)) + + @classmethod + def tearDownClass(cls): + if hasattr(cls, 'tk'): + cls.tk.destroy() + del cls.tk + del cls.text, cls.auto_expand + + def tearDown(self): + self.text.delete('1.0', 'end') + + def test_get_prevword(self): + text = self.text + previous = self.auto_expand.getprevword + equal = self.assertEqual + + equal(previous(), '') + + text.insert('insert', 't') + equal(previous(), 't') + + text.insert('insert', 'his') + equal(previous(), 'this') + + text.insert('insert', ' ') + equal(previous(), '') + + text.insert('insert', 'is') + equal(previous(), 'is') + + text.insert('insert', '\nsample\nstring') + equal(previous(), 'string') + + text.delete('3.0', 'insert') + equal(previous(), '') + + text.delete('1.0', 'end') + equal(previous(), '') + + def test_before_only(self): + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + self.text.insert('insert', 'ab ac bx ad ab a') + equal(self.auto_expand.getwords(), ['ab', 'ad', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_after_only(self): + # Also add punctuation 'noise' that shoud be ignored. + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'a, [ab] ac: () bx"" cd ac= ad ya') + text.mark_set('insert', '1.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'ad', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'a') + + def test_both_before_after(self): + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'ab xy yz\n') + text.insert('insert', 'a ac by ac') + + text.mark_set('insert', '2.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_other_expand_cases(self): + text = self.text + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + # no expansion candidate found + equal(self.auto_expand.getwords(), []) + equal(expand('event'), 'break') + + text.insert('insert', 'bx cy dz a') + equal(self.auto_expand.getwords(), []) + + # reset state by successfully expanding once + # move cursor to another position and expand again + text.insert('insert', 'ac xy a ac ad a') + text.mark_set('insert', '1.7') + expand('event') + initial_state = self.auto_expand.state + text.mark_set('insert', '1.end') + expand('event') + new_state = self.auto_expand.state + self.assertNotEqual(initial_state, new_state) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -450,6 +450,8 @@ IDLE ---- +- Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. + - Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. - Issue #21477: htest.py - Improve framework, complete set of tests. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 03:21:38 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 03:21:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy40IC0+IDMuNCk6?= =?utf-8?q?_Merge_heads=2E?= Message-ID: <3gkTjG5l1Zz7LpS@mail.python.org> http://hg.python.org/cpython/rev/3d5f6198c945 changeset: 91020:3d5f6198c945 branch: 3.4 parent: 91014:36a4f8b60d57 parent: 91018:bbdcf09e3097 user: Terry Jan Reedy date: Wed Jun 04 21:01:08 2014 -0400 summary: Merge heads. files: Lib/idlelib/AutoExpand.py | 21 + Lib/idlelib/idle_test/test_autoexpand.py | 141 +++++++++++ Misc/NEWS | 2 + 3 files changed, 164 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/AutoExpand.py b/Lib/idlelib/AutoExpand.py --- a/Lib/idlelib/AutoExpand.py +++ b/Lib/idlelib/AutoExpand.py @@ -1,3 +1,17 @@ +'''Complete the current word before the cursor with words in the editor. + +Each menu selection or shortcut key selection replaces the word with a +different word with the same prefix. The search for matches begins +before the target and moves toward the top of the editor. It then starts +after the cursor and moves down. It then returns to the original word and +the cycle starts again. + +Changing the current text line or leaving the cursor in a different +place before requesting the next selection causes AutoExpand to reset +its state. + +This is an extension file and there is only one instance of AutoExpand. +''' import string import re @@ -20,6 +34,7 @@ self.state = None def expand_word_event(self, event): + "Replace the current word with the next expansion." curinsert = self.text.index("insert") curline = self.text.get("insert linestart", "insert lineend") if not self.state: @@ -46,6 +61,7 @@ return "break" def getwords(self): + "Return a list of words that match the prefix before the cursor." word = self.getprevword() if not word: return [] @@ -76,8 +92,13 @@ return words def getprevword(self): + "Return the word prefix before the cursor." line = self.text.get("insert linestart", "insert") i = len(line) while i > 0 and line[i-1] in self.wordchars: i = i-1 return line[i:] + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_autoexpand.py @@ -0,0 +1,141 @@ +"""Unit tests for idlelib.AutoExpand""" +import unittest +from test.support import requires +from tkinter import Text, Tk +#from idlelib.idle_test.mock_tk import Text +from idlelib.AutoExpand import AutoExpand + + +class Dummy_Editwin: + # AutoExpand.__init__ only needs .text + def __init__(self, text): + self.text = text + +class AutoExpandTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + if 'tkinter' in str(Text): + requires('gui') + cls.tk = Tk() + cls.text = Text(cls.tk) + else: + cls.text = Text() + cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text)) + + @classmethod + def tearDownClass(cls): + if hasattr(cls, 'tk'): + cls.tk.destroy() + del cls.tk + del cls.text, cls.auto_expand + + def tearDown(self): + self.text.delete('1.0', 'end') + + def test_get_prevword(self): + text = self.text + previous = self.auto_expand.getprevword + equal = self.assertEqual + + equal(previous(), '') + + text.insert('insert', 't') + equal(previous(), 't') + + text.insert('insert', 'his') + equal(previous(), 'this') + + text.insert('insert', ' ') + equal(previous(), '') + + text.insert('insert', 'is') + equal(previous(), 'is') + + text.insert('insert', '\nsample\nstring') + equal(previous(), 'string') + + text.delete('3.0', 'insert') + equal(previous(), '') + + text.delete('1.0', 'end') + equal(previous(), '') + + def test_before_only(self): + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + self.text.insert('insert', 'ab ac bx ad ab a') + equal(self.auto_expand.getwords(), ['ab', 'ad', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_after_only(self): + # Also add punctuation 'noise' that shoud be ignored. + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'a, [ab] ac: () bx"" cd ac= ad ya') + text.mark_set('insert', '1.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'ad', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'a') + + def test_both_before_after(self): + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'ab xy yz\n') + text.insert('insert', 'a ac by ac') + + text.mark_set('insert', '2.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_other_expand_cases(self): + text = self.text + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + # no expansion candidate found + equal(self.auto_expand.getwords(), []) + equal(expand('event'), 'break') + + text.insert('insert', 'bx cy dz a') + equal(self.auto_expand.getwords(), []) + + # reset state by successfully expanding once + # move cursor to another position and expand again + text.insert('insert', 'ac xy a ac ad a') + text.mark_set('insert', '1.7') + expand('event') + initial_state = self.auto_expand.state + text.mark_set('insert', '1.end') + expand('event') + new_state = self.auto_expand.state + self.assertNotEqual(initial_state, new_state) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -66,6 +66,8 @@ IDLE ---- +- Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. + - Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. Tests -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 03:21:40 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 03:21:40 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?b?KTogTWVyZ2UgaGVhZHMu?= Message-ID: <3gkTjJ1cFxz7LnP@mail.python.org> http://hg.python.org/cpython/rev/90703f97456f changeset: 91021:90703f97456f parent: 91016:38acef3c3228 parent: 91019:ea64286c110d user: Terry Jan Reedy date: Wed Jun 04 21:02:48 2014 -0400 summary: Merge heads. files: Lib/idlelib/AutoExpand.py | 21 + Lib/idlelib/idle_test/test_autoexpand.py | 141 +++++++++++ Misc/NEWS | 2 + 3 files changed, 164 insertions(+), 0 deletions(-) diff --git a/Lib/idlelib/AutoExpand.py b/Lib/idlelib/AutoExpand.py --- a/Lib/idlelib/AutoExpand.py +++ b/Lib/idlelib/AutoExpand.py @@ -1,3 +1,17 @@ +'''Complete the current word before the cursor with words in the editor. + +Each menu selection or shortcut key selection replaces the word with a +different word with the same prefix. The search for matches begins +before the target and moves toward the top of the editor. It then starts +after the cursor and moves down. It then returns to the original word and +the cycle starts again. + +Changing the current text line or leaving the cursor in a different +place before requesting the next selection causes AutoExpand to reset +its state. + +This is an extension file and there is only one instance of AutoExpand. +''' import string import re @@ -20,6 +34,7 @@ self.state = None def expand_word_event(self, event): + "Replace the current word with the next expansion." curinsert = self.text.index("insert") curline = self.text.get("insert linestart", "insert lineend") if not self.state: @@ -46,6 +61,7 @@ return "break" def getwords(self): + "Return a list of words that match the prefix before the cursor." word = self.getprevword() if not word: return [] @@ -76,8 +92,13 @@ return words def getprevword(self): + "Return the word prefix before the cursor." line = self.text.get("insert linestart", "insert") i = len(line) while i > 0 and line[i-1] in self.wordchars: i = i-1 return line[i:] + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_autoexpand.py @@ -0,0 +1,141 @@ +"""Unit tests for idlelib.AutoExpand""" +import unittest +from test.support import requires +from tkinter import Text, Tk +#from idlelib.idle_test.mock_tk import Text +from idlelib.AutoExpand import AutoExpand + + +class Dummy_Editwin: + # AutoExpand.__init__ only needs .text + def __init__(self, text): + self.text = text + +class AutoExpandTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + if 'tkinter' in str(Text): + requires('gui') + cls.tk = Tk() + cls.text = Text(cls.tk) + else: + cls.text = Text() + cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text)) + + @classmethod + def tearDownClass(cls): + if hasattr(cls, 'tk'): + cls.tk.destroy() + del cls.tk + del cls.text, cls.auto_expand + + def tearDown(self): + self.text.delete('1.0', 'end') + + def test_get_prevword(self): + text = self.text + previous = self.auto_expand.getprevword + equal = self.assertEqual + + equal(previous(), '') + + text.insert('insert', 't') + equal(previous(), 't') + + text.insert('insert', 'his') + equal(previous(), 'this') + + text.insert('insert', ' ') + equal(previous(), '') + + text.insert('insert', 'is') + equal(previous(), 'is') + + text.insert('insert', '\nsample\nstring') + equal(previous(), 'string') + + text.delete('3.0', 'insert') + equal(previous(), '') + + text.delete('1.0', 'end') + equal(previous(), '') + + def test_before_only(self): + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + self.text.insert('insert', 'ab ac bx ad ab a') + equal(self.auto_expand.getwords(), ['ab', 'ad', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_after_only(self): + # Also add punctuation 'noise' that shoud be ignored. + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'a, [ab] ac: () bx"" cd ac= ad ya') + text.mark_set('insert', '1.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'ad', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'ad') + expand('event') + equal(previous(), 'a') + + def test_both_before_after(self): + text = self.text + previous = self.auto_expand.getprevword + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + text.insert('insert', 'ab xy yz\n') + text.insert('insert', 'a ac by ac') + + text.mark_set('insert', '2.1') + equal(self.auto_expand.getwords(), ['ab', 'ac', 'a']) + expand('event') + equal(previous(), 'ab') + expand('event') + equal(previous(), 'ac') + expand('event') + equal(previous(), 'a') + + def test_other_expand_cases(self): + text = self.text + expand = self.auto_expand.expand_word_event + equal = self.assertEqual + + # no expansion candidate found + equal(self.auto_expand.getwords(), []) + equal(expand('event'), 'break') + + text.insert('insert', 'bx cy dz a') + equal(self.auto_expand.getwords(), []) + + # reset state by successfully expanding once + # move cursor to another position and expand again + text.insert('insert', 'ac xy a ac ad a') + text.mark_set('insert', '1.7') + expand('event') + initial_state = self.auto_expand.state + text.mark_set('insert', '1.end') + expand('event') + new_state = self.auto_expand.state + self.assertNotEqual(initial_state, new_state) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -450,6 +450,8 @@ IDLE ---- +- Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. + - Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. - Issue #21477: htest.py - Improve framework, complete set of tests. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 03:21:41 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 03:21:41 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gkTjK3Ns2z7LpD@mail.python.org> http://hg.python.org/cpython/rev/cb36da605a87 changeset: 91022:cb36da605a87 parent: 91021:90703f97456f parent: 91020:3d5f6198c945 user: Terry Jan Reedy date: Wed Jun 04 21:20:54 2014 -0400 summary: Merge with 3.4 files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 09:54:53 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 09:54:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4OTEw?= =?utf-8?q?=3A_Add_unittest_for_textView=2E_Patch_by_Phil_Webster=2E?= Message-ID: <3gkfR115RQz7Nsj@mail.python.org> http://hg.python.org/cpython/rev/9ac57970ee4c changeset: 91023:9ac57970ee4c branch: 2.7 parent: 91017:bdbcd0ae0bde user: Terry Jan Reedy date: Thu Jun 05 03:38:28 2014 -0400 summary: Issue #18910: Add unittest for textView. Patch by Phil Webster. files: Lib/idlelib/idle_test/mock_idle.py | 10 +- Lib/idlelib/idle_test/test_textview.py | 98 ++++++++++++++ Lib/idlelib/textView.py | 9 +- Misc/NEWS | 2 + 4 files changed, 116 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/mock_idle.py b/Lib/idlelib/idle_test/mock_idle.py --- a/Lib/idlelib/idle_test/mock_idle.py +++ b/Lib/idlelib/idle_test/mock_idle.py @@ -8,14 +8,22 @@ class Func(object): '''Mock function captures args and returns result set by test. + Attributes: + self.called - records call even if no args, kwds passed. + self.result - set by init, returned by call. + self.args - captures positional arguments. + self.kwds - captures keyword arguments. + Most common use will probably be to mock methods. - mock_tk.Var and Mbox_func are special cases of this. + Mock_tk.Var and Mbox_func are special variants of this. ''' def __init__(self, result=None): + self.called = False self.result = result self.args = None self.kwds = None def __call__(self, *args, **kwds): + self.called = True self.args = args self.kwds = kwds return self.result diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_textview.py @@ -0,0 +1,98 @@ +'''Test the functions and main class method of textView.py.''' + +import unittest +import os +from test.test_support import requires +from Tkinter import Tk, Text, TclError +from idlelib import textView as tv +from idlelib.idle_test.mock_idle import Func +from idlelib.idle_test.mock_tk import Mbox + +orig_mbox = tv.tkMessageBox + +class textviewClassTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.TV = TV = tv.TextViewer + TV.transient = Func() + TV.grab_set = Func() + TV.wait_window = Func() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + TV = cls.TV + del cls.root, cls.TV + del TV.transient, TV.grab_set, TV.wait_window + + def setUp(self): + TV = self.TV + TV.transient.__init__() + TV.grab_set.__init__() + TV.wait_window.__init__() + + + def test_init_modal(self): + TV = self.TV + view = TV(self.root, 'Title', 'test text') + self.assertTrue(TV.transient.called) + self.assertTrue(TV.grab_set.called) + self.assertTrue(TV.wait_window.called) + view.Ok() + + def test_init_nonmodal(self): + TV = self.TV + view = TV(self.root, 'Title', 'test text', modal=False) + self.assertFalse(TV.transient.called) + self.assertFalse(TV.grab_set.called) + self.assertFalse(TV.wait_window.called) + view.Ok() + + def test_ok(self): + view = self.TV(self.root, 'Title', 'test text', modal=False) + view.destroy = Func() + view.Ok() + self.assertTrue(view.destroy.called) + del view.destroy # unmask real function + view.destroy + + +class textviewTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + tv.tkMessageBox = Mbox + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + tv.tkMessageBox = orig_mbox + + def test_view_text(self): + # If modal True, tkinter will error with 'can't invoke "event" command' + view = tv.view_text(self.root, 'Title', 'test text', modal=False) + self.assertIsInstance(view, tv.TextViewer) + + def test_view_file(self): + test_dir = os.path.dirname(__file__) + testfile = os.path.join(test_dir, 'test_textview.py') + view = tv.view_file(self.root, 'Title', testfile, modal=False) + self.assertIsInstance(view, tv.TextViewer) + self.assertIn('Test', view.textView.get('1.0', '1.end')) + view.Ok() + + # Mock messagebox will be used and view_file will not return anything + testfile = os.path.join(test_dir, '../notthere.py') + view = tv.view_file(self.root, 'Title', testfile, modal=False) + self.assertIsNone(view) + +if __name__ == '__main__': + unittest.main(verbosity=2, exit=False) + from idlelib.idle_test.htest import run + run(TextViewer) diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py --- a/Lib/idlelib/textView.py +++ b/Lib/idlelib/textView.py @@ -12,7 +12,11 @@ def __init__(self, parent, title, text, modal=True, _htest=False): """Show the given text in a scrollable window with a 'close' button - _htest - bool, change box location when running htest + If modal option set to False, user can interact with other windows, + otherwise they will be unable to interact with other windows until + the textview window is closed. + + _htest - bool; change box location when running htest. """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) @@ -72,7 +76,6 @@ else: textFile = open(filename, 'r') except IOError: - import tkMessageBox tkMessageBox.showerror(title='File Load Error', message='Unable to load file %r .' % filename, parent=parent) @@ -81,5 +84,7 @@ if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(TextViewer) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -46,6 +46,8 @@ IDLE ---- +- Issue #18910: Add unittest for textView. Patch by Phil Webster. + - Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. - Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 09:54:54 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 09:54:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE4OTEw?= =?utf-8?q?=3A_Add_unittest_for_textView=2E_Patch_by_Phil_Webster=2E?= Message-ID: <3gkfR248Btz7Ljt@mail.python.org> http://hg.python.org/cpython/rev/99047f3a19a9 changeset: 91024:99047f3a19a9 branch: 3.4 parent: 91020:3d5f6198c945 user: Terry Jan Reedy date: Thu Jun 05 03:38:34 2014 -0400 summary: Issue #18910: Add unittest for textView. Patch by Phil Webster. files: Lib/idlelib/idle_test/mock_idle.py | 10 +- Lib/idlelib/idle_test/test_textview.py | 96 ++++++++++++++ Lib/idlelib/textView.py | 11 +- Misc/NEWS | 2 + 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/idle_test/mock_idle.py b/Lib/idlelib/idle_test/mock_idle.py --- a/Lib/idlelib/idle_test/mock_idle.py +++ b/Lib/idlelib/idle_test/mock_idle.py @@ -8,14 +8,22 @@ class Func: '''Mock function captures args and returns result set by test. + Attributes: + self.called - records call even if no args, kwds passed. + self.result - set by init, returned by call. + self.args - captures positional arguments. + self.kwds - captures keyword arguments. + Most common use will probably be to mock methods. - mock_tk.Var and Mbox_func are special cases of this. + Mock_tk.Var and Mbox_func are special variants of this. ''' def __init__(self, result=None): + self.called = False self.result = result self.args = None self.kwds = None def __call__(self, *args, **kwds): + self.called = True self.args = args self.kwds = kwds return self.result diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_textview.py @@ -0,0 +1,96 @@ +'''Test the functions and main class method of textView.py.''' + +import unittest +import os +from test.support import requires +from tkinter import Tk, Text, TclError +from idlelib import textView as tv +from idlelib.idle_test.mock_idle import Func +from idlelib.idle_test.mock_tk import Mbox + +orig_mbox = tv.tkMessageBox + +class TextViewTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.TV = TV = tv.TextViewer + TV.transient = Func() + TV.grab_set = Func() + TV.wait_window = Func() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + TV = cls.TV + del cls.root, cls.TV + del TV.transient, TV.grab_set, TV.wait_window + + def setUp(self): + TV = self.TV + TV.transient.__init__() + TV.grab_set.__init__() + TV.wait_window.__init__() + + + def test_init_modal(self): + TV = self.TV + view = TV(self.root, 'Title', 'test text') + self.assertTrue(TV.transient.called) + self.assertTrue(TV.grab_set.called) + self.assertTrue(TV.wait_window.called) + view.Ok() + + def test_init_nonmodal(self): + TV = self.TV + view = TV(self.root, 'Title', 'test text', modal=False) + self.assertFalse(TV.transient.called) + self.assertFalse(TV.grab_set.called) + self.assertFalse(TV.wait_window.called) + view.Ok() + + def test_ok(self): + view = self.TV(self.root, 'Title', 'test text', modal=False) + view.destroy = Func() + view.Ok() + self.assertTrue(view.destroy.called) + del view.destroy # unmask real function + view.destroy + + +class textviewTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + tv.tkMessageBox = Mbox + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + tv.tkMessageBox = orig_mbox + + def test_view_text(self): + # If modal True, tkinter will error with 'can't invoke "event" command' + view = tv.view_text(self.root, 'Title', 'test text', modal=False) + self.assertIsInstance(view, tv.TextViewer) + + def test_view_file(self): + test_dir = os.path.dirname(__file__) + testfile = os.path.join(test_dir, 'test_textview.py') + view = tv.view_file(self.root, 'Title', testfile, modal=False) + self.assertIsInstance(view, tv.TextViewer) + self.assertIn('Test', view.textView.get('1.0', '1.end')) + view.Ok() + + # Mock messagebox will be used and view_file will not return anything + testfile = os.path.join(test_dir, '../notthere.py') + view = tv.view_file(self.root, 'Title', testfile, modal=False) + self.assertIsNone(view) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py --- a/Lib/idlelib/textView.py +++ b/Lib/idlelib/textView.py @@ -12,7 +12,11 @@ def __init__(self, parent, title, text, modal=True, _htest=False): """Show the given text in a scrollable window with a 'close' button - _htest - bool, change box location when running htest + If modal option set to False, user can interact with other windows, + otherwise they will be unable to interact with other windows until + the textview window is closed. + + _htest - bool; change box location when running htest. """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) @@ -68,8 +72,7 @@ try: with open(filename, 'r', encoding=encoding) as file: contents = file.read() - except OSError: - import tkinter.messagebox as tkMessageBox + except IOError: tkMessageBox.showerror(title='File Load Error', message='Unable to load file %r .' % filename, parent=parent) @@ -77,5 +80,7 @@ return view_text(parent, title, contents, modal) if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(TextViewer) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -66,6 +66,8 @@ IDLE ---- +- Issue #18910: Add unittest for textView. Patch by Phil Webster. + - Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. - Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 09:54:55 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 09:54:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_heads_from_3=2E4=2E?= Message-ID: <3gkfR36lhjz7Nsp@mail.python.org> http://hg.python.org/cpython/rev/6d696f07d44e changeset: 91025:6d696f07d44e parent: 91022:cb36da605a87 parent: 91024:99047f3a19a9 user: Terry Jan Reedy date: Thu Jun 05 03:45:08 2014 -0400 summary: Merge heads from 3.4. files: Lib/idlelib/idle_test/mock_idle.py | 10 +- Lib/idlelib/idle_test/test_textview.py | 96 ++++++++++++++ Lib/idlelib/textView.py | 11 +- Misc/NEWS | 2 + 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/idle_test/mock_idle.py b/Lib/idlelib/idle_test/mock_idle.py --- a/Lib/idlelib/idle_test/mock_idle.py +++ b/Lib/idlelib/idle_test/mock_idle.py @@ -8,14 +8,22 @@ class Func: '''Mock function captures args and returns result set by test. + Attributes: + self.called - records call even if no args, kwds passed. + self.result - set by init, returned by call. + self.args - captures positional arguments. + self.kwds - captures keyword arguments. + Most common use will probably be to mock methods. - mock_tk.Var and Mbox_func are special cases of this. + Mock_tk.Var and Mbox_func are special variants of this. ''' def __init__(self, result=None): + self.called = False self.result = result self.args = None self.kwds = None def __call__(self, *args, **kwds): + self.called = True self.args = args self.kwds = kwds return self.result diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_textview.py @@ -0,0 +1,96 @@ +'''Test the functions and main class method of textView.py.''' + +import unittest +import os +from test.support import requires +from tkinter import Tk, Text, TclError +from idlelib import textView as tv +from idlelib.idle_test.mock_idle import Func +from idlelib.idle_test.mock_tk import Mbox + +orig_mbox = tv.tkMessageBox + +class TextViewTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.TV = TV = tv.TextViewer + TV.transient = Func() + TV.grab_set = Func() + TV.wait_window = Func() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + TV = cls.TV + del cls.root, cls.TV + del TV.transient, TV.grab_set, TV.wait_window + + def setUp(self): + TV = self.TV + TV.transient.__init__() + TV.grab_set.__init__() + TV.wait_window.__init__() + + + def test_init_modal(self): + TV = self.TV + view = TV(self.root, 'Title', 'test text') + self.assertTrue(TV.transient.called) + self.assertTrue(TV.grab_set.called) + self.assertTrue(TV.wait_window.called) + view.Ok() + + def test_init_nonmodal(self): + TV = self.TV + view = TV(self.root, 'Title', 'test text', modal=False) + self.assertFalse(TV.transient.called) + self.assertFalse(TV.grab_set.called) + self.assertFalse(TV.wait_window.called) + view.Ok() + + def test_ok(self): + view = self.TV(self.root, 'Title', 'test text', modal=False) + view.destroy = Func() + view.Ok() + self.assertTrue(view.destroy.called) + del view.destroy # unmask real function + view.destroy + + +class textviewTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + tv.tkMessageBox = Mbox + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + tv.tkMessageBox = orig_mbox + + def test_view_text(self): + # If modal True, tkinter will error with 'can't invoke "event" command' + view = tv.view_text(self.root, 'Title', 'test text', modal=False) + self.assertIsInstance(view, tv.TextViewer) + + def test_view_file(self): + test_dir = os.path.dirname(__file__) + testfile = os.path.join(test_dir, 'test_textview.py') + view = tv.view_file(self.root, 'Title', testfile, modal=False) + self.assertIsInstance(view, tv.TextViewer) + self.assertIn('Test', view.textView.get('1.0', '1.end')) + view.Ok() + + # Mock messagebox will be used and view_file will not return anything + testfile = os.path.join(test_dir, '../notthere.py') + view = tv.view_file(self.root, 'Title', testfile, modal=False) + self.assertIsNone(view) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py --- a/Lib/idlelib/textView.py +++ b/Lib/idlelib/textView.py @@ -12,7 +12,11 @@ def __init__(self, parent, title, text, modal=True, _htest=False): """Show the given text in a scrollable window with a 'close' button - _htest - bool, change box location when running htest + If modal option set to False, user can interact with other windows, + otherwise they will be unable to interact with other windows until + the textview window is closed. + + _htest - bool; change box location when running htest. """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) @@ -68,8 +72,7 @@ try: with open(filename, 'r', encoding=encoding) as file: contents = file.read() - except OSError: - import tkinter.messagebox as tkMessageBox + except IOError: tkMessageBox.showerror(title='File Load Error', message='Unable to load file %r .' % filename, parent=parent) @@ -77,5 +80,7 @@ return view_text(parent, title, contents, modal) if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(TextViewer) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -450,6 +450,8 @@ IDLE ---- +- Issue #18910: Add unittest for textView. Patch by Phil Webster. + - Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. - Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 09:54:57 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 09:54:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_whitespace?= Message-ID: <3gkfR512Ylz7Nsp@mail.python.org> http://hg.python.org/cpython/rev/ae76dd86affb changeset: 91026:ae76dd86affb branch: 2.7 parent: 91023:9ac57970ee4c user: Terry Jan Reedy date: Thu Jun 05 03:53:42 2014 -0400 summary: whitespace files: Lib/idlelib/idle_test/test_textview.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -33,7 +33,7 @@ TV.transient.__init__() TV.grab_set.__init__() TV.wait_window.__init__() - + def test_init_modal(self): TV = self.TV -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 09:54:58 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 09:54:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_whitespace?= Message-ID: <3gkfR62V2Xz7LkF@mail.python.org> http://hg.python.org/cpython/rev/723e1b414501 changeset: 91027:723e1b414501 branch: 3.4 parent: 91024:99047f3a19a9 user: Terry Jan Reedy date: Thu Jun 05 03:54:02 2014 -0400 summary: whitespace files: Lib/idlelib/idle_test/test_textview.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -33,7 +33,7 @@ TV.transient.__init__() TV.grab_set.__init__() TV.wait_window.__init__() - + def test_init_modal(self): TV = self.TV -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 09:54:59 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 5 Jun 2014 09:54:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gkfR74K0Vz7Nt7@mail.python.org> http://hg.python.org/cpython/rev/4d12a29a25f4 changeset: 91028:4d12a29a25f4 parent: 91025:6d696f07d44e parent: 91027:723e1b414501 user: Terry Jan Reedy date: Thu Jun 05 03:54:21 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/idle_test/test_textview.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -33,7 +33,7 @@ TV.transient.__init__() TV.grab_set.__init__() TV.wait_window.__init__() - + def test_init_modal(self): TV = self.TV -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Thu Jun 5 09:57:28 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 05 Jun 2014 09:57:28 +0200 Subject: [Python-checkins] Daily reference leaks (cb36da605a87): sum=9 Message-ID: results for cb36da605a87 on branch "default" -------------------------------------------- test_asyncio leaked [0, 4, 0] memory blocks, sum=4 test_collections leaked [0, 0, 2] references, sum=2 test_functools leaked [0, 0, 3] memory blocks, sum=3 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/reflogo3KocY', '-x'] From python-checkins at python.org Thu Jun 5 10:32:38 2014 From: python-checkins at python.org (vinay.sajip) Date: Thu, 5 Jun 2014 10:32:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjYz?= =?utf-8?q?=3A_Fixed_error_caused_by_trying_to_create_an_existing_director?= =?utf-8?q?y=2E?= Message-ID: <3gkgGZ4sbJz7Ljq@mail.python.org> http://hg.python.org/cpython/rev/477e71004040 changeset: 91029:477e71004040 branch: 3.4 parent: 91027:723e1b414501 user: Vinay Sajip date: Thu Jun 05 09:31:20 2014 +0100 summary: Issue #21663: Fixed error caused by trying to create an existing directory. files: Lib/venv/__init__.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -238,7 +238,8 @@ if 'init.tcl' in files: tcldir = os.path.basename(root) tcldir = os.path.join(context.env_dir, 'Lib', tcldir) - os.makedirs(tcldir) + if not os.path.exists(tcldir): + os.makedirs(tcldir) src = os.path.join(root, 'init.tcl') dst = os.path.join(tcldir, 'init.tcl') shutil.copyfile(src, dst) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 10:32:39 2014 From: python-checkins at python.org (vinay.sajip) Date: Thu, 5 Jun 2014 10:32:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Closes_=2321663=3A_Merged_fix_from_3=2E4=2E?= Message-ID: <3gkgGb6LJ5z7LkM@mail.python.org> http://hg.python.org/cpython/rev/1ed9edde3bfc changeset: 91030:1ed9edde3bfc parent: 91028:4d12a29a25f4 parent: 91029:477e71004040 user: Vinay Sajip date: Thu Jun 05 09:32:24 2014 +0100 summary: Closes #21663: Merged fix from 3.4. files: Lib/venv/__init__.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -238,7 +238,8 @@ if 'init.tcl' in files: tcldir = os.path.basename(root) tcldir = os.path.join(context.env_dir, 'Lib', tcldir) - os.makedirs(tcldir) + if not os.path.exists(tcldir): + os.makedirs(tcldir) src = os.path.join(root, 'init.tcl') dst = os.path.join(tcldir, 'init.tcl') shutil.copyfile(src, dst) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 12:09:06 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 5 Jun 2014 12:09:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Tulip_issue_83?= =?utf-8?q?=2C_Python_issue_=2321252=3A_Fill_some_XXX_docstrings_in_asynci?= =?utf-8?q?o?= Message-ID: <3gkjPt29Zrz7PtG@mail.python.org> http://hg.python.org/cpython/rev/d1712437cab2 changeset: 91031:d1712437cab2 branch: 3.4 parent: 91029:477e71004040 user: Victor Stinner date: Thu Jun 05 12:06:44 2014 +0200 summary: Tulip issue 83, Python issue #21252: Fill some XXX docstrings in asyncio files: Doc/library/asyncio-eventloop.rst | 8 ++-- Lib/asyncio/events.py | 35 ++++++++++++------ Lib/asyncio/unix_events.py | 4 +- 3 files changed, 29 insertions(+), 18 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 @@ -64,19 +64,19 @@ .. method:: get_event_loop() - Get the event loop for current context. Returns an event loop object - implementing :class:`BaseEventLoop` interface, or raises an exception in case + Get the event loop for the current context. Returns an event loop object + implementing the :class:`BaseEventLoop` interface, or raises an exception in case no event loop has been set for the current context and the current policy does not specify to create one. It should never return ``None``. .. method:: set_event_loop(loop) - Set the event loop of the current context to *loop*. + Set the event loop for the current context to *loop*. .. method:: new_event_loop() Create and return a new event loop object according to this policy's rules. - If there's need to set this loop as the event loop of the current context, + If there's need to set this loop as the event loop for the current context, :meth:`set_event_loop` must be called explicitly. Access to the global loop policy diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -355,25 +355,33 @@ """Abstract policy for accessing the event loop.""" def get_event_loop(self): - """XXX""" + """Get the event loop for the current context. + + Returns an event loop object implementing the BaseEventLoop interface, + or raises an exception in case no event loop has been set for the + current context and the current policy does not specify to create one. + + It should never return None.""" raise NotImplementedError def set_event_loop(self, loop): - """XXX""" + """Set the event loop for the current context to loop.""" raise NotImplementedError def new_event_loop(self): - """XXX""" + """Create and return a new event loop object according to this + policy's rules. If there's need to set this loop as the event loop for + the current context, set_event_loop must be called explicitly.""" raise NotImplementedError # Child processes handling (Unix only). def get_child_watcher(self): - """XXX""" + "Get the watcher for child processes." raise NotImplementedError def set_child_watcher(self, watcher): - """XXX""" + """Set the watcher for child processes.""" raise NotImplementedError @@ -447,39 +455,42 @@ def get_event_loop_policy(): - """XXX""" + """Get the current event loop policy.""" if _event_loop_policy is None: _init_event_loop_policy() return _event_loop_policy def set_event_loop_policy(policy): - """XXX""" + """Set the current event loop policy. + + If policy is None, the default policy is restored.""" global _event_loop_policy assert policy is None or isinstance(policy, AbstractEventLoopPolicy) _event_loop_policy = policy def get_event_loop(): - """XXX""" + """Equivalent to calling get_event_loop_policy().get_event_loop().""" return get_event_loop_policy().get_event_loop() def set_event_loop(loop): - """XXX""" + """Equivalent to calling get_event_loop_policy().set_event_loop(loop).""" get_event_loop_policy().set_event_loop(loop) def new_event_loop(): - """XXX""" + """Equivalent to calling get_event_loop_policy().new_event_loop().""" return get_event_loop_policy().new_event_loop() def get_child_watcher(): - """XXX""" + """Equivalent to calling get_event_loop_policy().get_child_watcher().""" return get_event_loop_policy().get_child_watcher() def set_child_watcher(watcher): - """XXX""" + """Equivalent to calling + get_event_loop_policy().set_child_watcher(watcher).""" return get_event_loop_policy().set_child_watcher(watcher) 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 @@ -822,7 +822,7 @@ self._watcher.attach_loop(loop) def get_child_watcher(self): - """Get the child watcher + """Get the watcher for child processes. If not yet set, a SafeChildWatcher object is automatically created. """ @@ -832,7 +832,7 @@ return self._watcher def set_child_watcher(self, watcher): - """Set the child watcher""" + """Set the watcher for child processes.""" assert watcher is None or isinstance(watcher, AbstractChildWatcher) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 12:09:07 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 5 Jun 2014 12:09:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Tulip_issue_83=2C_Python_issue_=232125?= =?utf-8?q?2=3A_Fill_some_XXX_docstrings_in?= Message-ID: <3gkjPv51SFz7QF4@mail.python.org> http://hg.python.org/cpython/rev/782c3b4cbc88 changeset: 91032:782c3b4cbc88 parent: 91030:1ed9edde3bfc parent: 91031:d1712437cab2 user: Victor Stinner date: Thu Jun 05 12:07:14 2014 +0200 summary: (Merge 3.4) Tulip issue 83, Python issue #21252: Fill some XXX docstrings in asyncio files: Doc/library/asyncio-eventloop.rst | 8 ++-- Lib/asyncio/events.py | 35 ++++++++++++------ Lib/asyncio/unix_events.py | 4 +- 3 files changed, 29 insertions(+), 18 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 @@ -64,19 +64,19 @@ .. method:: get_event_loop() - Get the event loop for current context. Returns an event loop object - implementing :class:`BaseEventLoop` interface, or raises an exception in case + Get the event loop for the current context. Returns an event loop object + implementing the :class:`BaseEventLoop` interface, or raises an exception in case no event loop has been set for the current context and the current policy does not specify to create one. It should never return ``None``. .. method:: set_event_loop(loop) - Set the event loop of the current context to *loop*. + Set the event loop for the current context to *loop*. .. method:: new_event_loop() Create and return a new event loop object according to this policy's rules. - If there's need to set this loop as the event loop of the current context, + If there's need to set this loop as the event loop for the current context, :meth:`set_event_loop` must be called explicitly. Access to the global loop policy diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -355,25 +355,33 @@ """Abstract policy for accessing the event loop.""" def get_event_loop(self): - """XXX""" + """Get the event loop for the current context. + + Returns an event loop object implementing the BaseEventLoop interface, + or raises an exception in case no event loop has been set for the + current context and the current policy does not specify to create one. + + It should never return None.""" raise NotImplementedError def set_event_loop(self, loop): - """XXX""" + """Set the event loop for the current context to loop.""" raise NotImplementedError def new_event_loop(self): - """XXX""" + """Create and return a new event loop object according to this + policy's rules. If there's need to set this loop as the event loop for + the current context, set_event_loop must be called explicitly.""" raise NotImplementedError # Child processes handling (Unix only). def get_child_watcher(self): - """XXX""" + "Get the watcher for child processes." raise NotImplementedError def set_child_watcher(self, watcher): - """XXX""" + """Set the watcher for child processes.""" raise NotImplementedError @@ -447,39 +455,42 @@ def get_event_loop_policy(): - """XXX""" + """Get the current event loop policy.""" if _event_loop_policy is None: _init_event_loop_policy() return _event_loop_policy def set_event_loop_policy(policy): - """XXX""" + """Set the current event loop policy. + + If policy is None, the default policy is restored.""" global _event_loop_policy assert policy is None or isinstance(policy, AbstractEventLoopPolicy) _event_loop_policy = policy def get_event_loop(): - """XXX""" + """Equivalent to calling get_event_loop_policy().get_event_loop().""" return get_event_loop_policy().get_event_loop() def set_event_loop(loop): - """XXX""" + """Equivalent to calling get_event_loop_policy().set_event_loop(loop).""" get_event_loop_policy().set_event_loop(loop) def new_event_loop(): - """XXX""" + """Equivalent to calling get_event_loop_policy().new_event_loop().""" return get_event_loop_policy().new_event_loop() def get_child_watcher(): - """XXX""" + """Equivalent to calling get_event_loop_policy().get_child_watcher().""" return get_event_loop_policy().get_child_watcher() def set_child_watcher(watcher): - """XXX""" + """Equivalent to calling + get_event_loop_policy().set_child_watcher(watcher).""" return get_event_loop_policy().set_child_watcher(watcher) 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 @@ -822,7 +822,7 @@ self._watcher.attach_loop(loop) def get_child_watcher(self): - """Get the child watcher + """Get the watcher for child processes. If not yet set, a SafeChildWatcher object is automatically created. """ @@ -832,7 +832,7 @@ return self._watcher def set_child_watcher(self, watcher): - """Set the child watcher""" + """Set the watcher for child processes.""" assert watcher is None or isinstance(watcher, AbstractChildWatcher) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 14:29:34 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 5 Jun 2014 14:29:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321515=3A_tempfile?= =?utf-8?q?=2ETemporaryFile_now_uses_os=2EO=5FTMPFILE_flag_is_available?= Message-ID: <3gkmWy5PGWz7QDp@mail.python.org> http://hg.python.org/cpython/rev/4b51a992cb70 changeset: 91033:4b51a992cb70 user: Victor Stinner date: Thu Jun 05 14:27:45 2014 +0200 summary: Issue #21515: tempfile.TemporaryFile now uses os.O_TMPFILE flag is available files: Doc/library/tempfile.rst | 7 +++++++ Lib/tempfile.py | 26 ++++++++++++++++++++++++++ Misc/NEWS | 2 ++ 3 files changed, 35 insertions(+), 0 deletions(-) diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -54,6 +54,13 @@ underlying true file object. This file-like object can be used in a :keyword:`with` statement, just like a normal file. + The :py:data:`os.O_TMPFILE` flag is used if it is available and works + (Linux-specific, require Linux kernel 3.11 or later). + + .. versionchanged:: 3.5 + + The :py:data:`os.O_TMPFILE` flag is now used if available. + .. function:: NamedTemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix='', prefix='tmp', dir=None, delete=True) diff --git a/Lib/tempfile.py b/Lib/tempfile.py --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -473,6 +473,11 @@ TemporaryFile = NamedTemporaryFile else: + # Is the O_TMPFILE flag available and does it work? + # The flag is set to False if os.open(dir, os.O_TMPFILE) raises an + # IsADirectoryError exception + _O_TMPFILE_WORKS = hasattr(_os, 'O_TMPFILE') + def TemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix="", prefix=template, dir=None): @@ -488,11 +493,32 @@ Returns an object with a file-like interface. The file has no name, and will cease to exist when it is closed. """ + global _O_TMPFILE_WORKS if dir is None: dir = gettempdir() flags = _bin_openflags + if _O_TMPFILE_WORKS: + try: + flags2 = (flags | _os.O_TMPFILE) & ~_os.O_CREAT + fd = _os.open(dir, flags2, 0o600) + except IsADirectoryError: + # Linux kernel older than 3.11 ignores O_TMPFILE flag. + # Set flag to None to not try again. + _O_TMPFILE_WORKS = False + except OSError: + # The filesystem of the directory does not support O_TMPFILE. + # For example, OSError(95, 'Operation not supported'). + pass + else: + try: + return _io.open(fd, mode, buffering=buffering, + newline=newline, encoding=encoding) + except: + _os.close(fd) + raise + # Fallback to _mkstemp_inner(). (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags) try: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -88,6 +88,8 @@ Library ------- +- Issue #21515: tempfile.TemporaryFile now uses os.O_TMPFILE flag is available. + - Issue #21618: The subprocess module could fail to close open fds that were inherited by the calling process and already higher than POSIX resource limits would otherwise allow. On systems with a functioning /proc/self/fd -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 19:02:05 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 5 Jun 2014 19:02:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Clean_up_Tcl/T?= =?utf-8?q?k_building_in_the_Windows_buildbot_scripts=2E?= Message-ID: <3gktZP2qkKz7dff@mail.python.org> http://hg.python.org/cpython/rev/baac4ea2901b changeset: 91034:baac4ea2901b branch: 3.4 parent: 91031:d1712437cab2 user: Zachary Ware date: Thu Jun 05 11:44:22 2014 -0500 summary: Clean up Tcl/Tk building in the Windows buildbot scripts. - Nix the 'noxp' option, it was for Win2k compatibility (which was dropped with Python 3.3) and made default ttk ugly on post-Win2k systems (#21665) - Use 'OPTS=symbols' instead of 'DEBUG=1'; symbols is the official method - Use core, shell, dlls, install-binaries, and install-libraries targets instead of all and install to avoid trying to 1) build packages that don't work with MSVC 10+ and 2) install unnecessary bits and pieces. files: Tools/buildbot/external-amd64.bat | 10 +++++----- Tools/buildbot/external.bat | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Tools/buildbot/external-amd64.bat b/Tools/buildbot/external-amd64.bat --- a/Tools/buildbot/external-amd64.bat +++ b/Tools/buildbot/external-amd64.bat @@ -6,16 +6,16 @@ if not exist tcltk64\bin\tcl86tg.dll ( cd tcl-8.6.1.0\win - nmake -f makefile.vc DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 clean all - nmake -f makefile.vc DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 install + nmake -f makefile.vc OPTS=symbols MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 clean core shell dlls + nmake -f makefile.vc OPTS=symbols MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 install-binaries install-libraries cd ..\.. ) if not exist tcltk64\bin\tk86tg.dll ( cd tk-8.6.1.0\win - nmake -f makefile.vc OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.6.1.0 clean - nmake -f makefile.vc OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.6.1.0 all - nmake -f makefile.vc OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.6.1.0 install + nmake -f makefile.vc OPTS=symbols MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.6.1.0 clean + nmake -f makefile.vc OPTS=symbols MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.6.1.0 all + nmake -f makefile.vc OPTS=symbols MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.6.1.0 install-binaries install-libraries cd ..\.. ) diff --git a/Tools/buildbot/external.bat b/Tools/buildbot/external.bat --- a/Tools/buildbot/external.bat +++ b/Tools/buildbot/external.bat @@ -7,15 +7,15 @@ if not exist tcltk\bin\tcl86tg.dll ( @rem all and install need to be separate invocations, otherwise nmakehlp is not found on install cd tcl-8.6.1.0\win - nmake -f makefile.vc DEBUG=1 INSTALLDIR=..\..\tcltk clean all - nmake -f makefile.vc DEBUG=1 INSTALLDIR=..\..\tcltk install + nmake -f makefile.vc OPTS=symbols INSTALLDIR=..\..\tcltk clean core shell dlls + nmake -f makefile.vc OPTS=symbols INSTALLDIR=..\..\tcltk install-binaries install-libraries cd ..\.. ) if not exist tcltk\bin\tk86tg.dll ( cd tk-8.6.1.0\win - nmake -f makefile.vc OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.6.1.0 clean - nmake -f makefile.vc OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.6.1.0 all - nmake -f makefile.vc OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.6.1.0 install + nmake -f makefile.vc OPTS=symbols INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.6.1.0 clean + nmake -f makefile.vc OPTS=symbols INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.6.1.0 all + nmake -f makefile.vc OPTS=symbols INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.6.1.0 install-binaries install-libraries cd ..\.. ) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 19:02:06 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 5 Jun 2014 19:02:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321665=3A_Don=27t_use_=27OPTS=3Dnoxp=27_when_com?= =?utf-8?q?piling_Tk=2E?= Message-ID: <3gktZQ5fX6z7dfQ@mail.python.org> http://hg.python.org/cpython/rev/b3063de0dbd9 changeset: 91035:b3063de0dbd9 parent: 91033:4b51a992cb70 parent: 91034:baac4ea2901b user: Zachary Ware date: Thu Jun 05 11:53:44 2014 -0500 summary: Issue #21665: Don't use 'OPTS=noxp' when compiling Tk. That option had been for Win2k compatibility (which was dropped with Python 3.3) and makes default ttk ugly on post-Win2k systems. files: PCbuild/tk.vcxproj | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PCbuild/tk.vcxproj b/PCbuild/tk.vcxproj --- a/PCbuild/tk.vcxproj +++ b/PCbuild/tk.vcxproj @@ -153,9 +153,9 @@ IF "$(Platform)" EQU "x64" set TclMachine=AMD64 IF "$(Configuration)" EQU "Debug" ( - set TclOpts=symbols,noxp + set TclOpts=symbols ) ELSE ( - set TclOpts=noxp + set TclOpts= ) cd $(tkDir)\win -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 19:02:08 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 5 Jun 2014 19:02:08 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Force_a_clean_?= =?utf-8?q?pull-and-build_of_Tcl/Tk_on_Windows_buildbots=2E?= Message-ID: <3gktZS0rRZz7dgR@mail.python.org> http://hg.python.org/cpython/rev/09fc284399d9 changeset: 91036:09fc284399d9 branch: 3.4 parent: 91034:baac4ea2901b user: Zachary Ware date: Thu Jun 05 11:56:36 2014 -0500 summary: Force a clean pull-and-build of Tcl/Tk on Windows buildbots. files: Tools/buildbot/external-common.bat | 24 ++++++++++++------ 1 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -5,14 +5,22 @@ @rem XXX: If you need to force the buildbots to start from a fresh environment, uncomment @rem the following, check it in, then check it out, comment it out, then check it back in. @rem if exist bzip2-1.0.6 rd /s/q bzip2-1.0.6 - at rem if exist tcltk rd /s/q tcltk - at rem if exist tcltk64 rd /s/q tcltk64 - at rem if exist tcl8.4.12 rd /s/q tcl8.4.12 - at rem if exist tcl8.4.16 rd /s/q tcl8.4.16 - at rem if exist tcl-8.4.18.1 rd /s/q tcl-8.4.18.1 - at rem if exist tk8.4.12 rd /s/q tk8.4.12 - at rem if exist tk8.4.16 rd /s/q tk8.4.16 - at rem if exist tk-8.4.18.1 rd /s/q tk-8.4.18.1 +if exist tcltk rd /s/q tcltk +if exist tcltk64 rd /s/q tcltk64 +if exist tcl8.4.12 rd /s/q tcl8.4.12 +if exist tcl8.4.16 rd /s/q tcl8.4.16 +if exist tcl-8.4.18.1 rd /s/q tcl-8.4.18.1 +if exist tcl-8.5.2.1 rd /s/q tcl-8.5.2.1 +if exist tcl-8.5.9.0 rd /s/q tcl-8.5.9.0 +if exist tcl-8.5.11.0 rd /s/q tcl-8.5.11.0 +if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 +if exist tk8.4.12 rd /s/q tk8.4.12 +if exist tk8.4.16 rd /s/q tk8.4.16 +if exist tk-8.4.18.1 rd /s/q tk-8.4.18.1 +if exist tk-8.5.2.0 rd /s/q tk-8.5.2.0 +if exist tk-8.5.9.0 rd /s/q tk-8.5.9.0 +if exist tk-8.5.11.0 rd /s/q tk-8.5.11.0 +if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 @rem if exist db-4.4.20 rd /s/q db-4.4.20 @rem if exist openssl-1.0.1e rd /s/q openssl-1.0.1g @rem if exist sqlite-3.7.12 rd /s/q sqlite-3.7.12 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 19:02:09 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 5 Jun 2014 19:02:09 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Force_a_clean_pull-and-build_of_Tcl/Tk_on_Windows_buildb?= =?utf-8?q?ots=2E?= Message-ID: <3gktZT2h5cz7dgn@mail.python.org> http://hg.python.org/cpython/rev/1bc25dc09c06 changeset: 91037:1bc25dc09c06 parent: 91035:b3063de0dbd9 parent: 91036:09fc284399d9 user: Zachary Ware date: Thu Jun 05 11:56:58 2014 -0500 summary: Force a clean pull-and-build of Tcl/Tk on Windows buildbots. files: Tools/buildbot/external-common.bat | 24 ++++++++++++------ 1 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -5,14 +5,22 @@ @rem XXX: If you need to force the buildbots to start from a fresh environment, uncomment @rem the following, check it in, then check it out, comment it out, then check it back in. @rem if exist bzip2-1.0.6 rd /s/q bzip2-1.0.6 - at rem if exist tcltk rd /s/q tcltk - at rem if exist tcltk64 rd /s/q tcltk64 - at rem if exist tcl8.4.12 rd /s/q tcl8.4.12 - at rem if exist tcl8.4.16 rd /s/q tcl8.4.16 - at rem if exist tcl-8.4.18.1 rd /s/q tcl-8.4.18.1 - at rem if exist tk8.4.12 rd /s/q tk8.4.12 - at rem if exist tk8.4.16 rd /s/q tk8.4.16 - at rem if exist tk-8.4.18.1 rd /s/q tk-8.4.18.1 +if exist tcltk rd /s/q tcltk +if exist tcltk64 rd /s/q tcltk64 +if exist tcl8.4.12 rd /s/q tcl8.4.12 +if exist tcl8.4.16 rd /s/q tcl8.4.16 +if exist tcl-8.4.18.1 rd /s/q tcl-8.4.18.1 +if exist tcl-8.5.2.1 rd /s/q tcl-8.5.2.1 +if exist tcl-8.5.9.0 rd /s/q tcl-8.5.9.0 +if exist tcl-8.5.11.0 rd /s/q tcl-8.5.11.0 +if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 +if exist tk8.4.12 rd /s/q tk8.4.12 +if exist tk8.4.16 rd /s/q tk8.4.16 +if exist tk-8.4.18.1 rd /s/q tk-8.4.18.1 +if exist tk-8.5.2.0 rd /s/q tk-8.5.2.0 +if exist tk-8.5.9.0 rd /s/q tk-8.5.9.0 +if exist tk-8.5.11.0 rd /s/q tk-8.5.11.0 +if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 @rem if exist db-4.4.20 rd /s/q db-4.4.20 @rem if exist openssl-1.0.1e rd /s/q openssl-1.0.1g @rem if exist sqlite-3.7.12 rd /s/q sqlite-3.7.12 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 20:28:51 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 5 Jun 2014 20:28:51 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Revert_Tools/b?= =?utf-8?q?uildbot/external-common=2Ebat_to_normal_=28with_updated_version?= =?utf-8?b?cyku?= Message-ID: <3gkwVW0SlHz7LjN@mail.python.org> http://hg.python.org/cpython/rev/2a3264a0cd9f changeset: 91038:2a3264a0cd9f branch: 3.4 parent: 91036:09fc284399d9 user: Zachary Ware date: Thu Jun 05 12:00:28 2014 -0500 summary: Revert Tools/buildbot/external-common.bat to normal (with updated versions). files: Tools/buildbot/external-common.bat | 20 +++-------------- 1 files changed, 4 insertions(+), 16 deletions(-) diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -5,22 +5,10 @@ @rem XXX: If you need to force the buildbots to start from a fresh environment, uncomment @rem the following, check it in, then check it out, comment it out, then check it back in. @rem if exist bzip2-1.0.6 rd /s/q bzip2-1.0.6 -if exist tcltk rd /s/q tcltk -if exist tcltk64 rd /s/q tcltk64 -if exist tcl8.4.12 rd /s/q tcl8.4.12 -if exist tcl8.4.16 rd /s/q tcl8.4.16 -if exist tcl-8.4.18.1 rd /s/q tcl-8.4.18.1 -if exist tcl-8.5.2.1 rd /s/q tcl-8.5.2.1 -if exist tcl-8.5.9.0 rd /s/q tcl-8.5.9.0 -if exist tcl-8.5.11.0 rd /s/q tcl-8.5.11.0 -if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 -if exist tk8.4.12 rd /s/q tk8.4.12 -if exist tk8.4.16 rd /s/q tk8.4.16 -if exist tk-8.4.18.1 rd /s/q tk-8.4.18.1 -if exist tk-8.5.2.0 rd /s/q tk-8.5.2.0 -if exist tk-8.5.9.0 rd /s/q tk-8.5.9.0 -if exist tk-8.5.11.0 rd /s/q tk-8.5.11.0 -if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 + at rem if exist tcltk rd /s/q tcltk + at rem if exist tcltk64 rd /s/q tcltk64 + at rem if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 + at rem if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 @rem if exist db-4.4.20 rd /s/q db-4.4.20 @rem if exist openssl-1.0.1e rd /s/q openssl-1.0.1g @rem if exist sqlite-3.7.12 rd /s/q sqlite-3.7.12 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 20:28:52 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 5 Jun 2014 20:28:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Revert_Tools/buildbot/external-common=2Ebat_to_normal_?= =?utf-8?q?=28with_updated_versions=29=2E?= Message-ID: <3gkwVX2Thyz7LjN@mail.python.org> http://hg.python.org/cpython/rev/6bca618a6b6f changeset: 91039:6bca618a6b6f parent: 91037:1bc25dc09c06 parent: 91038:2a3264a0cd9f user: Zachary Ware date: Thu Jun 05 12:00:51 2014 -0500 summary: Revert Tools/buildbot/external-common.bat to normal (with updated versions). files: Tools/buildbot/external-common.bat | 20 +++-------------- 1 files changed, 4 insertions(+), 16 deletions(-) diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -5,22 +5,10 @@ @rem XXX: If you need to force the buildbots to start from a fresh environment, uncomment @rem the following, check it in, then check it out, comment it out, then check it back in. @rem if exist bzip2-1.0.6 rd /s/q bzip2-1.0.6 -if exist tcltk rd /s/q tcltk -if exist tcltk64 rd /s/q tcltk64 -if exist tcl8.4.12 rd /s/q tcl8.4.12 -if exist tcl8.4.16 rd /s/q tcl8.4.16 -if exist tcl-8.4.18.1 rd /s/q tcl-8.4.18.1 -if exist tcl-8.5.2.1 rd /s/q tcl-8.5.2.1 -if exist tcl-8.5.9.0 rd /s/q tcl-8.5.9.0 -if exist tcl-8.5.11.0 rd /s/q tcl-8.5.11.0 -if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 -if exist tk8.4.12 rd /s/q tk8.4.12 -if exist tk8.4.16 rd /s/q tk8.4.16 -if exist tk-8.4.18.1 rd /s/q tk-8.4.18.1 -if exist tk-8.5.2.0 rd /s/q tk-8.5.2.0 -if exist tk-8.5.9.0 rd /s/q tk-8.5.9.0 -if exist tk-8.5.11.0 rd /s/q tk-8.5.11.0 -if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 + at rem if exist tcltk rd /s/q tcltk + at rem if exist tcltk64 rd /s/q tcltk64 + at rem if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 + at rem if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 @rem if exist db-4.4.20 rd /s/q db-4.4.20 @rem if exist openssl-1.0.1e rd /s/q openssl-1.0.1g @rem if exist sqlite-3.7.12 rd /s/q sqlite-3.7.12 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 20:41:52 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 5 Jun 2014 20:41:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjYx?= =?utf-8?q?=3A_Fix_typo=2E?= Message-ID: <3gkwnX3l7Hz7LjP@mail.python.org> http://hg.python.org/cpython/rev/a708844c1b8d changeset: 91040:a708844c1b8d branch: 3.4 parent: 91038:2a3264a0cd9f user: Zachary Ware date: Thu Jun 05 13:41:06 2014 -0500 summary: Issue #21661: Fix typo. files: Doc/distributing/index.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/distributing/index.rst b/Doc/distributing/index.rst --- a/Doc/distributing/index.rst +++ b/Doc/distributing/index.rst @@ -49,7 +49,7 @@ of the mailing list used to coordinate Python packaging standards development). * ``setuptools`` is a (largely) drop-in replacement for ``distutils`` first - published in 2004. It's most notable addition over the unmodified + published in 2004. Its most notable addition over the unmodified ``distutils`` tools was the ability to declare dependencies on other packages. It is currently recommended as a more regularly updated alternative to ``distutils`` that offers consistent support for more -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 20:41:53 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 5 Jun 2014 20:41:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Closes_=2321661=3A_Merge_typo_fix=2E?= Message-ID: <3gkwnY5Vwtz7LkS@mail.python.org> http://hg.python.org/cpython/rev/1b02b771b1fa changeset: 91041:1b02b771b1fa parent: 91039:6bca618a6b6f parent: 91040:a708844c1b8d user: Zachary Ware date: Thu Jun 05 13:41:36 2014 -0500 summary: Closes #21661: Merge typo fix. files: Doc/distributing/index.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/distributing/index.rst b/Doc/distributing/index.rst --- a/Doc/distributing/index.rst +++ b/Doc/distributing/index.rst @@ -49,7 +49,7 @@ of the mailing list used to coordinate Python packaging standards development). * ``setuptools`` is a (largely) drop-in replacement for ``distutils`` first - published in 2004. It's most notable addition over the unmodified + published in 2004. Its most notable addition over the unmodified ``distutils`` tools was the ability to declare dependencies on other packages. It is currently recommended as a more regularly updated alternative to ``distutils`` that offers consistent support for more -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 21:17:20 2014 From: python-checkins at python.org (r.david.murray) Date: Thu, 5 Jun 2014 21:17:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzIxNjUzOiBmaXgg?= =?utf-8?q?doc_for_return_type_of_sqlite3=2ERow=2Ekeys=28=29=2E?= Message-ID: <3gkxZS1PmCz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/f51ecdac91c8 changeset: 91042:f51ecdac91c8 branch: 2.7 parent: 91026:ae76dd86affb user: R David Murray date: Thu Jun 05 15:15:43 2014 -0400 summary: #21653: fix doc for return type of sqlite3.Row.keys(). files: Doc/library/sqlite3.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -617,7 +617,7 @@ .. method:: keys - This method returns a tuple of column names. Immediately after a query, + This method returns a list of column names. Immediately after a query, it is the first member of each tuple in :attr:`Cursor.description`. .. versionadded:: 2.6 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 21:17:21 2014 From: python-checkins at python.org (r.david.murray) Date: Thu, 5 Jun 2014 21:17:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogIzIxNjUzOiBmaXgg?= =?utf-8?q?doc_for_return_type_of_sqlite3=2ERow=2Ekeys=28=29=2E?= Message-ID: <3gkxZT57Vrz7Lkc@mail.python.org> http://hg.python.org/cpython/rev/6c890b2739f4 changeset: 91043:6c890b2739f4 branch: 3.4 parent: 91040:a708844c1b8d user: R David Murray date: Thu Jun 05 15:16:38 2014 -0400 summary: #21653: fix doc for return type of sqlite3.Row.keys(). files: Doc/library/sqlite3.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -646,7 +646,7 @@ .. method:: keys - This method returns a tuple of column names. Immediately after a query, + This method returns a list of column names. Immediately after a query, it is the first member of each tuple in :attr:`Cursor.description`. Let's assume we initialize a table as in the example given above:: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 21:17:23 2014 From: python-checkins at python.org (r.david.murray) Date: Thu, 5 Jun 2014 21:17:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge=3A_=2321653=3A_fix_doc_for_return_type_of_sqlite3?= =?utf-8?b?LlJvdy5rZXlzKCku?= Message-ID: <3gkxZW0xnbz7Lkc@mail.python.org> http://hg.python.org/cpython/rev/e65cd43d136b changeset: 91044:e65cd43d136b parent: 91041:1b02b771b1fa parent: 91043:6c890b2739f4 user: R David Murray date: Thu Jun 05 15:17:01 2014 -0400 summary: Merge: #21653: fix doc for return type of sqlite3.Row.keys(). files: Doc/library/sqlite3.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -646,7 +646,7 @@ .. method:: keys - This method returns a tuple of column names. Immediately after a query, + This method returns a list of column names. Immediately after a query, it is the first member of each tuple in :attr:`Cursor.description`. Let's assume we initialize a table as in the example given above:: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 21:33:04 2014 From: python-checkins at python.org (r.david.murray) Date: Thu, 5 Jun 2014 21:33:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogIzIxNjYyOiBmaXgg?= =?utf-8?q?typo=2C_improve_sentence_flow?= Message-ID: <3gkxwc40P9z7LjN@mail.python.org> http://hg.python.org/cpython/rev/ead4dee062e3 changeset: 91045:ead4dee062e3 branch: 3.4 parent: 91043:6c890b2739f4 user: R David Murray date: Thu Jun 05 15:31:56 2014 -0400 summary: #21662: fix typo, improve sentence flow Patch by Steve Dougherty. files: Doc/reference/datamodel.rst | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2102,9 +2102,9 @@ .. note:: - When :meth:`__index__` is defined, :meth:`__int__` should also be defined, - and both shuld return the same value, in order to have a coherent integer - type class. + In order to have a coherent integer type class, when :meth:`__index__` is + defined :meth:`__int__` should also be defined, and both should return + the same value. .. _context-managers: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 21:33:06 2014 From: python-checkins at python.org (r.david.murray) Date: Thu, 5 Jun 2014 21:33:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_=2321662=3A_fix_typo=2C_improve_sentence_flow?= Message-ID: <3gkxwf0LMHz7Ljy@mail.python.org> http://hg.python.org/cpython/rev/3aa21b5b145a changeset: 91046:3aa21b5b145a parent: 91044:e65cd43d136b parent: 91045:ead4dee062e3 user: R David Murray date: Thu Jun 05 15:32:34 2014 -0400 summary: Merge #21662: fix typo, improve sentence flow files: Doc/reference/datamodel.rst | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2106,9 +2106,9 @@ .. note:: - When :meth:`__index__` is defined, :meth:`__int__` should also be defined, - and both shuld return the same value, in order to have a coherent integer - type class. + In order to have a coherent integer type class, when :meth:`__index__` is + defined :meth:`__int__` should also be defined, and both should return + the same value. .. _context-managers: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 5 22:58:18 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 5 Jun 2014 22:58:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4Mjky?= =?utf-8?q?=3A_s/tkinter/Tkinter/?= Message-ID: <3gkzpy3vMvz7Ljb@mail.python.org> http://hg.python.org/cpython/rev/2567c68fb300 changeset: 91047:2567c68fb300 branch: 2.7 parent: 91042:f51ecdac91c8 user: Zachary Ware date: Thu Jun 05 15:57:44 2014 -0500 summary: Issue #18292: s/tkinter/Tkinter/ files: Lib/idlelib/idle_test/test_autoexpand.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py --- a/Lib/idlelib/idle_test/test_autoexpand.py +++ b/Lib/idlelib/idle_test/test_autoexpand.py @@ -15,7 +15,7 @@ @classmethod def setUpClass(cls): - if 'tkinter' in str(Text): + if 'Tkinter' in str(Text): requires('gui') cls.tk = Tk() cls.text = Text(cls.tk) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 6 03:55:04 2014 From: python-checkins at python.org (donald.stufft) Date: Fri, 6 Jun 2014 03:55:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_Update_PEP_470_to_clarify_som?= =?utf-8?q?e_points?= Message-ID: <3gl6PN0288z7LjM@mail.python.org> http://hg.python.org/peps/rev/2445eb12979c changeset: 5486:2445eb12979c user: Donald Stufft date: Thu Jun 05 21:54:57 2014 -0400 summary: Update PEP 470 to clarify some points files: pep-0470.txt | 135 +++++++++++++++++++++++++++++++++----- 1 files changed, 117 insertions(+), 18 deletions(-) diff --git a/pep-0470.txt b/pep-0470.txt --- a/pep-0470.txt +++ b/pep-0470.txt @@ -9,7 +9,7 @@ Type: Process Content-Type: text/x-rst Created: 12-May-2014 -Post-History: 14-May-2014 +Post-History: 14-May-2014, 05-Jun-2014 Abstract @@ -25,6 +25,10 @@ any obligation too. They can still list their project in PyPI as an index, and the tooling will still allow them to host it elsewhere. +This PEP strictly is concerned with the Simple Installer API and how automated +installers interact with PyPI, it has no bearing on the informational pages +which are primarily for human consumption. + Rationale ========= @@ -47,7 +51,10 @@ that installer to provide additional custom locations to search for files during the dependency resolution phase. For pip these locations can be configured per invocation, per shell environment, per requirements file, per -virtual environment, and per user. +virtual environment, and per user. The mechanism for specifying additional +locations have existed within pip and setuptools for many years, by comparison +the mechanisms in PEP 438 and any other new mechanism will have existed for +only a short period of time (if they exist at all currently). The use of additional indexes instead of external links on the simple installer API provides a simple clean interface which is consistent with the @@ -122,7 +129,7 @@ External Links on the Simple Installer API ------------------------------------------ -PEP438 proposed a system of classifying file links as either internal, +PEP 438 proposed a system of classifying file links as either internal, external, or unsafe. It recommended that by default only internal links would be installed by an installer however users could opt into external links on either a global or a per package basis. Additionally they could also opt into @@ -130,7 +137,7 @@ This system has turned out to be *extremely* unfriendly towards the end users and it is the position of this PEP that the situation has become untenable. The -situation as provided by PEP438 requires an end user to be aware not only of +situation as provided by PEP 438 requires an end user to be aware not only of the difference between internal, external, and unsafe, but also to be aware of what hosting mode the package they are trying to install is in, what links are available on that project's /simple/ page, whether or not those links have @@ -212,7 +219,10 @@ or more of the additional URLs to search in. This message should include any comments that the project has included to enable them to communicate to the user and provide hints as to which URL they might want if some are only -useful or compatible with certain platforms or situations. +useful or compatible with certain platforms or situations. When the installer +has implemented the auto discovery mechanisms they should also deprecate any +of the mechanisms added for PEP 438 (such as ``--allow-external``) for removal +at the end of the deprecation period proposed by the PEP. This feature *must* be added to PyPI prior to starting the deprecation and removal process for link spidering. @@ -222,10 +232,11 @@ ========================================= A new hosting mode will be added to PyPI. This hosting mode will be called -``pypi-only`` and will be in addition to the three that PEP438 has already given -us which are ``pypi-explicit``, ``pypi-scrape``, ``pypi-scrape-crawl``. This -new hosting mode will modify a project's simple api page so that it only lists -the files which are directly hosted on PyPI and will not link to anything else. +``pypi-only`` and will be in addition to the three that PEP 438 has already +given us which are ``pypi-explicit``, ``pypi-scrape``, ``pypi-scrape-crawl``. +This new hosting mode will modify a project's simple api page so that it only +lists the files which are directly hosted on PyPI and will not link to anything +else. Upon acceptance of this PEP and the addition of the ``pypi-only`` mode, all new projects will by defaulted to the PyPI only mode and they will be locked to @@ -249,27 +260,115 @@ to enter their PyPI credentials and package name and have it automatically download and re-host all of their files on PyPI. This email *must also* include instructions for setting up their own index page and registering that -with PyPI. +with PyPI, including the fact that they can use pythonhosted.org as a host +for an index page without requiring them to host any additional infrastructure +or purchase a TLS certificate. This email must also contain a link to the Terms +of Service for PyPI as many users may have signed up a long time ago and may +not recall what those terms are. Five months after the initial email, another email must be sent to any projects still relying on external hosting. This email will include all of the same information that the first email contained, except that the removal date will be one month away instead of six. -Finally a month later all projects will be switched to the ``pypa-only`` mode +Finally a month later all projects will be switched to the ``pypi-only`` mode and PyPI will be modified to remove the externally linked files functionality. +At this point in time any installers should finally remove any of the +deprecated PEP 438 functionality such as ``--allow-external`` and +``--allow-unverified`` in pip. + + +PIL +--- + +It's obvious from the numbers below that the vast bulk of the impact come from +the PIL project. On 2014-05-17 an email was sent to the contact for PIL +inquiring whether or not they would be willing to upload to PyPI. A response +has not been received as of yet (2014-06-05) nor has any change in the hosting +happened. Due to the popularity of PIL this PEP also proposes that during the +deprecation period that PyPI Administrators will set the PIL download URL as +the external index for that project. Allowing the users of PIL to take +advantage of the auto discovery mechanisms although the project has seemingly +become unmaintained. Impact ====== -============ ======= ========== ======= -\ PyPI External Total -============ ======= ========== ======= - **Safe** 37779 65 37844 - **Unsafe** 0 2974 2974 - **Total** 37779 3039 -============ ======= ========== ======= +The largest impact of this is going to be projects where the maintainers are +no longer maintaining the project, for one reason or another. For these +projects it's unlikely that a maintainer will arrive to set the external index +metadata which would allow the auto discovery mechanism to find it. + +Looking at the numbers factoring out PIL (which has been special cased above) +the actual impact should be quite low, with it affecting just 6.9% of projects +which host only externally or 2.8% which have their latest version hosted +externally. This represents a mere 3883 unique IP addresses. The break down of +this is that of those 3883 addresses, 100% of them installed something that +could not be verified while only 3% installed something which could be. + + +Projects Which Rely on Externally Hosted files +---------------------------------------------- + +This is determined by crawling the simple index and looking for installable +files using a similar detection method as pip and setuptools use. The "latest" +version is determined using ``pkg_resources.parse_version`` sort order. + +============ ======= ================ =================== ======= +\ PyPI External (old) External (latest) Total +============ ======= ================ =================== ======= + **Safe** 38716 31 35 38782 + **Unsafe** 0 1659 1169 2828 + **Total** 38716 1690 1204 41610 +============ ======= ================ =================== ======= + + +Top Externally Hosted Projects by Requests +------------------------------------------ + +This is determined by looking at the number of requests the +``/simple//`` page had gotten in a single day. The total number of +requests during that day was 17,960,467. + +============================== ======== +Project Requests +============================== ======== +PIL 13470 +mysql-connector-python 321 +salesforce-python-toolkit 54 +pyodbc 50 +elementtree 44 +atfork 39 +RBTools 29 +django-contrib-requestprovider 28 +wadofstuff-django-serializers 23 +Pygame 21 +============================== ======== + + +Top Externally Hosted Projects by Unique IPs +-------------------------------------------- + +This is determined by looking at the IP addresses of requests the +``/simple//`` page had gotten in a single day. The total number of +unique IP addresses during that day was 105,587. + +============================== ========== +Project Unique IPs +============================== ========== +PIL 3515 +mysql-connector-python 117 +pyodbc 34 +elementtree 21 +RBTools 19 +egenix-mx-base 16 +Pygame 14 +salesforce-python-toolkit 13 +django-contrib-requestprovider 12 +wxPython 11 +python-apt 10 +============================== ========== Rejected Proposals -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Fri Jun 6 04:06:15 2014 From: python-checkins at python.org (donald.stufft) Date: Fri, 6 Jun 2014 04:06:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_Specifically_mention_what_old?= =?utf-8?q?/latest_means?= Message-ID: <3gl6fH0PYWz7LjM@mail.python.org> http://hg.python.org/peps/rev/ce2e6512f7cc changeset: 5487:ce2e6512f7cc user: Donald Stufft date: Thu Jun 05 22:06:08 2014 -0400 summary: Specifically mention what old/latest means files: pep-0470.txt | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/pep-0470.txt b/pep-0470.txt --- a/pep-0470.txt +++ b/pep-0470.txt @@ -313,7 +313,9 @@ This is determined by crawling the simple index and looking for installable files using a similar detection method as pip and setuptools use. The "latest" -version is determined using ``pkg_resources.parse_version`` sort order. +version is determined using ``pkg_resources.parse_version`` sort order and it +is used to show whether or not the latest version is hosted externally or only +old versions are. ============ ======= ================ =================== ======= \ PyPI External (old) External (latest) Total -- Repository URL: http://hg.python.org/peps From root at python.org Fri Jun 6 07:05:24 2014 From: root at python.org (Cron Daemon) Date: Fri, 06 Jun 2014 07:05:24 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From root at python.org Fri Jun 6 07:10:22 2014 From: root at python.org (Cron Daemon) Date: Fri, 06 Jun 2014 07:10:22 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From python-checkins at python.org Fri Jun 6 08:28:03 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 6 Jun 2014 08:28:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNjcx?= =?utf-8?q?=2C_CVE-2014-0224=3A_Update_the_Windows_build_to_openssl-1=2E0?= =?utf-8?b?LjFo?= Message-ID: <3glDSM1HVhz7LjZ@mail.python.org> http://hg.python.org/cpython/rev/3dfdcc97250f changeset: 91048:3dfdcc97250f branch: 2.7 user: Zachary Ware date: Fri Jun 06 01:13:37 2014 -0500 summary: Issue #21671, CVE-2014-0224: Update the Windows build to openssl-1.0.1h files: Misc/NEWS | 6 ++++++ PCbuild/pyproject.vsprops | 2 +- PCbuild/readme.txt | 2 +- Tools/buildbot/external-common.bat | 5 +++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -73,6 +73,12 @@ - Issue #20635: Added tests for Tk geometry managers. +Windows +------- + +- Issue #21671, CVE-2014-0224: The bundled version of OpenSSL has been + updated to 1.0.1h. + What's New in Python 2.7.7 ========================== diff --git a/PCbuild/pyproject.vsprops b/PCbuild/pyproject.vsprops --- a/PCbuild/pyproject.vsprops +++ b/PCbuild/pyproject.vsprops @@ -82,7 +82,7 @@ /> http://hg.python.org/cpython/rev/79f3d25caac3 changeset: 91049:79f3d25caac3 branch: 3.4 parent: 91045:ead4dee062e3 user: Zachary Ware date: Fri Jun 06 01:23:53 2014 -0500 summary: Issue #21671, CVE-2014-0224: Update the Windows build to openssl-1.0.1h files: Misc/NEWS | 6 ++++++ PCbuild/pyproject.props | 2 +- PCbuild/readme.txt | 2 +- Tools/buildbot/external-common.bat | 8 ++++---- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,6 +92,12 @@ - Issue #21522: Added Tkinter tests for Listbox.itemconfigure(), PanedWindow.paneconfigure(), and Menu.entryconfigure(). +Windows +------- + +- Issue #21671, CVE-2014-0224: The bundled version of OpenSSL has been + updated to 1.0.1h. + What's New in Python 3.4.1? =========================== diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -20,7 +20,7 @@ $(externalsDir)\sqlite-3.8.3.1 $(externalsDir)\bzip2-1.0.6 $(externalsDir)\xz-5.0.5 - $(externalsDir)\openssl-1.0.1g + $(externalsDir)\openssl-1.0.1h $(externalsDir)\tcltk $(externalsDir)\tcltk64 $(tcltkDir)\lib\tcl86t.lib;$(tcltkDir)\lib\tk86t.lib diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -168,7 +168,7 @@ Homepage: http://tukaani.org/xz/ _ssl - Python wrapper for version 1.0.1g of the OpenSSL secure sockets + Python wrapper for version 1.0.1h of the OpenSSL secure sockets library, which is built by ssl.vcxproj Homepage: http://www.openssl.org/ diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -10,7 +10,7 @@ @rem if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 @rem if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 @rem if exist db-4.4.20 rd /s/q db-4.4.20 - at rem if exist openssl-1.0.1e rd /s/q openssl-1.0.1g + at rem if exist openssl-1.0.1h rd /s/q openssl-1.0.1h @rem if exist sqlite-3.7.12 rd /s/q sqlite-3.7.12 @rem bzip @@ -20,9 +20,9 @@ ) @rem OpenSSL -if not exist openssl-1.0.1g ( - rd /s/q openssl-1.0.1e - svn export http://svn.python.org/projects/external/openssl-1.0.1g +if not exist openssl-1.0.1h ( + rd /s/q openssl-1.0.1g + svn export http://svn.python.org/projects/external/openssl-1.0.1h ) @rem tcl/tk -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 6 08:28:05 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 6 Jun 2014 08:28:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321671=3A_Merge_with_3=2E4?= Message-ID: <3glDSP4tS2z7Lkw@mail.python.org> http://hg.python.org/cpython/rev/a32ced15b883 changeset: 91050:a32ced15b883 parent: 91046:3aa21b5b145a parent: 91049:79f3d25caac3 user: Zachary Ware date: Fri Jun 06 01:27:34 2014 -0500 summary: Issue #21671: Merge with 3.4 files: Misc/NEWS | 8 ++++++-- PCbuild/pyproject.props | 2 +- PCbuild/readme.txt | 2 +- Tools/buildbot/external-common.bat | 8 ++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -484,8 +484,6 @@ - Issue #21037: Add a build option to enable AddressSanitizer support. -- The Windows build now includes OpenSSL 1.0.1g - - Issue #19962: The Windows build process now creates "python.bat" in the root of the source tree, which passes all arguments through to the most recently built interpreter. @@ -607,6 +605,12 @@ - Issue #20535: PYTHONWARNING no longer affects the run_tests.py script. Patch by Arfrever Frehtes Taifersar Arahesis. +Windows +------- + +- Issue #21671, CVE-2014-0224: The bundled version of OpenSSL has been + updated to 1.0.1h. + What's New in Python 3.4.0? =========================== diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -20,7 +20,7 @@ $(externalsDir)\sqlite-3.8.3.1 $(externalsDir)\bzip2-1.0.6 $(externalsDir)\xz-5.0.5 - $(externalsDir)\openssl-1.0.1g + $(externalsDir)\openssl-1.0.1h $(externalsDir)\tcl-8.6.1.0 $(externalsDir)\tk-8.6.1.0 $(externalsDir)\tix-8.4.3.4 diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -168,7 +168,7 @@ Homepage: http://tukaani.org/xz/ _ssl - Python wrapper for version 1.0.1g of the OpenSSL secure sockets + Python wrapper for version 1.0.1h of the OpenSSL secure sockets library, which is built by ssl.vcxproj Homepage: http://www.openssl.org/ diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -10,7 +10,7 @@ @rem if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 @rem if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 @rem if exist db-4.4.20 rd /s/q db-4.4.20 - at rem if exist openssl-1.0.1e rd /s/q openssl-1.0.1g + at rem if exist openssl-1.0.1h rd /s/q openssl-1.0.1h @rem if exist sqlite-3.7.12 rd /s/q sqlite-3.7.12 @rem bzip @@ -20,9 +20,9 @@ ) @rem OpenSSL -if not exist openssl-1.0.1g ( - rd /s/q openssl-1.0.1e - svn export http://svn.python.org/projects/external/openssl-1.0.1g +if not exist openssl-1.0.1h ( + rd /s/q openssl-1.0.1g + svn export http://svn.python.org/projects/external/openssl-1.0.1h ) @rem tcl/tk/tix -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 6 13:57:16 2014 From: python-checkins at python.org (donald.stufft) Date: Fri, 6 Jun 2014 13:57:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_Add_additional_reasons_and_ex?= =?utf-8?q?plicitly_reject_the_=22next_steps=22_of_PEP_438?= Message-ID: <3glMmD3Ynzz7Llq@mail.python.org> http://hg.python.org/peps/rev/3128e9d38937 changeset: 5488:3128e9d38937 user: Donald Stufft date: Fri Jun 06 07:57:08 2014 -0400 summary: Add additional reasons and explicitly reject the "next steps" of PEP 438 files: pep-0470.txt | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-) diff --git a/pep-0470.txt b/pep-0470.txt --- a/pep-0470.txt +++ b/pep-0470.txt @@ -389,6 +389,9 @@ hosted. * Default to disallowing safely externally hosted files with only a global flag to enable them, but disallow unsafely hosted. +* Continue on the suggested path of PEP 438 and remove the option to unsafely + host externally but continue to allow the option to safely host externally. + These proposals are rejected because: @@ -454,6 +457,18 @@ or attempt to deploy to a server where their install will fail again until they add the "make it work" flag in their configuration file. +* The URL classification only works for a certain subset of projects, however + it does not allow for any project which needs additional restrictions such + as Access Controls. This means that there would be two methods of doing the + same thing, linking to a file safely and hosting an index. Hosting an index + works in all situations and by relying on this we make for a more consistent + experience no matter the reason for external hosting. + +* The safe external hosting option hampers the ability of PyPI to upgrade it's + security infrastructure. For instance if MD5 becomes broken in the future + there will be no way for PyPI to upgrade the hashes of the projects which + rely on safe external hosting via MD5 while files that are hosted on PyPI + can simply be processed over with a new hash function. Copyright ========= -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Fri Jun 6 16:14:52 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 6 Jun 2014 16:14:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Quash_extraneo?= =?utf-8?q?us_quote=2E?= Message-ID: <3glQq00d95z7LjZ@mail.python.org> http://hg.python.org/cpython/rev/119959d6ca0b changeset: 91051:119959d6ca0b branch: 3.4 parent: 91049:79f3d25caac3 user: Zachary Ware date: Fri Jun 06 09:13:18 2014 -0500 summary: Quash extraneous quote. files: Doc/c-api/unicode.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1624,7 +1624,7 @@ Compare a unicode object, *uni*, with *string* and return -1, 0, 1 for less than, equal, and greater than, respectively. It is best to pass only ASCII-encoded strings, but the function interprets the input string as - ISO-8859-1 if it contains non-ASCII characters". + ISO-8859-1 if it contains non-ASCII characters. .. c:function:: PyObject* PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 6 16:14:53 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 6 Jun 2014 16:14:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_quote_quashing=2E?= Message-ID: <3glQq121wqz7LjZ@mail.python.org> http://hg.python.org/cpython/rev/c0b74aa009f4 changeset: 91052:c0b74aa009f4 parent: 91050:a32ced15b883 parent: 91051:119959d6ca0b user: Zachary Ware date: Fri Jun 06 09:14:33 2014 -0500 summary: Merge quote quashing. files: Doc/c-api/unicode.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1624,7 +1624,7 @@ Compare a unicode object, *uni*, with *string* and return -1, 0, 1 for less than, equal, and greater than, respectively. It is best to pass only ASCII-encoded strings, but the function interprets the input string as - ISO-8859-1 if it contains non-ASCII characters". + ISO-8859-1 if it contains non-ASCII characters. .. c:function:: PyObject* PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 6 23:44:03 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 6 Jun 2014 23:44:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4OTEw?= =?utf-8?q?=3A_test=5FtextView_-_since_all_tests_require_=27gui=27=2C_make?= =?utf-8?q?_root_global=2E?= Message-ID: <3glcnH3rmRz7Lk3@mail.python.org> http://hg.python.org/cpython/rev/86ba41b7bb46 changeset: 91053:86ba41b7bb46 branch: 2.7 parent: 91048:3dfdcc97250f user: Terry Jan Reedy date: Fri Jun 06 17:43:14 2014 -0400 summary: Issue #18910: test_textView - since all tests require 'gui', make root global. Subclass TextViewer to add mock methods instead of monkey-patching it. files: Lib/idlelib/idle_test/test_textview.py | 71 ++++++------- 1 files changed, 35 insertions(+), 36 deletions(-) diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -1,58 +1,60 @@ -'''Test the functions and main class method of textView.py.''' +'''Test the functions and main class method of textView.py. + +Since all methods and functions create (or destroy) a TextViewer, which +is a widget containing multiple widgets, all tests must be gui tests. +Using mock Text would not change this. Other mocks are used to retrieve +information about calls. + +The coverage is essentially 100%. +''' +from test.test_support import requires +requires('gui') import unittest import os -from test.test_support import requires from Tkinter import Tk, Text, TclError from idlelib import textView as tv from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Mbox -orig_mbox = tv.tkMessageBox +def setUpModule(): + global root + root = Tk() -class textviewClassTest(unittest.TestCase): +def tearDownModule(): + global root + root.destroy() + del root - @classmethod - def setUpClass(cls): - requires('gui') - cls.root = Tk() - cls.TV = TV = tv.TextViewer - TV.transient = Func() - TV.grab_set = Func() - TV.wait_window = Func() - @classmethod - def tearDownClass(cls): - cls.root.destroy() - TV = cls.TV - del cls.root, cls.TV - del TV.transient, TV.grab_set, TV.wait_window +class TV(tv.TextViewer): # used by TextViewTest + transient = Func() + grab_set = Func() + wait_window = Func() + +class TextViewTest(unittest.TestCase): def setUp(self): - TV = self.TV TV.transient.__init__() TV.grab_set.__init__() TV.wait_window.__init__() - def test_init_modal(self): - TV = self.TV - view = TV(self.root, 'Title', 'test text') + view = TV(root, 'Title', 'test text') self.assertTrue(TV.transient.called) self.assertTrue(TV.grab_set.called) self.assertTrue(TV.wait_window.called) view.Ok() def test_init_nonmodal(self): - TV = self.TV - view = TV(self.root, 'Title', 'test text', modal=False) + view = TV(root, 'Title', 'test text', modal=False) self.assertFalse(TV.transient.called) self.assertFalse(TV.grab_set.called) self.assertFalse(TV.wait_window.called) view.Ok() def test_ok(self): - view = self.TV(self.root, 'Title', 'test text', modal=False) + view = TV(root, 'Title', 'test text', modal=False) view.destroy = Func() view.Ok() self.assertTrue(view.destroy.called) @@ -64,35 +66,32 @@ @classmethod def setUpClass(cls): - requires('gui') - cls.root = Tk() + cls.orig_mbox = tv.tkMessageBox tv.tkMessageBox = Mbox @classmethod def tearDownClass(cls): - cls.root.destroy() - del cls.root - tv.tkMessageBox = orig_mbox + tv.tkMessageBox = cls.orig_mbox + del cls.orig_mbox def test_view_text(self): # If modal True, tkinter will error with 'can't invoke "event" command' - view = tv.view_text(self.root, 'Title', 'test text', modal=False) + view = tv.view_text(root, 'Title', 'test text', modal=False) self.assertIsInstance(view, tv.TextViewer) def test_view_file(self): test_dir = os.path.dirname(__file__) testfile = os.path.join(test_dir, 'test_textview.py') - view = tv.view_file(self.root, 'Title', testfile, modal=False) + view = tv.view_file(root, 'Title', testfile, modal=False) self.assertIsInstance(view, tv.TextViewer) self.assertIn('Test', view.textView.get('1.0', '1.end')) view.Ok() # Mock messagebox will be used and view_file will not return anything testfile = os.path.join(test_dir, '../notthere.py') - view = tv.view_file(self.root, 'Title', testfile, modal=False) + view = tv.view_file(root, 'Title', testfile, modal=False) self.assertIsNone(view) + if __name__ == '__main__': - unittest.main(verbosity=2, exit=False) - from idlelib.idle_test.htest import run - run(TextViewer) + unittest.main(verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 6 23:44:04 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 6 Jun 2014 23:44:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE4OTEw?= =?utf-8?q?=3A_test=5FtextView_-_since_all_tests_require_=27gui=27=2C_make?= =?utf-8?q?_root_global=2E?= Message-ID: <3glcnJ6J0rz7LkC@mail.python.org> http://hg.python.org/cpython/rev/5a46ebfa5d90 changeset: 91054:5a46ebfa5d90 branch: 3.4 parent: 91051:119959d6ca0b user: Terry Jan Reedy date: Fri Jun 06 17:43:19 2014 -0400 summary: Issue #18910: test_textView - since all tests require 'gui', make root global. Subclass TextViewer to add mock methods instead of monkey-patching it. files: Lib/idlelib/idle_test/test_textview.py | 69 +++++++------ 1 files changed, 35 insertions(+), 34 deletions(-) diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -1,58 +1,60 @@ -'''Test the functions and main class method of textView.py.''' +'''Test the functions and main class method of textView.py. + +Since all methods and functions create (or destroy) a TextViewer, which +is a widget containing multiple widgets, all tests must be gui tests. +Using mock Text would not change this. Other mocks are used to retrieve +information about calls. + +The coverage is essentially 100%. +''' +from test.support import requires +requires('gui') import unittest import os -from test.support import requires from tkinter import Tk, Text, TclError from idlelib import textView as tv from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Mbox -orig_mbox = tv.tkMessageBox +def setUpModule(): + global root + root = Tk() + +def tearDownModule(): + global root + root.destroy() + del root + + +class TV(tv.TextViewer): # used by TextViewTest + transient = Func() + grab_set = Func() + wait_window = Func() class TextViewTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - requires('gui') - cls.root = Tk() - cls.TV = TV = tv.TextViewer - TV.transient = Func() - TV.grab_set = Func() - TV.wait_window = Func() - - @classmethod - def tearDownClass(cls): - cls.root.destroy() - TV = cls.TV - del cls.root, cls.TV - del TV.transient, TV.grab_set, TV.wait_window - def setUp(self): - TV = self.TV TV.transient.__init__() TV.grab_set.__init__() TV.wait_window.__init__() - def test_init_modal(self): - TV = self.TV - view = TV(self.root, 'Title', 'test text') + view = TV(root, 'Title', 'test text') self.assertTrue(TV.transient.called) self.assertTrue(TV.grab_set.called) self.assertTrue(TV.wait_window.called) view.Ok() def test_init_nonmodal(self): - TV = self.TV - view = TV(self.root, 'Title', 'test text', modal=False) + view = TV(root, 'Title', 'test text', modal=False) self.assertFalse(TV.transient.called) self.assertFalse(TV.grab_set.called) self.assertFalse(TV.wait_window.called) view.Ok() def test_ok(self): - view = self.TV(self.root, 'Title', 'test text', modal=False) + view = TV(root, 'Title', 'test text', modal=False) view.destroy = Func() view.Ok() self.assertTrue(view.destroy.called) @@ -64,33 +66,32 @@ @classmethod def setUpClass(cls): - requires('gui') - cls.root = Tk() + cls.orig_mbox = tv.tkMessageBox tv.tkMessageBox = Mbox @classmethod def tearDownClass(cls): - cls.root.destroy() - del cls.root - tv.tkMessageBox = orig_mbox + tv.tkMessageBox = cls.orig_mbox + del cls.orig_mbox def test_view_text(self): # If modal True, tkinter will error with 'can't invoke "event" command' - view = tv.view_text(self.root, 'Title', 'test text', modal=False) + view = tv.view_text(root, 'Title', 'test text', modal=False) self.assertIsInstance(view, tv.TextViewer) def test_view_file(self): test_dir = os.path.dirname(__file__) testfile = os.path.join(test_dir, 'test_textview.py') - view = tv.view_file(self.root, 'Title', testfile, modal=False) + view = tv.view_file(root, 'Title', testfile, modal=False) self.assertIsInstance(view, tv.TextViewer) self.assertIn('Test', view.textView.get('1.0', '1.end')) view.Ok() # Mock messagebox will be used and view_file will not return anything testfile = os.path.join(test_dir, '../notthere.py') - view = tv.view_file(self.root, 'Title', testfile, modal=False) + view = tv.view_file(root, 'Title', testfile, modal=False) self.assertIsNone(view) + if __name__ == '__main__': unittest.main(verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 6 23:44:06 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 6 Jun 2014 23:44:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3glcnL22b7z7Llx@mail.python.org> http://hg.python.org/cpython/rev/585ad5d806bd changeset: 91055:585ad5d806bd parent: 91052:c0b74aa009f4 parent: 91054:5a46ebfa5d90 user: Terry Jan Reedy date: Fri Jun 06 17:43:37 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/idle_test/test_textview.py | 69 +++++++------ 1 files changed, 35 insertions(+), 34 deletions(-) diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -1,58 +1,60 @@ -'''Test the functions and main class method of textView.py.''' +'''Test the functions and main class method of textView.py. + +Since all methods and functions create (or destroy) a TextViewer, which +is a widget containing multiple widgets, all tests must be gui tests. +Using mock Text would not change this. Other mocks are used to retrieve +information about calls. + +The coverage is essentially 100%. +''' +from test.support import requires +requires('gui') import unittest import os -from test.support import requires from tkinter import Tk, Text, TclError from idlelib import textView as tv from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Mbox -orig_mbox = tv.tkMessageBox +def setUpModule(): + global root + root = Tk() + +def tearDownModule(): + global root + root.destroy() + del root + + +class TV(tv.TextViewer): # used by TextViewTest + transient = Func() + grab_set = Func() + wait_window = Func() class TextViewTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - requires('gui') - cls.root = Tk() - cls.TV = TV = tv.TextViewer - TV.transient = Func() - TV.grab_set = Func() - TV.wait_window = Func() - - @classmethod - def tearDownClass(cls): - cls.root.destroy() - TV = cls.TV - del cls.root, cls.TV - del TV.transient, TV.grab_set, TV.wait_window - def setUp(self): - TV = self.TV TV.transient.__init__() TV.grab_set.__init__() TV.wait_window.__init__() - def test_init_modal(self): - TV = self.TV - view = TV(self.root, 'Title', 'test text') + view = TV(root, 'Title', 'test text') self.assertTrue(TV.transient.called) self.assertTrue(TV.grab_set.called) self.assertTrue(TV.wait_window.called) view.Ok() def test_init_nonmodal(self): - TV = self.TV - view = TV(self.root, 'Title', 'test text', modal=False) + view = TV(root, 'Title', 'test text', modal=False) self.assertFalse(TV.transient.called) self.assertFalse(TV.grab_set.called) self.assertFalse(TV.wait_window.called) view.Ok() def test_ok(self): - view = self.TV(self.root, 'Title', 'test text', modal=False) + view = TV(root, 'Title', 'test text', modal=False) view.destroy = Func() view.Ok() self.assertTrue(view.destroy.called) @@ -64,33 +66,32 @@ @classmethod def setUpClass(cls): - requires('gui') - cls.root = Tk() + cls.orig_mbox = tv.tkMessageBox tv.tkMessageBox = Mbox @classmethod def tearDownClass(cls): - cls.root.destroy() - del cls.root - tv.tkMessageBox = orig_mbox + tv.tkMessageBox = cls.orig_mbox + del cls.orig_mbox def test_view_text(self): # If modal True, tkinter will error with 'can't invoke "event" command' - view = tv.view_text(self.root, 'Title', 'test text', modal=False) + view = tv.view_text(root, 'Title', 'test text', modal=False) self.assertIsInstance(view, tv.TextViewer) def test_view_file(self): test_dir = os.path.dirname(__file__) testfile = os.path.join(test_dir, 'test_textview.py') - view = tv.view_file(self.root, 'Title', testfile, modal=False) + view = tv.view_file(root, 'Title', testfile, modal=False) self.assertIsInstance(view, tv.TextViewer) self.assertIn('Test', view.textView.get('1.0', '1.end')) view.Ok() # Mock messagebox will be used and view_file will not return anything testfile = os.path.join(test_dir, '../notthere.py') - view = tv.view_file(self.root, 'Title', testfile, modal=False) + view = tv.view_file(root, 'Title', testfile, modal=False) self.assertIsNone(view) + if __name__ == '__main__': unittest.main(verbosity=2) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Jun 7 10:52:42 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 07 Jun 2014 10:52:42 +0200 Subject: [Python-checkins] Daily reference leaks (585ad5d806bd): sum=11 Message-ID: results for 585ad5d806bd on branch "default" -------------------------------------------- test_collections leaked [2, 2, 0] references, sum=4 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_site leaked [2, 0, 0] references, sum=2 test_site leaked [2, 0, 0] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog24Nqhr', '-x'] From python-checkins at python.org Sat Jun 7 15:22:29 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 7 Jun 2014 15:22:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjY3?= =?utf-8?q?=3A_Clarify_string_data_model_description?= Message-ID: <3gm1c53DXjz7LkC@mail.python.org> http://hg.python.org/cpython/rev/6ffb6909c439 changeset: 91056:6ffb6909c439 branch: 3.4 parent: 91054:5a46ebfa5d90 user: Nick Coghlan date: Sat Jun 07 23:21:14 2014 +1000 summary: Issue #21667: Clarify string data model description files: Doc/reference/datamodel.rst | 19 ++++++++++--------- 1 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -285,16 +285,17 @@ single: integer single: Unicode - A string is a sequence of values that represent Unicode codepoints. - All the codepoints in range ``U+0000 - U+10FFFF`` can be represented - in a string. Python doesn't have a :c:type:`chr` type, and - every character in the string is represented as a string object - with length ``1``. The built-in function :func:`ord` converts a - character to its codepoint (as an integer); :func:`chr` converts - an integer in range ``0 - 10FFFF`` to the corresponding character. + A string is a sequence of values that represent Unicode code points. + All the code points in the range ``U+0000 - U+10FFFF`` can be + represented in a string. Python doesn't have a :c:type:`char` type; + instead, every code point in the string is represented as a string + object with length ``1``. The built-in function :func:`ord` + converts a code point from its string form to an integer in the + range ``0 - 10FFFF``; :func:`chr` converts an integer in the range + ``0 - 10FFFF`` to the corresponding length ``1`` string object. :meth:`str.encode` can be used to convert a :class:`str` to - :class:`bytes` using the given encoding, and :meth:`bytes.decode` can - be used to achieve the opposite. + :class:`bytes` using the given text encoding, and + :meth:`bytes.decode` can be used to achieve the opposite. Tuples .. index:: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 15:22:30 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 7 Jun 2014 15:22:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_issue_=2321667_from_3=2E4?= Message-ID: <3gm1c64vtpz7LlZ@mail.python.org> http://hg.python.org/cpython/rev/7c120e77d6f7 changeset: 91057:7c120e77d6f7 parent: 91055:585ad5d806bd parent: 91056:6ffb6909c439 user: Nick Coghlan date: Sat Jun 07 23:22:06 2014 +1000 summary: Merge issue #21667 from 3.4 files: Doc/reference/datamodel.rst | 19 ++++++++++--------- 1 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -285,16 +285,17 @@ single: integer single: Unicode - A string is a sequence of values that represent Unicode codepoints. - All the codepoints in range ``U+0000 - U+10FFFF`` can be represented - in a string. Python doesn't have a :c:type:`chr` type, and - every character in the string is represented as a string object - with length ``1``. The built-in function :func:`ord` converts a - character to its codepoint (as an integer); :func:`chr` converts - an integer in range ``0 - 10FFFF`` to the corresponding character. + A string is a sequence of values that represent Unicode code points. + All the code points in the range ``U+0000 - U+10FFFF`` can be + represented in a string. Python doesn't have a :c:type:`char` type; + instead, every code point in the string is represented as a string + object with length ``1``. The built-in function :func:`ord` + converts a code point from its string form to an integer in the + range ``0 - 10FFFF``; :func:`chr` converts an integer in the range + ``0 - 10FFFF`` to the corresponding length ``1`` string object. :meth:`str.encode` can be used to convert a :class:`str` to - :class:`bytes` using the given encoding, and :meth:`bytes.decode` can - be used to achieve the opposite. + :class:`bytes` using the given text encoding, and + :meth:`bytes.decode` can be used to achieve the opposite. Tuples .. index:: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 15:36:35 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 7 Jun 2014 15:36:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Updates_to_Pyt?= =?utf-8?q?hon_2=2E7_What=27s_New_preamble?= Message-ID: <3gm1wM4C8lz7LkM@mail.python.org> http://hg.python.org/cpython/rev/7c28b3a92f40 changeset: 91058:7c28b3a92f40 branch: 2.7 parent: 91053:86ba41b7bb46 user: Nick Coghlan date: Sat Jun 07 23:36:13 2014 +1000 summary: Updates to Python 2.7 What's New preamble - refers to release PEP for lifecycle information - refers to Python Package Index for migration tools - covers enhancements added in maintenance releases Closes issue #21569 files: Doc/whatsnew/2.7.rst | 172 +++++++++++++++++++++++------- 1 files changed, 133 insertions(+), 39 deletions(-) diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -79,45 +79,91 @@ The Future for Python 2.x ========================= -Python 2.7 is intended to be the last major release in the 2.x series. -The Python maintainers are planning to focus their future efforts on -the Python 3.x series. - -This means that 2.7 will remain in place for a long time, running -production systems that have not been ported to Python 3.x. -Two consequences of the long-term significance of 2.7 are: - -* It's very likely the 2.7 release will have a longer period of - maintenance compared to earlier 2.x versions. Python 2.7 will - continue to be maintained while the transition to 3.x continues, and - the developers are planning to support Python 2.7 with bug-fix - releases beyond the typical two years. - -* A policy decision was made to silence warnings only of interest to - developers. :exc:`DeprecationWarning` and its - descendants are now ignored unless otherwise requested, preventing - users from seeing warnings triggered by an application. This change - was also made in the branch that will become Python 3.2. (Discussed - on stdlib-sig and carried out in :issue:`7319`.) - - In previous releases, :exc:`DeprecationWarning` messages were - enabled by default, providing Python developers with a clear - indication of where their code may break in a future major version - of Python. - - However, there are increasingly many users of Python-based - applications who are not directly involved in the development of - those applications. :exc:`DeprecationWarning` messages are - irrelevant to such users, making them worry about an application - that's actually working correctly and burdening application developers - with responding to these concerns. - - You can re-enable display of :exc:`DeprecationWarning` messages by - running Python with the :option:`-Wdefault <-W>` (short form: - :option:`-Wd <-W>`) switch, or by setting the :envvar:`PYTHONWARNINGS` - environment variable to ``"default"`` (or ``"d"``) before running - Python. Python code can also re-enable them - by calling ``warnings.simplefilter('default')``. +Python 2.7 is the last major release in the 2.x series, as the Python +maintainers have shifted the focus of their new feature development efforts +to the Python 3.x series. This means that while Python 2 continues to +receive bug fixes, and to be updated to build correctly on new hardware and +versions of supported operated systems, there will be no new full feature +releases for the language or standard library. + +However, while there is a large common subset between Python 2.7 and Python +3, and many of the changes involved in migrating to that common subset, or +directly to Python 3, can be safely automated, some other changes (notably +those associated with Unicode handling) may require careful consideration, +and preferably robust automated regression test suites, to migrate +effectively. + +This means that Python 2.7 will remain in place for a long time, providing a +stable and supported base platform for production systems that have not yet +been ported to Python 3. The full expected lifecycle of the Python 2.7 +series is detailed in :pep:`373`. + +Some key consequences of the long-term significance of 2.7 are: + +* As noted above, the 2.7 release has a much longer period of maintenance + when compared to earlier 2.x versions. Python 2.7 is currently expected to + remain supported by the core development team (receiving security updates + and other bug fixes) until at least 2020 (10 years after its initial + release, compared to the more typical support period of 18-24 months). + +* As the Python 2.7 standard library ages, making effective use of the + Python Package Index (either directly or via a redistributor) becomes + more important for Python 2 users. In addition to a wide variety of third + party packages for various tasks, the available packages include backports + of new modules and features from the Python 3 standard library that are + compatible with Python 2, as well as various tools and libraries that can + make it easier to migrate to Python 3. The `Python Packaging User Guide + `__ provides guidance on downloading and + installing software from the Python Package Index. + +* While the preferred approach to enhancing Python 2 is now the publication + of new packages on the Python Package Index, this approach doesn't + necessarily work in all cases, especially those related to network + security. In exceptional cases that cannot be handled adequately by + publishing new or updated packages on PyPI, the Python Enhancement + Proposal process may be used to make the case for adding new features + directly to the Python 2 standard library. Any such additions, and the + maintenance releases where they were added, will be noted in the + :ref:`py27-maintenance-enhancements` section below. + +For projects wishing to migrate from Python 2 to Python 3, or for library +and framework developers wishing to support users on both Python 2 and +Python 3, there are a variety of tools and guides available to help decide +on a suitable approach and manage some of the technical details involved. +The recommended starting point is the :ref:`pyporting-howto` HOWTO guide. + + +Changes to the Handling of Deprecation Warnings +=============================================== + +For Python 2.7, a policy decision was made to silence warnings only of +interest to developers by default. :exc:`DeprecationWarning` and its +descendants are now ignored unless otherwise requested, preventing +users from seeing warnings triggered by an application. This change +was also made in the branch that became Python 3.2. (Discussed +on stdlib-sig and carried out in :issue:`7319`.) + +In previous releases, :exc:`DeprecationWarning` messages were +enabled by default, providing Python developers with a clear +indication of where their code may break in a future major version +of Python. + +However, there are increasingly many users of Python-based +applications who are not directly involved in the development of +those applications. :exc:`DeprecationWarning` messages are +irrelevant to such users, making them worry about an application +that's actually working correctly and burdening application developers +with responding to these concerns. + +You can re-enable display of :exc:`DeprecationWarning` messages by +running Python with the :option:`-Wdefault <-W>` (short form: +:option:`-Wd <-W>`) switch, or by setting the :envvar:`PYTHONWARNINGS` +environment variable to ``"default"`` (or ``"d"``) before running +Python. Python code can also re-enable them +by calling ``warnings.simplefilter('default')``. + +The ``unittest`` module also automatically reenables deprecation warnings +when running tests. Python 3.1 Features @@ -2464,6 +2510,54 @@ .. ====================================================================== +.. _py27-maintenance-enhancements: + +New Features Added to Python 2.7 Maintenance Releases +===================================================== + +New features may be added to Python 2.7 maintenance releases when the +situation genuinely calls for it. Any such additions must go through +the Python Enhancement Proposal process, and make a compelling case for why +they can't be adequately addressed by either adding the new feature solely to +Python 3, or else by publishing it on the Python Package Index. + +In addition to the specific proposals listed below, there is a general +exemption allowing new ``-3`` warnings to be added in any Python 2.7 +maintenance release. + + +PEP 434: IDLE Enhancement Exception for All Branches +---------------------------------------------------- + +:pep:`434` describes a general exemption for changes made to the IDLE +development environment shipped along with Python. This exemption makes it +possible for the IDLE developers to provide a more consistent user +experience across all supported versions of Python 2 and 3. + +For details of any IDLE changes, refer to the NEWS file for the specific +release. + + +PEP 466: Network Security Enhancements for Python 2.7 +----------------------------------------------------- + +:pep:`466` describes a number of network security enhancement proposals +that have been approved for inclusion in Python 2.7 maintenance releases, +with the first of those changes appearing in the Python 2.7.7 release. + +:pep:`466` related features added in Python 2.7.7: + +* :func:`hmac.compare_digest` was added to make a timing attack resistant + comparison operation broadly available to Python 2 applications + (backported by Alex Gaynor in :issue:`21306`) + +* the version of OpenSSL linked with the prebuilt Windows installers + published on python.org was updated to 1.0.1g (contributed by + Zachary Ware in :issue:`21462`) + + +.. ====================================================================== + .. _acks27: Acknowledgements -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 16:00:33 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 7 Jun 2014 16:00:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNTY5?= =?utf-8?q?=3A_sync_Python_2=2E7_What=27s_New_with_2=2E7_version?= Message-ID: <3gm2S12JWQz7Lld@mail.python.org> http://hg.python.org/cpython/rev/d23cea976f46 changeset: 91059:d23cea976f46 branch: 3.4 parent: 91056:6ffb6909c439 user: Nick Coghlan date: Sat Jun 07 23:43:00 2014 +1000 summary: Issue #21569: sync Python 2.7 What's New with 2.7 version files: Doc/whatsnew/2.7.rst | 318 +++++++++++++++++++----------- 1 files changed, 203 insertions(+), 115 deletions(-) diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -7,7 +7,6 @@ .. hyperlink all the methods & functions. .. T_STRING_INPLACE not described in main docs -.. "Format String Syntax" in string.rst could use many more examples. .. $Id$ Rules for maintenance: @@ -50,17 +49,16 @@ This saves the maintainer some effort going through the SVN logs when researching a change. -This article explains the new features in Python 2.7. The final -release of 2.7 is currently scheduled for July 2010; the detailed -schedule is described in :pep:`373`. +This article explains the new features in Python 2.7. Python 2.7 was released +on July 3, 2010. Numeric handling has been improved in many ways, for both -floating-point numbers and for the :class:`Decimal` class. There are -some useful additions to the standard library, such as a greatly -enhanced :mod:`unittest` module, the :mod:`argparse` module for -parsing command-line options, convenient ordered-dictionary and -:class:`Counter` classes in the :mod:`collections` module, and many -other improvements. +floating-point numbers and for the :class:`~decimal.Decimal` class. +There are some useful additions to the standard library, such as a +greatly enhanced :mod:`unittest` module, the :mod:`argparse` module +for parsing command-line options, convenient :class:`~collections.OrderedDict` +and :class:`~collections.Counter` classes in the :mod:`collections` module, +and many other improvements. Python 2.7 is planned to be the last of the 2.x releases, so we worked on making it a good release for the long term. To help with porting @@ -81,45 +79,91 @@ The Future for Python 2.x ========================= -Python 2.7 is intended to be the last major release in the 2.x series. -The Python maintainers are planning to focus their future efforts on -the Python 3.x series. - -This means that 2.7 will remain in place for a long time, running -production systems that have not been ported to Python 3.x. -Two consequences of the long-term significance of 2.7 are: - -* It's very likely the 2.7 release will have a longer period of - maintenance compared to earlier 2.x versions. Python 2.7 will - continue to be maintained while the transition to 3.x continues, and - the developers are planning to support Python 2.7 with bug-fix - releases beyond the typical two years. - -* A policy decision was made to silence warnings only of interest to - developers. :exc:`DeprecationWarning` and its - descendants are now ignored unless otherwise requested, preventing - users from seeing warnings triggered by an application. This change - was also made in the branch that will become Python 3.2. (Discussed - on stdlib-sig and carried out in :issue:`7319`.) - - In previous releases, :exc:`DeprecationWarning` messages were - enabled by default, providing Python developers with a clear - indication of where their code may break in a future major version - of Python. - - However, there are increasingly many users of Python-based - applications who are not directly involved in the development of - those applications. :exc:`DeprecationWarning` messages are - irrelevant to such users, making them worry about an application - that's actually working correctly and burdening application developers - with responding to these concerns. - - You can re-enable display of :exc:`DeprecationWarning` messages by - running Python with the :option:`-Wdefault` (short form: - :option:`-Wd`) switch, or by setting the :envvar:`PYTHONWARNINGS` - environment variable to ``"default"`` (or ``"d"``) before running - Python. Python code can also re-enable them - by calling ``warnings.simplefilter('default')``. +Python 2.7 is the last major release in the 2.x series, as the Python +maintainers have shifted the focus of their new feature development efforts +to the Python 3.x series. This means that while Python 2 continues to +receive bug fixes, and to be updated to build correctly on new hardware and +versions of supported operated systems, there will be no new full feature +releases for the language or standard library. + +However, while there is a large common subset between Python 2.7 and Python +3, and many of the changes involved in migrating to that common subset, or +directly to Python 3, can be safely automated, some other changes (notably +those associated with Unicode handling) may require careful consideration, +and preferably robust automated regression test suites, to migrate +effectively. + +This means that Python 2.7 will remain in place for a long time, providing a +stable and supported base platform for production systems that have not yet +been ported to Python 3. The full expected lifecycle of the Python 2.7 +series is detailed in :pep:`373`. + +Some key consequences of the long-term significance of 2.7 are: + +* As noted above, the 2.7 release has a much longer period of maintenance + when compared to earlier 2.x versions. Python 2.7 is currently expected to + remain supported by the core development team (receiving security updates + and other bug fixes) until at least 2020 (10 years after its initial + release, compared to the more typical support period of 18-24 months). + +* As the Python 2.7 standard library ages, making effective use of the + Python Package Index (either directly or via a redistributor) becomes + more important for Python 2 users. In addition to a wide variety of third + party packages for various tasks, the available packages include backports + of new modules and features from the Python 3 standard library that are + compatible with Python 2, as well as various tools and libraries that can + make it easier to migrate to Python 3. The `Python Packaging User Guide + `__ provides guidance on downloading and + installing software from the Python Package Index. + +* While the preferred approach to enhancing Python 2 is now the publication + of new packages on the Python Package Index, this approach doesn't + necessarily work in all cases, especially those related to network + security. In exceptional cases that cannot be handled adequately by + publishing new or updated packages on PyPI, the Python Enhancement + Proposal process may be used to make the case for adding new features + directly to the Python 2 standard library. Any such additions, and the + maintenance releases where they were added, will be noted in the + :ref:`py27-maintenance-enhancements` section below. + +For projects wishing to migrate from Python 2 to Python 3, or for library +and framework developers wishing to support users on both Python 2 and +Python 3, there are a variety of tools and guides available to help decide +on a suitable approach and manage some of the technical details involved. +The recommended starting point is the :ref:`pyporting-howto` HOWTO guide. + + +Changes to the Handling of Deprecation Warnings +=============================================== + +For Python 2.7, a policy decision was made to silence warnings only of +interest to developers by default. :exc:`DeprecationWarning` and its +descendants are now ignored unless otherwise requested, preventing +users from seeing warnings triggered by an application. This change +was also made in the branch that became Python 3.2. (Discussed +on stdlib-sig and carried out in :issue:`7319`.) + +In previous releases, :exc:`DeprecationWarning` messages were +enabled by default, providing Python developers with a clear +indication of where their code may break in a future major version +of Python. + +However, there are increasingly many users of Python-based +applications who are not directly involved in the development of +those applications. :exc:`DeprecationWarning` messages are +irrelevant to such users, making them worry about an application +that's actually working correctly and burdening application developers +with responding to these concerns. + +You can re-enable display of :exc:`DeprecationWarning` messages by +running Python with the :option:`-Wdefault <-W>` (short form: +:option:`-Wd <-W>`) switch, or by setting the :envvar:`PYTHONWARNINGS` +environment variable to ``"default"`` (or ``"d"``) before running +Python. Python code can also re-enable them +by calling ``warnings.simplefilter('default')``. + +The ``unittest`` module also automatically reenables deprecation warnings +when running tests. Python 3.1 Features @@ -133,7 +177,7 @@ A partial list of 3.1 features that were backported to 2.7: * The syntax for set literals (``{1,2,3}`` is a mutable set). -* Dictionary and set comprehensions (``{ i: i*2 for i in range(3)}``). +* Dictionary and set comprehensions (``{i: i*2 for i in range(3)}``). * Multiple context managers in a single :keyword:`with` statement. * A new version of the :mod:`io` library, rewritten in C for performance. * The ordered-dictionary type described in :ref:`pep-0372`. @@ -155,7 +199,7 @@ * :func:`operator.isCallable` and :func:`operator.sequenceIncludes`, which are not supported in 3.x, now trigger warnings. * The :option:`-3` switch now automatically - enables the :option:`-Qwarn` switch that causes warnings + enables the :option:`-Qwarn <-Q>` switch that causes warnings about using classic division with integers and long integers. @@ -390,9 +434,10 @@ .. seealso:: - `argparse module documentation `__ - - `Upgrading optparse code to use argparse `__ + :mod:`argparse` documentation + The documentation page of the argparse module. + + :ref:`argparse-from-optparse` Part of the Python documentation, describing how to convert code that uses :mod:`optparse`. @@ -402,8 +447,6 @@ PEP 391: Dictionary-Based Configuration For Logging ==================================================== -.. XXX not documented in library reference yet; add link here once it's added. - The :mod:`logging` module is very flexible; applications can define a tree of logging subsystems, and each logger in this tree can filter out certain messages, format them differently, and direct messages to @@ -412,21 +455,21 @@ All this flexibility can require a lot of configuration. You can write Python statements to create objects and set their properties, but a complex set-up requires verbose but boring code. -:mod:`logging` also supports a :func:`~logging.config.fileConfig` +:mod:`logging` also supports a :func:`~logging.fileConfig` function that parses a file, but the file format doesn't support configuring filters, and it's messier to generate programmatically. -Python 2.7 adds a :func:`~logging.config.dictConfig` function that +Python 2.7 adds a :func:`~logging.dictConfig` function that uses a dictionary to configure logging. There are many ways to produce a dictionary from different sources: construct one with code; parse a file containing JSON; or use a YAML parsing library if one is -installed. +installed. For more information see :ref:`logging-config-api`. The following example configures two loggers, the root logger and a -logger named "network". Messages sent to the root logger will be +logger named "network". Messages sent to the root logger will be sent to the system log using the syslog protocol, and messages to the "network" logger will be written to a :file:`network.log` file -that will be rotated once the log reaches 1Mb. +that will be rotated once the log reaches 1MB. :: @@ -445,7 +488,7 @@ 'filename': '/logs/network.log', 'formatter': 'standard', 'level': 'INFO', - 'maxBytes': 1024*1024}, + 'maxBytes': 1000000}, 'syslog': {'class': 'logging.handlers.SysLogHandler', 'formatter': 'standard', 'level': 'ERROR'}}, @@ -483,16 +526,19 @@ for UDP or :const:`socket.SOCK_STREAM` for TCP. The default protocol remains UDP. -* :class:`Logger` instances gained a :meth:`getChild` method that retrieves a - descendant logger using a relative path. For example, - once you retrieve a logger by doing ``log = getLogger('app')``, +* :class:`~logging.Logger` instances gained a :meth:`~logging.Logger.getChild` + method that retrieves a descendant logger using a relative path. + For example, once you retrieve a logger by doing ``log = getLogger('app')``, calling ``log.getChild('network.listen')`` is equivalent to ``getLogger('app.network.listen')``. -* The :class:`LoggerAdapter` class gained a :meth:`isEnabledFor` method - that takes a *level* and returns whether the underlying logger would +* The :class:`~logging.LoggerAdapter` class gained a + :meth:`~logging.LoggerAdapter.isEnabledFor` method that takes a + *level* and returns whether the underlying logger would process a message of that level of importance. +.. XXX: Logger objects don't have a class declaration so the link don't work + .. seealso:: :pep:`391` - Dictionary-Based Configuration For Logging @@ -501,14 +547,15 @@ PEP 3106: Dictionary Views ==================================================== -The dictionary methods :meth:`keys`, :meth:`values`, and :meth:`items` -are different in Python 3.x. They return an object called a :dfn:`view` -instead of a fully materialized list. - -It's not possible to change the return values of :meth:`keys`, -:meth:`values`, and :meth:`items` in Python 2.7 because too much code -would break. Instead the 3.x versions were added under the new names -:meth:`viewkeys`, :meth:`viewvalues`, and :meth:`viewitems`. +The dictionary methods :meth:`~dict.keys`, :meth:`~dict.values`, and +:meth:`~dict.items` are different in Python 3.x. They return an object +called a :dfn:`view` instead of a fully materialized list. + +It's not possible to change the return values of :meth:`~dict.keys`, +:meth:`~dict.values`, and :meth:`~dict.items` in Python 2.7 because +too much code would break. Instead the 3.x versions were added +under the new names :meth:`~dict.viewkeys`, :meth:`~dict.viewvalues`, +and :meth:`~dict.viewitems`. :: @@ -550,8 +597,8 @@ RuntimeError: dictionary changed size during iteration You can use the view methods in Python 2.x code, and the 2to3 -converter will change them to the standard :meth:`keys`, -:meth:`values`, and :meth:`items` methods. +converter will change them to the standard :meth:`~dict.keys`, +:meth:`~dict.values`, and :meth:`~dict.items` methods. .. seealso:: @@ -624,7 +671,7 @@ ``{}`` continues to represent an empty dictionary; use ``set()`` for an empty set. - >>> {1,2,3,4,5} + >>> {1, 2, 3, 4, 5} set([1, 2, 3, 4, 5]) >>> set() # empty set set([]) @@ -794,7 +841,7 @@ ``None`` as its first argument. (Fixed by Georg Brandl; :issue:`4759`.) - .. bytearray doesn't seem to be documented + .. XXX bytearray doesn't seem to be documented * When using ``@classmethod`` and ``@staticmethod`` to wrap methods as class or static methods, the wrapper object now @@ -867,12 +914,6 @@ Several performance enhancements have been added: -.. * A new :program:`configure` option, :option:`--with-computed-gotos`, - compiles the main bytecode interpreter loop using a new dispatch - mechanism that gives speedups of up to 20%, depending on the system - and benchmark. The new mechanism is only supported on certain - compilers, such as gcc, SunPro, and icc. - * A new opcode was added to perform the initial setup for :keyword:`with` statements, looking up the :meth:`__enter__` and :meth:`__exit__` methods. (Contributed by Benjamin Peterson.) @@ -1054,7 +1095,7 @@ :meth:`~collections.deque.count` method that returns the number of contained elements equal to the supplied argument *x*, and a :meth:`~collections.deque.reverse` method that reverses the elements - of the deque in-place. :class:`deque` also exposes its maximum + of the deque in-place. :class:`~collections.deque` also exposes its maximum length as the read-only :attr:`~collections.deque.maxlen` attribute. (Both features added by Raymond Hettinger.) @@ -1135,15 +1176,14 @@ ``Decimal('0.1000000000000000055511151231257827021181583404541015625')``. (Implemented by Raymond Hettinger; :issue:`4796`.) - Comparing instances of :class:`Decimal` with floating-point + Comparing instances of :class:`~decimal.Decimal` with floating-point numbers now produces sensible results based on the numeric values of the operands. Previously such comparisons would fall back to Python's default rules for comparing objects, which produced arbitrary results based on their type. Note that you still cannot combine :class:`Decimal` and floating-point in other operations such as addition, since you should be explicitly choosing how to convert between float and - :class:`Decimal`. - (Fixed by Mark Dickinson; :issue:`2531`.) + :class:`~decimal.Decimal`. (Fixed by Mark Dickinson; :issue:`2531`.) The constructor for :class:`~decimal.Decimal` now accepts floating-point numbers (added by Raymond Hettinger; :issue:`8257`) @@ -1195,8 +1235,8 @@ Ordering comparisons (``<``, ``<=``, ``>``, ``>=``) between fractions and complex numbers now raise a :exc:`TypeError`. - This fixes an oversight, making the :class:`Fraction` match the other - numeric types. + This fixes an oversight, making the :class:`~fractions.Fraction` + match the other numeric types. .. revision 79455 @@ -1210,7 +1250,7 @@ uploads thanks to an added *rest* parameter (patch by Pablo Mouzo; :issue:`6845`.) -* New class decorator: :func:`total_ordering` in the :mod:`functools` +* New class decorator: :func:`~functools.total_ordering` in the :mod:`functools` module takes a class that defines an :meth:`__eq__` method and one of :meth:`__lt__`, :meth:`__le__`, :meth:`__gt__`, or :meth:`__ge__`, and generates the missing comparison methods. Since the @@ -1218,7 +1258,7 @@ this decorator makes it easier to define ordered classes. (Added by Raymond Hettinger; :issue:`5479`.) - New function: :func:`cmp_to_key` will take an old-style comparison + New function: :func:`~functools.cmp_to_key` will take an old-style comparison function that expects two arguments and return a new callable that can be used as the *key* parameter to functions such as :func:`sorted`, :func:`min` and :func:`max`, etc. The primary @@ -1345,7 +1385,7 @@ with any object literal that decodes to a list of pairs. (Contributed by Raymond Hettinger; :issue:`5381`.) -* The :mod:`mailbox` module's :class:`Maildir` class now records the +* The :mod:`mailbox` module's :class:`~mailbox.Maildir` class now records the timestamp on the directories it reads, and only re-reads them if the modification time has subsequently changed. This improves performance by avoiding unneeded directory scans. (Fixed by @@ -1432,7 +1472,7 @@ * The :mod:`signal` module no longer re-installs the signal handler unless this is truly necessary, which fixes a bug that could make it impossible to catch the EINTR signal robustly. (Fixed by - Charles-Fran?ois Natali; :issue:`8354`.) + Charles-Francois Natali; :issue:`8354`.) * New functions: in the :mod:`site` module, three new functions return various site- and user-specific paths. @@ -1466,10 +1506,10 @@ defaults to False; if overridden to be True, new request connections will have the TCP_NODELAY option set to prevent buffering many small sends into a single TCP packet. - The :attr:`~SocketServer.TCPServer.timeout` class attribute can hold + The :attr:`~SocketServer.BaseServer.timeout` class attribute can hold a timeout in seconds that will be applied to the request socket; if - no request is received within that time, :meth:`handle_timeout` - will be called and :meth:`handle_request` will return. + no request is received within that time, :meth:`~SocketServer.BaseServer.handle_timeout` + will be called and :meth:`~SocketServer.BaseServer.handle_request` will return. (Contributed by Kristj?n Valur J?nsson; :issue:`6192` and :issue:`6267`.) * Updated module: the :mod:`sqlite3` module has been updated to @@ -1479,7 +1519,7 @@ and then call :meth:`~sqlite3.Connection.load_extension` to load a particular shared library. (Updated by Gerhard H?ring.) -* The :mod:`ssl` module's :class:`ssl.SSLSocket` objects now support the +* The :mod:`ssl` module's :class:`~ssl.SSLSocket` objects now support the buffer API, which fixed a test suite failure (fix by Antoine Pitrou; :issue:`7133`) and automatically set OpenSSL's :c:macro:`SSL_MODE_AUTO_RETRY`, which will prevent an error @@ -1535,7 +1575,7 @@ on receiving an :const:`EINTR` signal. (Reported by several people; final patch by Gregory P. Smith in :issue:`1068268`.) -* New function: :func:`~symtable.is_declared_global` in the :mod:`symtable` module +* New function: :func:`~symtable.Symbol.is_declared_global` in the :mod:`symtable` module returns true for variables that are explicitly declared to be global, false for ones that are implicitly global. (Contributed by Jeremy Hylton.) @@ -1716,7 +1756,7 @@ Makefile and the :file:`pyconfig.h` file. * :func:`~sysconfig.get_config_vars` returns a dictionary containing all of the configuration variables. -* :func:`~sysconfig.getpath` returns the configured path for +* :func:`~sysconfig.get_path` returns the configured path for a particular type of module: the standard library, site-specific modules, platform-specific modules, etc. * :func:`~sysconfig.is_python_build` returns true if you're running a @@ -1778,7 +1818,7 @@ Consult the :mod:`unittest` module documentation for more details. (Developed in :issue:`6001`.) -The :func:`main` function supports some other new options: +The :func:`~unittest.main` function supports some other new options: * :option:`-b` or :option:`--buffer` will buffer the standard output and standard error streams during each test. If the test passes, @@ -1796,7 +1836,7 @@ being tested or the tests being run have defined a signal handler of their own, by noticing that a signal handler was already set and calling it. If this doesn't work for you, there's a - :func:`removeHandler` decorator that can be used to mark tests that + :func:`~unittest.removeHandler` decorator that can be used to mark tests that should have the control-C handling disabled. * :option:`-f` or :option:`--failfast` makes @@ -1923,7 +1963,7 @@ :func:`unittest.main` now takes an optional ``exit`` argument. If False, :func:`~unittest.main` doesn't call :func:`sys.exit`, allowing -:func:`main` to be used from the interactive interpreter. +:func:`~unittest.main` to be used from the interactive interpreter. (Contributed by J. Pablo Fern?ndez; :issue:`3379`.) :class:`~unittest.TestResult` has new :meth:`~unittest.TestResult.startTestRun` and @@ -2120,7 +2160,7 @@ :c:macro:`Py_ISSPACE`, :c:macro:`Py_ISUPPER`, :c:macro:`Py_ISXDIGIT`, - and :c:macro:`Py_TOLOWER`, :c:macro:`Py_TOUPPER`. + :c:macro:`Py_TOLOWER`, and :c:macro:`Py_TOUPPER`. All of these functions are analogous to the C standard macros for classifying characters, but ignore the current locale setting, because in @@ -2266,11 +2306,11 @@ (Contributed by David Cournapeau; :issue:`4365`.) * The :mod:`_winreg` module for accessing the registry now implements - the :func:`CreateKeyEx` and :func:`DeleteKeyEx` functions, extended - versions of previously-supported functions that take several extra - arguments. The :func:`DisableReflectionKey`, - :func:`EnableReflectionKey`, and :func:`QueryReflectionKey` were also - tested and documented. + the :func:`~_winreg.CreateKeyEx` and :func:`~_winreg.DeleteKeyEx` + functions, extended versions of previously-supported functions that + take several extra arguments. The :func:`~_winreg.DisableReflectionKey`, + :func:`~_winreg.EnableReflectionKey`, and :func:`~_winreg.QueryReflectionKey` + were also tested and documented. (Implemented by Brian Curtin: :issue:`7347`.) * The new :c:func:`_beginthreadex` API is used to start threads, and @@ -2329,7 +2369,7 @@ attributes of the resulting code objects are overwritten when the original filename is obsolete. This can happen if the file has been renamed, moved, or is accessed through different paths. (Patch by - ?iga Seilnacht and Jean-Paul Calderone; :issue:`1180193`.) + Ziga Seilnacht and Jean-Paul Calderone; :issue:`1180193`.) * The :file:`regrtest.py` script now takes a :option:`--randseed=` switch that takes an integer that will be used as the random seed @@ -2387,20 +2427,20 @@ In the standard library: -* Operations with :class:`datetime` instances that resulted in a year +* Operations with :class:`~datetime.datetime` instances that resulted in a year falling outside the supported range didn't always raise :exc:`OverflowError`. Such errors are now checked more carefully and will now raise the exception. (Reported by Mark Leander, patch by Anand B. Pillai and Alexander Belopolsky; :issue:`7150`.) -* When using :class:`Decimal` instances with a string's +* When using :class:`~decimal.Decimal` instances with a string's :meth:`format` method, the default alignment was previously left-alignment. This has been changed to right-alignment, which might change the output of your programs. (Changed by Mark Dickinson; :issue:`6857`.) Comparisons involving a signaling NaN value (or ``sNAN``) now signal - :const:`InvalidOperation` instead of silently returning a true or + :const:`~decimal.InvalidOperation` instead of silently returning a true or false value depending on the comparison operator. Quiet NaN values (or ``NaN``) are now hashable. (Fixed by Mark Dickinson; :issue:`7279`.) @@ -2411,7 +2451,7 @@ or comment (which looks like ``). (Patch by Neil Muller; :issue:`2746`.) -* The :meth:`readline` method of :class:`StringIO` objects now does +* The :meth:`~StringIO.StringIO.readline` method of :class:`~StringIO.StringIO` objects now does nothing when a negative length is requested, as other file-like objects do. (:issue:`7348`). @@ -2470,6 +2510,54 @@ .. ====================================================================== +.. _py27-maintenance-enhancements: + +New Features Added to Python 2.7 Maintenance Releases +===================================================== + +New features may be added to Python 2.7 maintenance releases when the +situation genuinely calls for it. Any such additions must go through +the Python Enhancement Proposal process, and make a compelling case for why +they can't be adequately addressed by either adding the new feature solely to +Python 3, or else by publishing it on the Python Package Index. + +In addition to the specific proposals listed below, there is a general +exemption allowing new ``-3`` warnings to be added in any Python 2.7 +maintenance release. + + +PEP 434: IDLE Enhancement Exception for All Branches +---------------------------------------------------- + +:pep:`434` describes a general exemption for changes made to the IDLE +development environment shipped along with Python. This exemption makes it +possible for the IDLE developers to provide a more consistent user +experience across all supported versions of Python 2 and 3. + +For details of any IDLE changes, refer to the NEWS file for the specific +release. + + +PEP 466: Network Security Enhancements for Python 2.7 +----------------------------------------------------- + +:pep:`466` describes a number of network security enhancement proposals +that have been approved for inclusion in Python 2.7 maintenance releases, +with the first of those changes appearing in the Python 2.7.7 release. + +:pep:`466` related features added in Python 2.7.7: + +* :func:`hmac.compare_digest` was added to make a timing attack resistant + comparison operation broadly available to Python 2 applications + (backported by Alex Gaynor in :issue:`21306`) + +* the version of OpenSSL linked with the prebuilt Windows installers + published on python.org was updated to 1.0.1g (contributed by + Zachary Ware in :issue:`21462`) + + +.. ====================================================================== + .. _acks27: Acknowledgements -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 16:00:34 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 7 Jun 2014 16:00:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_issue_=2321569_from_3=2E4?= Message-ID: <3gm2S263rgz7Ln0@mail.python.org> http://hg.python.org/cpython/rev/b167df2912d6 changeset: 91060:b167df2912d6 parent: 91057:7c120e77d6f7 parent: 91059:d23cea976f46 user: Nick Coghlan date: Sun Jun 08 00:00:13 2014 +1000 summary: Merge issue #21569 from 3.4 files: Doc/whatsnew/2.7.rst | 318 +++++++++++++++++++----------- 1 files changed, 203 insertions(+), 115 deletions(-) diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -7,7 +7,6 @@ .. hyperlink all the methods & functions. .. T_STRING_INPLACE not described in main docs -.. "Format String Syntax" in string.rst could use many more examples. .. $Id$ Rules for maintenance: @@ -50,17 +49,16 @@ This saves the maintainer some effort going through the SVN logs when researching a change. -This article explains the new features in Python 2.7. The final -release of 2.7 is currently scheduled for July 2010; the detailed -schedule is described in :pep:`373`. +This article explains the new features in Python 2.7. Python 2.7 was released +on July 3, 2010. Numeric handling has been improved in many ways, for both -floating-point numbers and for the :class:`Decimal` class. There are -some useful additions to the standard library, such as a greatly -enhanced :mod:`unittest` module, the :mod:`argparse` module for -parsing command-line options, convenient ordered-dictionary and -:class:`Counter` classes in the :mod:`collections` module, and many -other improvements. +floating-point numbers and for the :class:`~decimal.Decimal` class. +There are some useful additions to the standard library, such as a +greatly enhanced :mod:`unittest` module, the :mod:`argparse` module +for parsing command-line options, convenient :class:`~collections.OrderedDict` +and :class:`~collections.Counter` classes in the :mod:`collections` module, +and many other improvements. Python 2.7 is planned to be the last of the 2.x releases, so we worked on making it a good release for the long term. To help with porting @@ -81,45 +79,91 @@ The Future for Python 2.x ========================= -Python 2.7 is intended to be the last major release in the 2.x series. -The Python maintainers are planning to focus their future efforts on -the Python 3.x series. - -This means that 2.7 will remain in place for a long time, running -production systems that have not been ported to Python 3.x. -Two consequences of the long-term significance of 2.7 are: - -* It's very likely the 2.7 release will have a longer period of - maintenance compared to earlier 2.x versions. Python 2.7 will - continue to be maintained while the transition to 3.x continues, and - the developers are planning to support Python 2.7 with bug-fix - releases beyond the typical two years. - -* A policy decision was made to silence warnings only of interest to - developers. :exc:`DeprecationWarning` and its - descendants are now ignored unless otherwise requested, preventing - users from seeing warnings triggered by an application. This change - was also made in the branch that will become Python 3.2. (Discussed - on stdlib-sig and carried out in :issue:`7319`.) - - In previous releases, :exc:`DeprecationWarning` messages were - enabled by default, providing Python developers with a clear - indication of where their code may break in a future major version - of Python. - - However, there are increasingly many users of Python-based - applications who are not directly involved in the development of - those applications. :exc:`DeprecationWarning` messages are - irrelevant to such users, making them worry about an application - that's actually working correctly and burdening application developers - with responding to these concerns. - - You can re-enable display of :exc:`DeprecationWarning` messages by - running Python with the :option:`-Wdefault` (short form: - :option:`-Wd`) switch, or by setting the :envvar:`PYTHONWARNINGS` - environment variable to ``"default"`` (or ``"d"``) before running - Python. Python code can also re-enable them - by calling ``warnings.simplefilter('default')``. +Python 2.7 is the last major release in the 2.x series, as the Python +maintainers have shifted the focus of their new feature development efforts +to the Python 3.x series. This means that while Python 2 continues to +receive bug fixes, and to be updated to build correctly on new hardware and +versions of supported operated systems, there will be no new full feature +releases for the language or standard library. + +However, while there is a large common subset between Python 2.7 and Python +3, and many of the changes involved in migrating to that common subset, or +directly to Python 3, can be safely automated, some other changes (notably +those associated with Unicode handling) may require careful consideration, +and preferably robust automated regression test suites, to migrate +effectively. + +This means that Python 2.7 will remain in place for a long time, providing a +stable and supported base platform for production systems that have not yet +been ported to Python 3. The full expected lifecycle of the Python 2.7 +series is detailed in :pep:`373`. + +Some key consequences of the long-term significance of 2.7 are: + +* As noted above, the 2.7 release has a much longer period of maintenance + when compared to earlier 2.x versions. Python 2.7 is currently expected to + remain supported by the core development team (receiving security updates + and other bug fixes) until at least 2020 (10 years after its initial + release, compared to the more typical support period of 18-24 months). + +* As the Python 2.7 standard library ages, making effective use of the + Python Package Index (either directly or via a redistributor) becomes + more important for Python 2 users. In addition to a wide variety of third + party packages for various tasks, the available packages include backports + of new modules and features from the Python 3 standard library that are + compatible with Python 2, as well as various tools and libraries that can + make it easier to migrate to Python 3. The `Python Packaging User Guide + `__ provides guidance on downloading and + installing software from the Python Package Index. + +* While the preferred approach to enhancing Python 2 is now the publication + of new packages on the Python Package Index, this approach doesn't + necessarily work in all cases, especially those related to network + security. In exceptional cases that cannot be handled adequately by + publishing new or updated packages on PyPI, the Python Enhancement + Proposal process may be used to make the case for adding new features + directly to the Python 2 standard library. Any such additions, and the + maintenance releases where they were added, will be noted in the + :ref:`py27-maintenance-enhancements` section below. + +For projects wishing to migrate from Python 2 to Python 3, or for library +and framework developers wishing to support users on both Python 2 and +Python 3, there are a variety of tools and guides available to help decide +on a suitable approach and manage some of the technical details involved. +The recommended starting point is the :ref:`pyporting-howto` HOWTO guide. + + +Changes to the Handling of Deprecation Warnings +=============================================== + +For Python 2.7, a policy decision was made to silence warnings only of +interest to developers by default. :exc:`DeprecationWarning` and its +descendants are now ignored unless otherwise requested, preventing +users from seeing warnings triggered by an application. This change +was also made in the branch that became Python 3.2. (Discussed +on stdlib-sig and carried out in :issue:`7319`.) + +In previous releases, :exc:`DeprecationWarning` messages were +enabled by default, providing Python developers with a clear +indication of where their code may break in a future major version +of Python. + +However, there are increasingly many users of Python-based +applications who are not directly involved in the development of +those applications. :exc:`DeprecationWarning` messages are +irrelevant to such users, making them worry about an application +that's actually working correctly and burdening application developers +with responding to these concerns. + +You can re-enable display of :exc:`DeprecationWarning` messages by +running Python with the :option:`-Wdefault <-W>` (short form: +:option:`-Wd <-W>`) switch, or by setting the :envvar:`PYTHONWARNINGS` +environment variable to ``"default"`` (or ``"d"``) before running +Python. Python code can also re-enable them +by calling ``warnings.simplefilter('default')``. + +The ``unittest`` module also automatically reenables deprecation warnings +when running tests. Python 3.1 Features @@ -133,7 +177,7 @@ A partial list of 3.1 features that were backported to 2.7: * The syntax for set literals (``{1,2,3}`` is a mutable set). -* Dictionary and set comprehensions (``{ i: i*2 for i in range(3)}``). +* Dictionary and set comprehensions (``{i: i*2 for i in range(3)}``). * Multiple context managers in a single :keyword:`with` statement. * A new version of the :mod:`io` library, rewritten in C for performance. * The ordered-dictionary type described in :ref:`pep-0372`. @@ -155,7 +199,7 @@ * :func:`operator.isCallable` and :func:`operator.sequenceIncludes`, which are not supported in 3.x, now trigger warnings. * The :option:`-3` switch now automatically - enables the :option:`-Qwarn` switch that causes warnings + enables the :option:`-Qwarn <-Q>` switch that causes warnings about using classic division with integers and long integers. @@ -390,9 +434,10 @@ .. seealso:: - `argparse module documentation `__ - - `Upgrading optparse code to use argparse `__ + :mod:`argparse` documentation + The documentation page of the argparse module. + + :ref:`argparse-from-optparse` Part of the Python documentation, describing how to convert code that uses :mod:`optparse`. @@ -402,8 +447,6 @@ PEP 391: Dictionary-Based Configuration For Logging ==================================================== -.. XXX not documented in library reference yet; add link here once it's added. - The :mod:`logging` module is very flexible; applications can define a tree of logging subsystems, and each logger in this tree can filter out certain messages, format them differently, and direct messages to @@ -412,21 +455,21 @@ All this flexibility can require a lot of configuration. You can write Python statements to create objects and set their properties, but a complex set-up requires verbose but boring code. -:mod:`logging` also supports a :func:`~logging.config.fileConfig` +:mod:`logging` also supports a :func:`~logging.fileConfig` function that parses a file, but the file format doesn't support configuring filters, and it's messier to generate programmatically. -Python 2.7 adds a :func:`~logging.config.dictConfig` function that +Python 2.7 adds a :func:`~logging.dictConfig` function that uses a dictionary to configure logging. There are many ways to produce a dictionary from different sources: construct one with code; parse a file containing JSON; or use a YAML parsing library if one is -installed. +installed. For more information see :ref:`logging-config-api`. The following example configures two loggers, the root logger and a -logger named "network". Messages sent to the root logger will be +logger named "network". Messages sent to the root logger will be sent to the system log using the syslog protocol, and messages to the "network" logger will be written to a :file:`network.log` file -that will be rotated once the log reaches 1Mb. +that will be rotated once the log reaches 1MB. :: @@ -445,7 +488,7 @@ 'filename': '/logs/network.log', 'formatter': 'standard', 'level': 'INFO', - 'maxBytes': 1024*1024}, + 'maxBytes': 1000000}, 'syslog': {'class': 'logging.handlers.SysLogHandler', 'formatter': 'standard', 'level': 'ERROR'}}, @@ -483,16 +526,19 @@ for UDP or :const:`socket.SOCK_STREAM` for TCP. The default protocol remains UDP. -* :class:`Logger` instances gained a :meth:`getChild` method that retrieves a - descendant logger using a relative path. For example, - once you retrieve a logger by doing ``log = getLogger('app')``, +* :class:`~logging.Logger` instances gained a :meth:`~logging.Logger.getChild` + method that retrieves a descendant logger using a relative path. + For example, once you retrieve a logger by doing ``log = getLogger('app')``, calling ``log.getChild('network.listen')`` is equivalent to ``getLogger('app.network.listen')``. -* The :class:`LoggerAdapter` class gained a :meth:`isEnabledFor` method - that takes a *level* and returns whether the underlying logger would +* The :class:`~logging.LoggerAdapter` class gained a + :meth:`~logging.LoggerAdapter.isEnabledFor` method that takes a + *level* and returns whether the underlying logger would process a message of that level of importance. +.. XXX: Logger objects don't have a class declaration so the link don't work + .. seealso:: :pep:`391` - Dictionary-Based Configuration For Logging @@ -501,14 +547,15 @@ PEP 3106: Dictionary Views ==================================================== -The dictionary methods :meth:`keys`, :meth:`values`, and :meth:`items` -are different in Python 3.x. They return an object called a :dfn:`view` -instead of a fully materialized list. - -It's not possible to change the return values of :meth:`keys`, -:meth:`values`, and :meth:`items` in Python 2.7 because too much code -would break. Instead the 3.x versions were added under the new names -:meth:`viewkeys`, :meth:`viewvalues`, and :meth:`viewitems`. +The dictionary methods :meth:`~dict.keys`, :meth:`~dict.values`, and +:meth:`~dict.items` are different in Python 3.x. They return an object +called a :dfn:`view` instead of a fully materialized list. + +It's not possible to change the return values of :meth:`~dict.keys`, +:meth:`~dict.values`, and :meth:`~dict.items` in Python 2.7 because +too much code would break. Instead the 3.x versions were added +under the new names :meth:`~dict.viewkeys`, :meth:`~dict.viewvalues`, +and :meth:`~dict.viewitems`. :: @@ -550,8 +597,8 @@ RuntimeError: dictionary changed size during iteration You can use the view methods in Python 2.x code, and the 2to3 -converter will change them to the standard :meth:`keys`, -:meth:`values`, and :meth:`items` methods. +converter will change them to the standard :meth:`~dict.keys`, +:meth:`~dict.values`, and :meth:`~dict.items` methods. .. seealso:: @@ -624,7 +671,7 @@ ``{}`` continues to represent an empty dictionary; use ``set()`` for an empty set. - >>> {1,2,3,4,5} + >>> {1, 2, 3, 4, 5} set([1, 2, 3, 4, 5]) >>> set() # empty set set([]) @@ -794,7 +841,7 @@ ``None`` as its first argument. (Fixed by Georg Brandl; :issue:`4759`.) - .. bytearray doesn't seem to be documented + .. XXX bytearray doesn't seem to be documented * When using ``@classmethod`` and ``@staticmethod`` to wrap methods as class or static methods, the wrapper object now @@ -867,12 +914,6 @@ Several performance enhancements have been added: -.. * A new :program:`configure` option, :option:`--with-computed-gotos`, - compiles the main bytecode interpreter loop using a new dispatch - mechanism that gives speedups of up to 20%, depending on the system - and benchmark. The new mechanism is only supported on certain - compilers, such as gcc, SunPro, and icc. - * A new opcode was added to perform the initial setup for :keyword:`with` statements, looking up the :meth:`__enter__` and :meth:`__exit__` methods. (Contributed by Benjamin Peterson.) @@ -1054,7 +1095,7 @@ :meth:`~collections.deque.count` method that returns the number of contained elements equal to the supplied argument *x*, and a :meth:`~collections.deque.reverse` method that reverses the elements - of the deque in-place. :class:`deque` also exposes its maximum + of the deque in-place. :class:`~collections.deque` also exposes its maximum length as the read-only :attr:`~collections.deque.maxlen` attribute. (Both features added by Raymond Hettinger.) @@ -1135,15 +1176,14 @@ ``Decimal('0.1000000000000000055511151231257827021181583404541015625')``. (Implemented by Raymond Hettinger; :issue:`4796`.) - Comparing instances of :class:`Decimal` with floating-point + Comparing instances of :class:`~decimal.Decimal` with floating-point numbers now produces sensible results based on the numeric values of the operands. Previously such comparisons would fall back to Python's default rules for comparing objects, which produced arbitrary results based on their type. Note that you still cannot combine :class:`Decimal` and floating-point in other operations such as addition, since you should be explicitly choosing how to convert between float and - :class:`Decimal`. - (Fixed by Mark Dickinson; :issue:`2531`.) + :class:`~decimal.Decimal`. (Fixed by Mark Dickinson; :issue:`2531`.) The constructor for :class:`~decimal.Decimal` now accepts floating-point numbers (added by Raymond Hettinger; :issue:`8257`) @@ -1195,8 +1235,8 @@ Ordering comparisons (``<``, ``<=``, ``>``, ``>=``) between fractions and complex numbers now raise a :exc:`TypeError`. - This fixes an oversight, making the :class:`Fraction` match the other - numeric types. + This fixes an oversight, making the :class:`~fractions.Fraction` + match the other numeric types. .. revision 79455 @@ -1210,7 +1250,7 @@ uploads thanks to an added *rest* parameter (patch by Pablo Mouzo; :issue:`6845`.) -* New class decorator: :func:`total_ordering` in the :mod:`functools` +* New class decorator: :func:`~functools.total_ordering` in the :mod:`functools` module takes a class that defines an :meth:`__eq__` method and one of :meth:`__lt__`, :meth:`__le__`, :meth:`__gt__`, or :meth:`__ge__`, and generates the missing comparison methods. Since the @@ -1218,7 +1258,7 @@ this decorator makes it easier to define ordered classes. (Added by Raymond Hettinger; :issue:`5479`.) - New function: :func:`cmp_to_key` will take an old-style comparison + New function: :func:`~functools.cmp_to_key` will take an old-style comparison function that expects two arguments and return a new callable that can be used as the *key* parameter to functions such as :func:`sorted`, :func:`min` and :func:`max`, etc. The primary @@ -1345,7 +1385,7 @@ with any object literal that decodes to a list of pairs. (Contributed by Raymond Hettinger; :issue:`5381`.) -* The :mod:`mailbox` module's :class:`Maildir` class now records the +* The :mod:`mailbox` module's :class:`~mailbox.Maildir` class now records the timestamp on the directories it reads, and only re-reads them if the modification time has subsequently changed. This improves performance by avoiding unneeded directory scans. (Fixed by @@ -1432,7 +1472,7 @@ * The :mod:`signal` module no longer re-installs the signal handler unless this is truly necessary, which fixes a bug that could make it impossible to catch the EINTR signal robustly. (Fixed by - Charles-Fran?ois Natali; :issue:`8354`.) + Charles-Francois Natali; :issue:`8354`.) * New functions: in the :mod:`site` module, three new functions return various site- and user-specific paths. @@ -1466,10 +1506,10 @@ defaults to False; if overridden to be True, new request connections will have the TCP_NODELAY option set to prevent buffering many small sends into a single TCP packet. - The :attr:`~SocketServer.TCPServer.timeout` class attribute can hold + The :attr:`~SocketServer.BaseServer.timeout` class attribute can hold a timeout in seconds that will be applied to the request socket; if - no request is received within that time, :meth:`handle_timeout` - will be called and :meth:`handle_request` will return. + no request is received within that time, :meth:`~SocketServer.BaseServer.handle_timeout` + will be called and :meth:`~SocketServer.BaseServer.handle_request` will return. (Contributed by Kristj?n Valur J?nsson; :issue:`6192` and :issue:`6267`.) * Updated module: the :mod:`sqlite3` module has been updated to @@ -1479,7 +1519,7 @@ and then call :meth:`~sqlite3.Connection.load_extension` to load a particular shared library. (Updated by Gerhard H?ring.) -* The :mod:`ssl` module's :class:`ssl.SSLSocket` objects now support the +* The :mod:`ssl` module's :class:`~ssl.SSLSocket` objects now support the buffer API, which fixed a test suite failure (fix by Antoine Pitrou; :issue:`7133`) and automatically set OpenSSL's :c:macro:`SSL_MODE_AUTO_RETRY`, which will prevent an error @@ -1535,7 +1575,7 @@ on receiving an :const:`EINTR` signal. (Reported by several people; final patch by Gregory P. Smith in :issue:`1068268`.) -* New function: :func:`~symtable.is_declared_global` in the :mod:`symtable` module +* New function: :func:`~symtable.Symbol.is_declared_global` in the :mod:`symtable` module returns true for variables that are explicitly declared to be global, false for ones that are implicitly global. (Contributed by Jeremy Hylton.) @@ -1716,7 +1756,7 @@ Makefile and the :file:`pyconfig.h` file. * :func:`~sysconfig.get_config_vars` returns a dictionary containing all of the configuration variables. -* :func:`~sysconfig.getpath` returns the configured path for +* :func:`~sysconfig.get_path` returns the configured path for a particular type of module: the standard library, site-specific modules, platform-specific modules, etc. * :func:`~sysconfig.is_python_build` returns true if you're running a @@ -1778,7 +1818,7 @@ Consult the :mod:`unittest` module documentation for more details. (Developed in :issue:`6001`.) -The :func:`main` function supports some other new options: +The :func:`~unittest.main` function supports some other new options: * :option:`-b` or :option:`--buffer` will buffer the standard output and standard error streams during each test. If the test passes, @@ -1796,7 +1836,7 @@ being tested or the tests being run have defined a signal handler of their own, by noticing that a signal handler was already set and calling it. If this doesn't work for you, there's a - :func:`removeHandler` decorator that can be used to mark tests that + :func:`~unittest.removeHandler` decorator that can be used to mark tests that should have the control-C handling disabled. * :option:`-f` or :option:`--failfast` makes @@ -1923,7 +1963,7 @@ :func:`unittest.main` now takes an optional ``exit`` argument. If False, :func:`~unittest.main` doesn't call :func:`sys.exit`, allowing -:func:`main` to be used from the interactive interpreter. +:func:`~unittest.main` to be used from the interactive interpreter. (Contributed by J. Pablo Fern?ndez; :issue:`3379`.) :class:`~unittest.TestResult` has new :meth:`~unittest.TestResult.startTestRun` and @@ -2120,7 +2160,7 @@ :c:macro:`Py_ISSPACE`, :c:macro:`Py_ISUPPER`, :c:macro:`Py_ISXDIGIT`, - and :c:macro:`Py_TOLOWER`, :c:macro:`Py_TOUPPER`. + :c:macro:`Py_TOLOWER`, and :c:macro:`Py_TOUPPER`. All of these functions are analogous to the C standard macros for classifying characters, but ignore the current locale setting, because in @@ -2266,11 +2306,11 @@ (Contributed by David Cournapeau; :issue:`4365`.) * The :mod:`_winreg` module for accessing the registry now implements - the :func:`CreateKeyEx` and :func:`DeleteKeyEx` functions, extended - versions of previously-supported functions that take several extra - arguments. The :func:`DisableReflectionKey`, - :func:`EnableReflectionKey`, and :func:`QueryReflectionKey` were also - tested and documented. + the :func:`~_winreg.CreateKeyEx` and :func:`~_winreg.DeleteKeyEx` + functions, extended versions of previously-supported functions that + take several extra arguments. The :func:`~_winreg.DisableReflectionKey`, + :func:`~_winreg.EnableReflectionKey`, and :func:`~_winreg.QueryReflectionKey` + were also tested and documented. (Implemented by Brian Curtin: :issue:`7347`.) * The new :c:func:`_beginthreadex` API is used to start threads, and @@ -2329,7 +2369,7 @@ attributes of the resulting code objects are overwritten when the original filename is obsolete. This can happen if the file has been renamed, moved, or is accessed through different paths. (Patch by - ?iga Seilnacht and Jean-Paul Calderone; :issue:`1180193`.) + Ziga Seilnacht and Jean-Paul Calderone; :issue:`1180193`.) * The :file:`regrtest.py` script now takes a :option:`--randseed=` switch that takes an integer that will be used as the random seed @@ -2387,20 +2427,20 @@ In the standard library: -* Operations with :class:`datetime` instances that resulted in a year +* Operations with :class:`~datetime.datetime` instances that resulted in a year falling outside the supported range didn't always raise :exc:`OverflowError`. Such errors are now checked more carefully and will now raise the exception. (Reported by Mark Leander, patch by Anand B. Pillai and Alexander Belopolsky; :issue:`7150`.) -* When using :class:`Decimal` instances with a string's +* When using :class:`~decimal.Decimal` instances with a string's :meth:`format` method, the default alignment was previously left-alignment. This has been changed to right-alignment, which might change the output of your programs. (Changed by Mark Dickinson; :issue:`6857`.) Comparisons involving a signaling NaN value (or ``sNAN``) now signal - :const:`InvalidOperation` instead of silently returning a true or + :const:`~decimal.InvalidOperation` instead of silently returning a true or false value depending on the comparison operator. Quiet NaN values (or ``NaN``) are now hashable. (Fixed by Mark Dickinson; :issue:`7279`.) @@ -2411,7 +2451,7 @@ or comment (which looks like ``). (Patch by Neil Muller; :issue:`2746`.) -* The :meth:`readline` method of :class:`StringIO` objects now does +* The :meth:`~StringIO.StringIO.readline` method of :class:`~StringIO.StringIO` objects now does nothing when a negative length is requested, as other file-like objects do. (:issue:`7348`). @@ -2470,6 +2510,54 @@ .. ====================================================================== +.. _py27-maintenance-enhancements: + +New Features Added to Python 2.7 Maintenance Releases +===================================================== + +New features may be added to Python 2.7 maintenance releases when the +situation genuinely calls for it. Any such additions must go through +the Python Enhancement Proposal process, and make a compelling case for why +they can't be adequately addressed by either adding the new feature solely to +Python 3, or else by publishing it on the Python Package Index. + +In addition to the specific proposals listed below, there is a general +exemption allowing new ``-3`` warnings to be added in any Python 2.7 +maintenance release. + + +PEP 434: IDLE Enhancement Exception for All Branches +---------------------------------------------------- + +:pep:`434` describes a general exemption for changes made to the IDLE +development environment shipped along with Python. This exemption makes it +possible for the IDLE developers to provide a more consistent user +experience across all supported versions of Python 2 and 3. + +For details of any IDLE changes, refer to the NEWS file for the specific +release. + + +PEP 466: Network Security Enhancements for Python 2.7 +----------------------------------------------------- + +:pep:`466` describes a number of network security enhancement proposals +that have been approved for inclusion in Python 2.7 maintenance releases, +with the first of those changes appearing in the Python 2.7.7 release. + +:pep:`466` related features added in Python 2.7.7: + +* :func:`hmac.compare_digest` was added to make a timing attack resistant + comparison operation broadly available to Python 2 applications + (backported by Alex Gaynor in :issue:`21306`) + +* the version of OpenSSL linked with the prebuilt Windows installers + published on python.org was updated to 1.0.1g (contributed by + Zachary Ware in :issue:`21462`) + + +.. ====================================================================== + .. _acks27: Acknowledgements -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 17:22:50 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 7 Jun 2014 17:22:50 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_PEP_466=3A_note_hashlib=2Epbk?= =?utf-8?q?df2=5Fhmac_backport?= Message-ID: <3gm4Gy3mDZz7LkG@mail.python.org> http://hg.python.org/peps/rev/616540dbecb2 changeset: 5489:616540dbecb2 user: Nick Coghlan date: Sun Jun 08 01:22:39 2014 +1000 summary: PEP 466: note hashlib.pbkdf2_hmac backport files: pep-0466.txt | 18 ++++++++++++------ 1 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pep-0466.txt b/pep-0466.txt --- a/pep-0466.txt +++ b/pep-0466.txt @@ -96,18 +96,24 @@ * `Issue #21306`_: backport ``hmac.compare_digest`` * `Issue #21462`_: upgrade OpenSSL in the Python 2.7 Windows installers -.. _Issue #21306: https://bugs.python.org/issue21306 -.. _Issue #21462: https://bugs.python.org/issue21462 +.. _Issue #21306: http://bugs.python.org/issue21306 +.. _Issue #21462: http://bugs.python.org/issue21462 + +Implemented for Python 2.7.8 (in development): + +* `Issue #21304`_: backport ``hashlib.pbkdf2`` + +.. _Issue #21304: http://bugs.python.org/issue21304 Still in progress: * `Issue #21305`_: backport ``os.urandom`` shared file descriptor change -* `Issue #21307`_: backport specified ``hashlib`` module features +* `Issue #21307`_: backport remaining specified ``hashlib`` module features * `Issue #21308`_: backport specified ``ssl`` module features -.. _Issue #21305: https://bugs.python.org/issue21305 -.. _Issue #21307: https://bugs.python.org/issue21307 -.. _Issue #21308: https://bugs.python.org/issue21308 +.. _Issue #21305: http://bugs.python.org/issue21305 +.. _Issue #21307: http://bugs.python.org/issue21307 +.. _Issue #21308: http://bugs.python.org/issue21308 Backwards compatibility considerations ====================================== -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Sat Jun 7 21:40:02 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Jun 2014 21:40:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_allow_the_keyw?= =?utf-8?q?ord_else_immediately_after_=28no_space=29_an_integer_=28closes_?= =?utf-8?q?=2321642=29?= Message-ID: <3gm9zk31nrz7Lpk@mail.python.org> http://hg.python.org/cpython/rev/4ad33d82193d changeset: 91061:4ad33d82193d branch: 3.4 parent: 91059:d23cea976f46 user: Benjamin Peterson date: Sat Jun 07 12:36:39 2014 -0700 summary: allow the keyword else immediately after (no space) an integer (closes #21642) files: Lib/test/test_grammar.py | 6 ++++++ Misc/NEWS | 4 ++++ Parser/tokenizer.c | 19 ++++++++++++++----- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -80,6 +80,12 @@ x = .3e14 x = 3.1e4 + def test_float_exponent_tokenization(self): + # See issue 21642. + self.assertEqual(1 if 1else 0, 1) + self.assertEqual(1 if 0else 0, 0) + self.assertRaises(SyntaxError, eval, "0 if 1Else 0") + def test_string_literals(self): x = ''; y = ""; self.assertTrue(len(x) == 0 and x == y) x = '\''; y = "'"; self.assertTrue(len(x) == 1 and x == y and ord(x) == 39) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #21642: If the conditional if-else expression, allow an integer written + with no space between itself and the ``else`` keyword (e.g. ``True if 42else + False``) to be valid syntax. + - Issue #21523: Fix over-pessimistic computation of the stack effect of some opcodes in the compiler. This also fixes a quadratic compilation time issue noticeable when compiling code with a large number of "and" diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1597,15 +1597,24 @@ } while (isdigit(c)); } if (c == 'e' || c == 'E') { - exponent: + int e; + exponent: + e = c; /* Exponent part */ c = tok_nextc(tok); - if (c == '+' || c == '-') + if (c == '+' || c == '-') { c = tok_nextc(tok); - if (!isdigit(c)) { - tok->done = E_TOKEN; + if (!isdigit(c)) { + tok->done = E_TOKEN; + tok_backup(tok, c); + return ERRORTOKEN; + } + } else if (!isdigit(c)) { tok_backup(tok, c); - return ERRORTOKEN; + tok_backup(tok, e); + *p_start = tok->start; + *p_end = tok->cur; + return NUMBER; } do { c = tok_nextc(tok); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 21:40:03 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Jun 2014 21:40:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_allow_the_keyw?= =?utf-8?q?ord_else_immediately_after_=28no_space=29_an_integer_=28closes_?= =?utf-8?q?=2321642=29?= Message-ID: <3gm9zl4fGnz7LpY@mail.python.org> http://hg.python.org/cpython/rev/29d34f4f8900 changeset: 91062:29d34f4f8900 branch: 2.7 parent: 91058:7c28b3a92f40 user: Benjamin Peterson date: Sat Jun 07 12:36:39 2014 -0700 summary: allow the keyword else immediately after (no space) an integer (closes #21642) files: Lib/test/test_grammar.py | 6 ++++++ Misc/NEWS | 4 ++++ Parser/tokenizer.c | 19 ++++++++++++++----- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -75,6 +75,12 @@ x = .3e14 x = 3.1e4 + def test_float_exponent_tokenization(self): + # See issue 21642. + self.assertEqual(1 if 1else 0, 1) + self.assertEqual(1 if 0else 0, 0) + self.assertRaises(SyntaxError, eval, "0 if 1Else 0") + def testStringLiterals(self): x = ''; y = ""; self.assertTrue(len(x) == 0 and x == y) x = '\''; y = "'"; self.assertTrue(len(x) == 1 and x == y and ord(x) == 39) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,10 @@ - Issue #19656: Running Python with the -3 option now also warns about non-ascii bytes literals. +- Issue #21642: If the conditional if-else expression, allow an integer written + with no space between itself and the ``else`` keyword (e.g. ``True if 42else + False``) to be valid syntax. + - Issue #21523: Fix over-pessimistic computation of the stack effect of some opcodes in the compiler. This also fixes a quadratic compilation time issue noticeable when compiling code with a large number of "and" diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1500,15 +1500,24 @@ } while (isdigit(c)); } if (c == 'e' || c == 'E') { - exponent: + int e; + exponent: + e = c; /* Exponent part */ c = tok_nextc(tok); - if (c == '+' || c == '-') + if (c == '+' || c == '-') { c = tok_nextc(tok); - if (!isdigit(c)) { - tok->done = E_TOKEN; + if (!isdigit(c)) { + tok->done = E_TOKEN; + tok_backup(tok, c); + return ERRORTOKEN; + } + } else if (!isdigit(c)) { tok_backup(tok, c); - return ERRORTOKEN; + tok_backup(tok, e); + *p_start = tok->start; + *p_end = tok->cur; + return NUMBER; } do { c = tok_nextc(tok); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 21:40:04 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Jun 2014 21:40:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjE2NDIp?= Message-ID: <3gm9zm6ZRPz7Lpy@mail.python.org> http://hg.python.org/cpython/rev/d5998cca01a8 changeset: 91063:d5998cca01a8 parent: 91060:b167df2912d6 parent: 91061:4ad33d82193d user: Benjamin Peterson date: Sat Jun 07 12:39:51 2014 -0700 summary: merge 3.4 (#21642) files: Lib/test/test_grammar.py | 6 ++++++ Misc/NEWS | 4 ++++ Parser/tokenizer.c | 19 ++++++++++++++----- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -80,6 +80,12 @@ x = .3e14 x = 3.1e4 + def test_float_exponent_tokenization(self): + # See issue 21642. + self.assertEqual(1 if 1else 0, 1) + self.assertEqual(1 if 0else 0, 0) + self.assertRaises(SyntaxError, eval, "0 if 1Else 0") + def test_string_literals(self): x = ''; y = ""; self.assertTrue(len(x) == 0 and x == y) x = '\''; y = "'"; self.assertTrue(len(x) == 1 and x == y and ord(x) == 39) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #21642: If the conditional if-else expression, allow an integer written + with no space between itself and the ``else`` keyword (e.g. ``True if 42else + False``) to be valid syntax. + - Issue #21523: Fix over-pessimistic computation of the stack effect of some opcodes in the compiler. This also fixes a quadratic compilation time issue noticeable when compiling code with a large number of "and" diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1603,15 +1603,24 @@ } while (isdigit(c)); } if (c == 'e' || c == 'E') { - exponent: + int e; + exponent: + e = c; /* Exponent part */ c = tok_nextc(tok); - if (c == '+' || c == '-') + if (c == '+' || c == '-') { c = tok_nextc(tok); - if (!isdigit(c)) { - tok->done = E_TOKEN; + if (!isdigit(c)) { + tok->done = E_TOKEN; + tok_backup(tok, c); + return ERRORTOKEN; + } + } else if (!isdigit(c)) { tok_backup(tok, c); - return ERRORTOKEN; + tok_backup(tok, e); + *p_start = tok->start; + *p_end = tok->cur; + return NUMBER; } do { c = tok_nextc(tok); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 21:48:18 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Jun 2014 21:48:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_document_the_c?= =?utf-8?q?ompress=5Flevel_argument_to_tarfile=2Eopen_=28closes_=2321404?= =?utf-8?q?=29?= Message-ID: <3gmB9G6ftTz7LnR@mail.python.org> http://hg.python.org/cpython/rev/390b7fd617a9 changeset: 91064:390b7fd617a9 branch: 2.7 parent: 91062:29d34f4f8900 user: Benjamin Peterson date: Sat Jun 07 12:45:37 2014 -0700 summary: document the compress_level argument to tarfile.open (closes #21404) Patch by Katherine Busch. files: Doc/library/tarfile.rst | 4 ++++ Misc/ACKS | 1 + 2 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -77,6 +77,10 @@ If *fileobj* is specified, it is used as an alternative to a file object opened for *name*. It is supposed to be at position 0. + For modes ``'w:gz'``, ``'r:gz'``, ``'w:bz2'``, ``'r:bz2'``, :func:`tarfile.open` + accepts the keyword argument *compresslevel* to specify the compression level of + the file. + For special purposes, there is a second format for *mode*: ``'filemode|[compression]'``. :func:`tarfile.open` will return a :class:`TarFile` object that processes its data as a stream of blocks. No random seeking will diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -194,6 +194,7 @@ Alastair Burt Tarn Weisner Burton Lee Busby +Katherine Busch Ralph Butler Nicolas Cadou Jp Calderone -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 21:48:20 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Jun 2014 21:48:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_document_the_c?= =?utf-8?q?ompress=5Flevel_argument_to_tarfile=2Eopen_=28closes_=2321404?= =?utf-8?q?=29?= Message-ID: <3gmB9J1Ml5z7Lnv@mail.python.org> http://hg.python.org/cpython/rev/0c712828fb6e changeset: 91065:0c712828fb6e branch: 3.4 parent: 91061:4ad33d82193d user: Benjamin Peterson date: Sat Jun 07 12:45:37 2014 -0700 summary: document the compress_level argument to tarfile.open (closes #21404) Patch by Katherine Busch. files: Doc/library/tarfile.rst | 4 ++++ Misc/ACKS | 1 + 2 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -81,6 +81,10 @@ If *fileobj* is specified, it is used as an alternative to a :term:`file object` opened in binary mode for *name*. It is supposed to be at position 0. + For modes ``'w:gz'``, ``'r:gz'``, ``'w:bz2'``, ``'r:bz2'``, :func:`tarfile.open` + accepts the keyword argument *compresslevel* to specify the compression level of + the file. + For special purposes, there is a second format for *mode*: ``'filemode|[compression]'``. :func:`tarfile.open` will return a :class:`TarFile` object that processes its data as a stream of blocks. No random seeking will diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -195,6 +195,7 @@ Alastair Burt Tarn Weisner Burton Lee Busby +Katherine Busch Ralph Butler Nicolas Cadou Jp Calderone -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 21:48:21 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Jun 2014 21:48:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjE0MDQp?= Message-ID: <3gmB9K36KGz7Lnv@mail.python.org> http://hg.python.org/cpython/rev/171e8f6c814c changeset: 91066:171e8f6c814c parent: 91063:d5998cca01a8 parent: 91065:0c712828fb6e user: Benjamin Peterson date: Sat Jun 07 12:48:09 2014 -0700 summary: merge 3.4 (#21404) files: Doc/library/tarfile.rst | 4 ++++ Misc/ACKS | 1 + 2 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -81,6 +81,10 @@ If *fileobj* is specified, it is used as an alternative to a :term:`file object` opened in binary mode for *name*. It is supposed to be at position 0. + For modes ``'w:gz'``, ``'r:gz'``, ``'w:bz2'``, ``'r:bz2'``, :func:`tarfile.open` + accepts the keyword argument *compresslevel* to specify the compression level of + the file. + For special purposes, there is a second format for *mode*: ``'filemode|[compression]'``. :func:`tarfile.open` will return a :class:`TarFile` object that processes its data as a stream of blocks. No random seeking will diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -196,6 +196,7 @@ Alastair Burt Tarn Weisner Burton Lee Busby +Katherine Busch Ralph Butler Nicolas Cadou Jp Calderone -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 22:52:00 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Jun 2014 22:52:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_specify_that_g?= =?utf-8?q?etuid=28=29_returns_the_real_uid_=28closes_=2310503=29?= Message-ID: <3gmCZm6tZ0z7LjW@mail.python.org> http://hg.python.org/cpython/rev/19172062e5c0 changeset: 91067:19172062e5c0 branch: 3.4 parent: 91065:0c712828fb6e user: Benjamin Peterson date: Sat Jun 07 13:50:34 2014 -0700 summary: specify that getuid() returns the real uid (closes #10503) Patch by ????????????. files: Doc/library/os.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -379,7 +379,7 @@ .. index:: single: user; id - Return the current process's user id. + Return the current process's real user id. Availability: Unix. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 22:52:02 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Jun 2014 22:52:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_specify_that_g?= =?utf-8?q?etuid=28=29_returns_the_real_uid_=28closes_=2310503=29?= Message-ID: <3gmCZp1c65z7Ljw@mail.python.org> http://hg.python.org/cpython/rev/6dfbe504f659 changeset: 91068:6dfbe504f659 branch: 2.7 parent: 91064:390b7fd617a9 user: Benjamin Peterson date: Sat Jun 07 13:50:34 2014 -0700 summary: specify that getuid() returns the real uid (closes #10503) Patch by ????????????. files: Doc/library/os.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -257,7 +257,7 @@ .. index:: single: user; id - Return the current process's user id. + Return the current process's real user id. Availability: Unix. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 7 22:52:03 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Jun 2014 22:52:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMTA1MDMp?= Message-ID: <3gmCZq3QjXz7LkD@mail.python.org> http://hg.python.org/cpython/rev/8866ac6f2269 changeset: 91069:8866ac6f2269 parent: 91066:171e8f6c814c parent: 91067:19172062e5c0 user: Benjamin Peterson date: Sat Jun 07 13:51:51 2014 -0700 summary: merge 3.4 (#10503) files: Doc/library/os.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -379,7 +379,7 @@ .. index:: single: user; id - Return the current process's user id. + Return the current process's real user id. Availability: Unix. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 00:09:42 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 00:09:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_in_ftp_cache_p?= =?utf-8?q?runing=2C_avoid_changing_the_size_of_a_dict_while_iterating_ove?= =?utf-8?q?r_it?= Message-ID: <3gmFJQ6jylz7LjN@mail.python.org> http://hg.python.org/cpython/rev/b8f9ae84d211 changeset: 91070:b8f9ae84d211 branch: 3.4 parent: 91067:19172062e5c0 user: Benjamin Peterson date: Sat Jun 07 15:08:04 2014 -0700 summary: in ftp cache pruning, avoid changing the size of a dict while iterating over it (closes #21463) Patch by Skyler Leigh Amador. files: Lib/test/test_urllib.py | 32 ++++++++++++++++++++++++++++- Lib/urllib/request.py | 2 +- Misc/ACKS | 1 + Misc/NEWS | 2 + 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -7,6 +7,7 @@ import email.message import io import unittest +from unittest.mock import patch from test import support import os import sys @@ -89,6 +90,26 @@ http.client.HTTPConnection = self._connection_class +class FakeFTPMixin(object): + def fakeftp(self): + class FakeFtpWrapper(object): + def __init__(self, user, passwd, host, port, dirs, timeout=None, + persistent=True): + pass + + def retrfile(self, file, type): + return io.BytesIO(), 0 + + def close(self): + pass + + self._ftpwrapper_class = urllib.request.ftpwrapper + urllib.request.ftpwrapper = FakeFtpWrapper + + def unfakeftp(self): + urllib.request.ftpwrapper = self._ftpwrapper_class + + class urlopen_FileTests(unittest.TestCase): """Test urlopen() opening a temporary file. @@ -195,7 +216,7 @@ self.env.set('NO_PROXY', 'localhost, anotherdomain.com, newdomain.com') self.assertTrue(urllib.request.proxy_bypass_environment('anotherdomain.com')) -class urlopen_HttpTests(unittest.TestCase, FakeHTTPMixin): +class urlopen_HttpTests(unittest.TestCase, FakeHTTPMixin, FakeFTPMixin): """Test urlopen() opening a fake http connection.""" def check_read(self, ver): @@ -309,6 +330,15 @@ self.assertFalse(e.exception.filename) self.assertTrue(e.exception.reason) + @patch.object(urllib.request, 'MAXFTPCACHE', 0) + def test_ftp_cache_pruning(self): + self.fakeftp() + try: + urllib.request.ftpcache['test'] = urllib.request.ftpwrapper('user', 'pass', 'localhost', 21, []) + urlopen('ftp://localhost') + finally: + self.unfakeftp() + def test_userpass_inurl(self): self.fakehttp(b"HTTP/1.0 200 OK\r\n\r\nHello!") diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1911,7 +1911,7 @@ # XXX thread unsafe! if len(self.ftpcache) > MAXFTPCACHE: # Prune the cache, rather arbitrarily - for k in self.ftpcache.keys(): + for k in list(self.ftpcache): if k != key: v = self.ftpcache[k] del self.ftpcache[k] diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -32,6 +32,7 @@ Ray Allen Billy G. Allie Kevin Altis +Skyler Leigh Amador Joe Amenta A. Amoroso Mark Anacker diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,8 @@ Library ------- +- Issue #21463: In urllib.request, fix pruning of the FTP cache. + - Issue #21618: The subprocess module could fail to close open fds that were inherited by the calling process and already higher than POSIX resource limits would otherwise allow. On systems with a functioning /proc/self/fd -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 00:09:44 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 00:09:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjE0NjMp?= Message-ID: <3gmFJS1XrPz7Ljl@mail.python.org> http://hg.python.org/cpython/rev/6f70a18313e5 changeset: 91071:6f70a18313e5 parent: 91069:8866ac6f2269 parent: 91070:b8f9ae84d211 user: Benjamin Peterson date: Sat Jun 07 15:09:36 2014 -0700 summary: merge 3.4 (#21463) files: Lib/test/test_urllib.py | 32 ++++++++++++++++++++++++++++- Lib/urllib/request.py | 2 +- Misc/ACKS | 1 + Misc/NEWS | 2 + 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -7,6 +7,7 @@ import email.message import io import unittest +from unittest.mock import patch from test import support import os import sys @@ -89,6 +90,26 @@ http.client.HTTPConnection = self._connection_class +class FakeFTPMixin(object): + def fakeftp(self): + class FakeFtpWrapper(object): + def __init__(self, user, passwd, host, port, dirs, timeout=None, + persistent=True): + pass + + def retrfile(self, file, type): + return io.BytesIO(), 0 + + def close(self): + pass + + self._ftpwrapper_class = urllib.request.ftpwrapper + urllib.request.ftpwrapper = FakeFtpWrapper + + def unfakeftp(self): + urllib.request.ftpwrapper = self._ftpwrapper_class + + class urlopen_FileTests(unittest.TestCase): """Test urlopen() opening a temporary file. @@ -195,7 +216,7 @@ self.env.set('NO_PROXY', 'localhost, anotherdomain.com, newdomain.com') self.assertTrue(urllib.request.proxy_bypass_environment('anotherdomain.com')) -class urlopen_HttpTests(unittest.TestCase, FakeHTTPMixin): +class urlopen_HttpTests(unittest.TestCase, FakeHTTPMixin, FakeFTPMixin): """Test urlopen() opening a fake http connection.""" def check_read(self, ver): @@ -309,6 +330,15 @@ self.assertFalse(e.exception.filename) self.assertTrue(e.exception.reason) + @patch.object(urllib.request, 'MAXFTPCACHE', 0) + def test_ftp_cache_pruning(self): + self.fakeftp() + try: + urllib.request.ftpcache['test'] = urllib.request.ftpwrapper('user', 'pass', 'localhost', 21, []) + urlopen('ftp://localhost') + finally: + self.unfakeftp() + def test_userpass_inurl(self): self.fakehttp(b"HTTP/1.0 200 OK\r\n\r\nHello!") diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1911,7 +1911,7 @@ # XXX thread unsafe! if len(self.ftpcache) > MAXFTPCACHE: # Prune the cache, rather arbitrarily - for k in self.ftpcache.keys(): + for k in list(self.ftpcache): if k != key: v = self.ftpcache[k] del self.ftpcache[k] diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -32,6 +32,7 @@ Ray Allen Billy G. Allie Kevin Altis +Skyler Leigh Amador Joe Amenta A. Amoroso Mark Anacker diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -94,6 +94,8 @@ - Issue #21515: tempfile.TemporaryFile now uses os.O_TMPFILE flag is available. +- Issue #21463: In urllib.request, fix pruning of the FTP cache. + - Issue #21618: The subprocess module could fail to close open fds that were inherited by the calling process and already higher than POSIX resource limits would otherwise allow. On systems with a functioning /proc/self/fd -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 01:47:27 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 01:47:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogZG9uJ3QgcmVtb3Zl?= =?utf-8?q?_self_from_example_code_in_the_HTML_output_=28closes_=2313223?= =?utf-8?q?=29?= Message-ID: <3gmHTC1k2zz7LkJ@mail.python.org> http://hg.python.org/cpython/rev/7aa72075d440 changeset: 91072:7aa72075d440 branch: 3.4 parent: 91070:b8f9ae84d211 user: Benjamin Peterson date: Sat Jun 07 16:44:00 2014 -0700 summary: don't remove self from example code in the HTML output (closes #13223) Patch by V?ctor Terr?n. files: Lib/pydoc.py | 9 ++++- Lib/test/pydoc_mod.py | 10 ++++++ Lib/test/test_pydoc.py | 44 ++++++++++++++++++++++++++++++ Misc/NEWS | 3 ++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -595,10 +595,15 @@ elif pep: url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) results.append('%s' % (url, escape(all))) + elif selfdot: + # Create a link for methods like 'self.method(...)' + # and use for attributes like 'self.attr' + if text[end:end+1] == '(': + results.append('self.' + self.namelink(name, methods)) + else: + results.append('self.%s' % name) elif text[end:end+1] == '(': results.append(self.namelink(name, methods, funcs, classes)) - elif selfdot: - results.append('self.%s' % name) else: results.append(self.namelink(name, classes)) here = end diff --git a/Lib/test/pydoc_mod.py b/Lib/test/pydoc_mod.py --- a/Lib/test/pydoc_mod.py +++ b/Lib/test/pydoc_mod.py @@ -15,6 +15,16 @@ NO_MEANING = "eggs" pass +class C(object): + def say_no(self): + return "no" + def get_answer(self): + """ Return say_no() """ + return self.say_no() + def is_it_true(self): + """ Return self.get_answer() """ + return self.get_answer() + def doc_func(): """ This function solves all of the world's problems: diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -47,6 +47,7 @@ builtins.object A B + C \x20\x20\x20\x20 class A(builtins.object) | Hello and goodbye @@ -74,6 +75,26 @@ | Data and other attributes defined here: |\x20\x20 | NO_MEANING = 'eggs' +\x20\x20\x20\x20 + class C(builtins.object) + | Methods defined here: + |\x20\x20 + | get_answer(self) + | Return say_no() + |\x20\x20 + | is_it_true(self) + | Return self.get_answer() + |\x20\x20 + | say_no(self) + |\x20\x20 + | ---------------------------------------------------------------------- + | Data descriptors defined here: + |\x20\x20 + | __dict__ + | dictionary for instance variables (if defined) + |\x20\x20 + | __weakref__ + | list of weak references to the object (if defined) FUNCTIONS doc_func() @@ -124,6 +145,7 @@
A
B +
C
@@ -165,6 +187,28 @@ Data and other attributes defined here:
NO_MEANING = 'eggs'
+

+ + + +\x20\x20\x20\x20 + +
 
+class C(builtins.object)
    Methods defined here:
+
get_answer(self)
Return say_no()
+ +
is_it_true(self)
Return self.get_answer()
+ +
say_no(self)
+ +
+Data descriptors defined here:
+
__dict__
+
dictionary for instance variables (if defined)
+
+
__weakref__
+
list of weak references to the object (if defined)
+

diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,9 @@ Library ------- +- Issue #13223: Fix pydoc.writedoc so that the HTML documentation for methods + that use 'self' in the example code is generated correctly. + - Issue #21463: In urllib.request, fix pruning of the FTP cache. - Issue #21618: The subprocess module could fail to close open fds that were -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 01:47:28 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 01:47:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogZG9uJ3QgcmVtb3Zl?= =?utf-8?q?_self_from_example_code_in_the_HTML_output_=28closes_=2313223?= =?utf-8?q?=29?= Message-ID: <3gmHTD4GQvz7Ll8@mail.python.org> http://hg.python.org/cpython/rev/e89c39125892 changeset: 91073:e89c39125892 branch: 2.7 parent: 91068:6dfbe504f659 user: Benjamin Peterson date: Sat Jun 07 16:44:00 2014 -0700 summary: don't remove self from example code in the HTML output (closes #13223) Patch by V?ctor Terr?n. files: Lib/pydoc.py | 9 ++++- Lib/test/pydoc_mod.py | 10 ++++++ Lib/test/test_pydoc.py | 44 ++++++++++++++++++++++++++++++ Misc/NEWS | 3 ++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -581,10 +581,15 @@ elif pep: url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) results.append('%s' % (url, escape(all))) + elif selfdot: + # Create a link for methods like 'self.method(...)' + # and use for attributes like 'self.attr' + if text[end:end+1] == '(': + results.append('self.' + self.namelink(name, methods)) + else: + results.append('self.%s' % name) elif text[end:end+1] == '(': results.append(self.namelink(name, methods, funcs, classes)) - elif selfdot: - results.append('self.%s' % name) else: results.append(self.namelink(name, classes)) here = end diff --git a/Lib/test/pydoc_mod.py b/Lib/test/pydoc_mod.py --- a/Lib/test/pydoc_mod.py +++ b/Lib/test/pydoc_mod.py @@ -15,6 +15,16 @@ NO_MEANING = "eggs" pass +class C(object): + def say_no(self): + return "no" + def get_answer(self): + """ Return say_no() """ + return self.say_no() + def is_it_true(self): + """ Return self.get_answer() """ + return self.get_answer() + def doc_func(): """ This function solves all of the world's problems: diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -38,6 +38,7 @@ CLASSES __builtin__.object B + C A \x20\x20\x20\x20 class A @@ -59,6 +60,26 @@ | Data and other attributes defined here: |\x20\x20 | NO_MEANING = 'eggs' +\x20\x20\x20\x20 + class C(__builtin__.object) + | Methods defined here: + |\x20\x20 + | get_answer(self) + | Return say_no() + |\x20\x20 + | is_it_true(self) + | Return self.get_answer() + |\x20\x20 + | say_no(self) + |\x20\x20 + | ---------------------------------------------------------------------- + | Data descriptors defined here: + |\x20\x20 + | __dict__ + | dictionary for instance variables (if defined) + |\x20\x20 + | __weakref__ + | list of weak references to the object (if defined) FUNCTIONS doc_func() @@ -108,6 +129,7 @@
B +
C
A @@ -142,6 +164,28 @@ Data and other attributes defined here:
NO_MEANING = 'eggs'
+

+ + + +\x20\x20\x20\x20 + +
 
+class C(__builtin__.object)
    Methods defined here:
+
get_answer(self)
Return say_no()
+ +
is_it_true(self)
Return self.get_answer()
+ +
say_no(self)
+ +
+Data descriptors defined here:
+
__dict__
+
dictionary for instance variables (if defined)
+
+
__weakref__
+
list of weak references to the object (if defined)
+

diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -28,6 +28,9 @@ - Issue #21304: Backport the key derivation function hashlib.pbkdf2_hmac from Python 3 per PEP 466. +- Issue #13223: Fix pydoc.writedoc so that the HTML documentation for methods + that use 'self' in the example code is generated correctly. + - Issue #21552: Fixed possible integer overflow of too long string lengths in the tkinter module on 64-bit platforms. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 01:47:30 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 01:47:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMTMyMjMp?= Message-ID: <3gmHTG02gHz7LlR@mail.python.org> http://hg.python.org/cpython/rev/cddb17c4975e changeset: 91074:cddb17c4975e parent: 91071:6f70a18313e5 parent: 91072:7aa72075d440 user: Benjamin Peterson date: Sat Jun 07 16:47:15 2014 -0700 summary: merge 3.4 (#13223) files: Lib/pydoc.py | 9 ++++- Lib/test/pydoc_mod.py | 10 ++++++ Lib/test/test_pydoc.py | 44 ++++++++++++++++++++++++++++++ Misc/NEWS | 3 ++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -593,10 +593,15 @@ elif pep: url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) results.append('%s' % (url, escape(all))) + elif selfdot: + # Create a link for methods like 'self.method(...)' + # and use for attributes like 'self.attr' + if text[end:end+1] == '(': + results.append('self.' + self.namelink(name, methods)) + else: + results.append('self.%s' % name) elif text[end:end+1] == '(': results.append(self.namelink(name, methods, funcs, classes)) - elif selfdot: - results.append('self.%s' % name) else: results.append(self.namelink(name, classes)) here = end diff --git a/Lib/test/pydoc_mod.py b/Lib/test/pydoc_mod.py --- a/Lib/test/pydoc_mod.py +++ b/Lib/test/pydoc_mod.py @@ -15,6 +15,16 @@ NO_MEANING = "eggs" pass +class C(object): + def say_no(self): + return "no" + def get_answer(self): + """ Return say_no() """ + return self.say_no() + def is_it_true(self): + """ Return self.get_answer() """ + return self.get_answer() + def doc_func(): """ This function solves all of the world's problems: diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -47,6 +47,7 @@ builtins.object A B + C \x20\x20\x20\x20 class A(builtins.object) | Hello and goodbye @@ -74,6 +75,26 @@ | Data and other attributes defined here: |\x20\x20 | NO_MEANING = 'eggs' +\x20\x20\x20\x20 + class C(builtins.object) + | Methods defined here: + |\x20\x20 + | get_answer(self) + | Return say_no() + |\x20\x20 + | is_it_true(self) + | Return self.get_answer() + |\x20\x20 + | say_no(self) + |\x20\x20 + | ---------------------------------------------------------------------- + | Data descriptors defined here: + |\x20\x20 + | __dict__ + | dictionary for instance variables (if defined) + |\x20\x20 + | __weakref__ + | list of weak references to the object (if defined) FUNCTIONS doc_func() @@ -124,6 +145,7 @@
A
B +
C
@@ -165,6 +187,28 @@ Data and other attributes defined here:
NO_MEANING = 'eggs'
+

+ + + +\x20\x20\x20\x20 + +
 
+class C(builtins.object)
    Methods defined here:
+
get_answer(self)
Return say_no()
+ +
is_it_true(self)
Return self.get_answer()
+ +
say_no(self)
+ +
+Data descriptors defined here:
+
__dict__
+
dictionary for instance variables (if defined)
+
+
__weakref__
+
list of weak references to the object (if defined)
+

diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -94,6 +94,9 @@ - Issue #21515: tempfile.TemporaryFile now uses os.O_TMPFILE flag is available. +- Issue #13223: Fix pydoc.writedoc so that the HTML documentation for methods + that use 'self' in the example code is generated correctly. + - Issue #21463: In urllib.request, fix pruning of the FTP cache. - Issue #21618: The subprocess module could fail to close open fds that were -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 02:56:01 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 02:56:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_document_Token?= =?utf-8?q?Error_and_unclosed_expression_behavior_=28closes_=2312063=29?= Message-ID: <3gmK0K1Xvdz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/188e5f42d4aa changeset: 91075:188e5f42d4aa branch: 2.7 parent: 91073:e89c39125892 user: Benjamin Peterson date: Sat Jun 07 17:47:41 2014 -0700 summary: document TokenError and unclosed expression behavior (closes #12063) Patch by Amandine Lee. files: Doc/library/tokenize.rst | 18 ++++++++++++++++++ Misc/ACKS | 1 + 2 files changed, 19 insertions(+), 0 deletions(-) diff --git a/Doc/library/tokenize.rst b/Doc/library/tokenize.rst --- a/Doc/library/tokenize.rst +++ b/Doc/library/tokenize.rst @@ -98,6 +98,24 @@ .. versionadded:: 2.5 +.. exception:: TokenError + + Raised when either a docstring or expression that may be split over several + lines is not completed anywhere in the file, for example:: + + """Beginning of + docstring + + or:: + + [1, + 2, + 3 + +Note that unclosed single-quoted strings do not cause an error to be +raised. They are tokenized as ``ERRORTOKEN``, followed by the tokenization of +their contents. + Example of a script re-writer that transforms float literals into Decimal objects:: diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -750,6 +750,7 @@ Chris Lawrence Brian Leair Mathieu Leduc-Hamel +Amandine Lee Christopher Lee Inyeol Lee James Lee -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 02:56:02 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 02:56:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_document_Token?= =?utf-8?q?Error_and_unclosed_expression_behavior_=28closes_=2312063=29?= Message-ID: <3gmK0L3BqVz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/ddc174c4c7e5 changeset: 91076:ddc174c4c7e5 branch: 3.4 parent: 91072:7aa72075d440 user: Benjamin Peterson date: Sat Jun 07 17:47:41 2014 -0700 summary: document TokenError and unclosed expression behavior (closes #12063) Patch by Amandine Lee. files: Doc/library/tokenize.rst | 18 ++++++++++++++++++ Misc/ACKS | 1 + 2 files changed, 19 insertions(+), 0 deletions(-) diff --git a/Doc/library/tokenize.rst b/Doc/library/tokenize.rst --- a/Doc/library/tokenize.rst +++ b/Doc/library/tokenize.rst @@ -131,6 +131,24 @@ .. versionadded:: 3.2 +.. exception:: TokenError + + Raised when either a docstring or expression that may be split over several + lines is not completed anywhere in the file, for example:: + + """Beginning of + docstring + + or:: + + [1, + 2, + 3 + +Note that unclosed single-quoted strings do not cause an error to be +raised. They are tokenized as ``ERRORTOKEN``, followed by the tokenization of +their contents. + .. _tokenize-cli: diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -749,6 +749,7 @@ Chris Lawrence Brian Leair Mathieu Leduc-Hamel +Amandine Lee Antony Lee Christopher Lee Inyeol Lee -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 02:56:03 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 02:56:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMTIwNjMp?= Message-ID: <3gmK0M4yV1z7LkD@mail.python.org> http://hg.python.org/cpython/rev/3f2f1ffc3ce2 changeset: 91077:3f2f1ffc3ce2 parent: 91074:cddb17c4975e parent: 91076:ddc174c4c7e5 user: Benjamin Peterson date: Sat Jun 07 17:55:53 2014 -0700 summary: merge 3.4 (#12063) files: Doc/library/tokenize.rst | 18 ++++++++++++++++++ Misc/ACKS | 1 + 2 files changed, 19 insertions(+), 0 deletions(-) diff --git a/Doc/library/tokenize.rst b/Doc/library/tokenize.rst --- a/Doc/library/tokenize.rst +++ b/Doc/library/tokenize.rst @@ -131,6 +131,24 @@ .. versionadded:: 3.2 +.. exception:: TokenError + + Raised when either a docstring or expression that may be split over several + lines is not completed anywhere in the file, for example:: + + """Beginning of + docstring + + or:: + + [1, + 2, + 3 + +Note that unclosed single-quoted strings do not cause an error to be +raised. They are tokenized as ``ERRORTOKEN``, followed by the tokenization of +their contents. + .. _tokenize-cli: diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -755,6 +755,7 @@ Chris Lawrence Brian Leair Mathieu Leduc-Hamel +Amandine Lee Antony Lee Christopher Lee Inyeol Lee -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 02:57:42 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 02:57:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_backed_out_86b?= =?utf-8?q?a41b7bb46_=28=2318910=29_for_test_breakage?= Message-ID: <3gmK2G3Bclz7Ljr@mail.python.org> http://hg.python.org/cpython/rev/a0be81607a50 changeset: 91078:a0be81607a50 branch: 2.7 parent: 91075:188e5f42d4aa user: Benjamin Peterson date: Sat Jun 07 17:57:36 2014 -0700 summary: backed out 86ba41b7bb46 (#18910) for test breakage files: Lib/idlelib/idle_test/test_textview.py | 71 +++++++------ 1 files changed, 36 insertions(+), 35 deletions(-) diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -1,60 +1,58 @@ -'''Test the functions and main class method of textView.py. - -Since all methods and functions create (or destroy) a TextViewer, which -is a widget containing multiple widgets, all tests must be gui tests. -Using mock Text would not change this. Other mocks are used to retrieve -information about calls. - -The coverage is essentially 100%. -''' -from test.test_support import requires -requires('gui') +'''Test the functions and main class method of textView.py.''' import unittest import os +from test.test_support import requires from Tkinter import Tk, Text, TclError from idlelib import textView as tv from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Mbox -def setUpModule(): - global root - root = Tk() +orig_mbox = tv.tkMessageBox -def tearDownModule(): - global root - root.destroy() - del root +class textviewClassTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.TV = TV = tv.TextViewer + TV.transient = Func() + TV.grab_set = Func() + TV.wait_window = Func() -class TV(tv.TextViewer): # used by TextViewTest - transient = Func() - grab_set = Func() - wait_window = Func() - -class TextViewTest(unittest.TestCase): + @classmethod + def tearDownClass(cls): + cls.root.destroy() + TV = cls.TV + del cls.root, cls.TV + del TV.transient, TV.grab_set, TV.wait_window def setUp(self): + TV = self.TV TV.transient.__init__() TV.grab_set.__init__() TV.wait_window.__init__() + def test_init_modal(self): - view = TV(root, 'Title', 'test text') + TV = self.TV + view = TV(self.root, 'Title', 'test text') self.assertTrue(TV.transient.called) self.assertTrue(TV.grab_set.called) self.assertTrue(TV.wait_window.called) view.Ok() def test_init_nonmodal(self): - view = TV(root, 'Title', 'test text', modal=False) + TV = self.TV + view = TV(self.root, 'Title', 'test text', modal=False) self.assertFalse(TV.transient.called) self.assertFalse(TV.grab_set.called) self.assertFalse(TV.wait_window.called) view.Ok() def test_ok(self): - view = TV(root, 'Title', 'test text', modal=False) + view = self.TV(self.root, 'Title', 'test text', modal=False) view.destroy = Func() view.Ok() self.assertTrue(view.destroy.called) @@ -66,32 +64,35 @@ @classmethod def setUpClass(cls): - cls.orig_mbox = tv.tkMessageBox + requires('gui') + cls.root = Tk() tv.tkMessageBox = Mbox @classmethod def tearDownClass(cls): - tv.tkMessageBox = cls.orig_mbox - del cls.orig_mbox + cls.root.destroy() + del cls.root + tv.tkMessageBox = orig_mbox def test_view_text(self): # If modal True, tkinter will error with 'can't invoke "event" command' - view = tv.view_text(root, 'Title', 'test text', modal=False) + view = tv.view_text(self.root, 'Title', 'test text', modal=False) self.assertIsInstance(view, tv.TextViewer) def test_view_file(self): test_dir = os.path.dirname(__file__) testfile = os.path.join(test_dir, 'test_textview.py') - view = tv.view_file(root, 'Title', testfile, modal=False) + view = tv.view_file(self.root, 'Title', testfile, modal=False) self.assertIsInstance(view, tv.TextViewer) self.assertIn('Test', view.textView.get('1.0', '1.end')) view.Ok() # Mock messagebox will be used and view_file will not return anything testfile = os.path.join(test_dir, '../notthere.py') - view = tv.view_file(root, 'Title', testfile, modal=False) + view = tv.view_file(self.root, 'Title', testfile, modal=False) self.assertIsNone(view) - if __name__ == '__main__': - unittest.main(verbosity=2) + unittest.main(verbosity=2, exit=False) + from idlelib.idle_test.htest import run + run(TextViewer) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 05:07:37 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 05:07:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_add_BufferedIOBase=2Ereadi?= =?utf-8?q?nto1_=28closes_=2320578=29?= Message-ID: <3gmMw917V8z7LjT@mail.python.org> http://hg.python.org/cpython/rev/0fb7789b5eeb changeset: 91079:0fb7789b5eeb parent: 91077:3f2f1ffc3ce2 user: Benjamin Peterson date: Sat Jun 07 20:06:48 2014 -0700 summary: add BufferedIOBase.readinto1 (closes #20578) Patch by Nikolaus Rath. files: Doc/library/io.rst | 31 ++++++++-- Lib/_pyio.py | 83 +++++++++++++++++++++++++++- Lib/test/test_io.py | 25 ++++++++ Misc/NEWS | 2 + Modules/_io/bufferedio.c | 67 ++++++++++++++++++++-- 5 files changed, 194 insertions(+), 14 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -385,8 +385,8 @@ .. method:: readinto(b) Read up to ``len(b)`` bytes into :class:`bytearray` *b* and return the - number of bytes read. If the object is in non-blocking mode and no - bytes are available, ``None`` is returned. + number of bytes read. If the object is in non-blocking mode and no bytes + are available, ``None`` is returned. .. method:: write(b) @@ -459,10 +459,11 @@ .. method:: read1(size=-1) - Read and return up to *size* bytes, with at most one call to the underlying - raw stream's :meth:`~RawIOBase.read` method. This can be useful if you - are implementing your own buffering on top of a :class:`BufferedIOBase` - object. + Read and return up to *size* bytes, with at most one call to the + underlying raw stream's :meth:`~RawIOBase.read` (or + :meth:`~RawIOBase.readinto`) method. This can be useful if you + are implementing your own buffering on top of a + :class:`BufferedIOBase` object. .. method:: readinto(b) @@ -472,8 +473,19 @@ Like :meth:`read`, multiple reads may be issued to the underlying raw stream, unless the latter is interactive. + A :exc:`BlockingIOError` is raised if the underlying raw stream is in non + blocking-mode, and has no data available at the moment. + + .. method:: readinto1(b) + + Read up to ``len(b)`` bytes into bytearray *b*, using at most one call to + the underlying raw stream's :meth:`~RawIOBase.read` (or + :meth:`~RawIOBase.readinto`) method. Return the number of bytes read. + A :exc:`BlockingIOError` is raised if the underlying raw stream is in - non blocking-mode, and has no data available at the moment. + non-blocking mode and has no data available at the moment. + + .. versionadded:: 3.5 .. method:: write(b) @@ -590,6 +602,11 @@ In :class:`BytesIO`, this is the same as :meth:`read`. + .. method:: readinto1() + + In :class:`BytesIO`, this is the same as :meth:`readinto`. + + .. versionadded:: 3.5 .. class:: BufferedReader(raw, buffer_size=DEFAULT_BUFFER_SIZE) diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -655,8 +655,26 @@ Raises BlockingIOError if the underlying raw stream has no data at the moment. """ + + return self._readinto(b, read1=False) + + def readinto1(self, b): + """Read up to len(b) bytes into *b*, using at most one system call + + Returns an int representing the number of bytes read (0 for EOF). + + Raises BlockingIOError if the underlying raw stream has no + data at the moment. + """ + + return self._readinto(b, read1=True) + + def _readinto(self, b, read1): # XXX This ought to work with anything that supports the buffer API - data = self.read(len(b)) + if read1: + data = self.read1(len(b)) + else: + data = self.read(len(b)) n = len(data) try: b[:n] = data @@ -1058,6 +1076,62 @@ return self._read_unlocked( min(size, len(self._read_buf) - self._read_pos)) + # Implementing readinto() and readinto1() is not strictly necessary (we + # could rely on the base class that provides an implementation in terms of + # read() and read1()). We do ai anyway to keep the _pyio implementation + # similar to the io implementation (which implements the methods for + # performance reasons). + def readinto(self, buf): + """Read data into *buf*.""" + return self._readinto(buf, read1=False) + def readinto1(self, buf): + """Read data into *buf* with at most one system call.""" + return self._readinto(buf, read1=True) + + def _readinto(self, buf, read1): + """Read data into *buf* with at most one system call.""" + + if len(buf) == 0: + return 0 + + written = 0 + with self._read_lock: + while written < len(buf): + + # First try to read from internal buffer + avail = min(len(self._read_buf) - self._read_pos, len(buf)) + if avail: + buf[written:written+avail] = \ + self._read_buf[self._read_pos:self._read_pos+avail] + self._read_pos += avail + written += avail + if written == len(buf): + break + + # If remaining space in callers buffer is larger than + # internal buffer, read directly into callers buffer + if len(buf) - written > self.buffer_size: + # If we don't use a memoryview, slicing buf will create + # a new object + if not isinstance(buf, memoryview): + buf = memoryview(buf) + n = self.raw.readinto(buf[written:]) + if not n: + break # eof + written += n + + # Otherwise refill internal buffer - unless we're + # in read1 mode and already got some data + elif not (read1 and written): + if not self._peek_unlocked(1): + break # eof + + # In readinto1 mode, return as soon as we have some data + if read1 and written: + break + + return written + def tell(self): return _BufferedIOMixin.tell(self) - len(self._read_buf) + self._read_pos @@ -1207,6 +1281,9 @@ def read1(self, size): return self.reader.read1(size) + def readinto1(self, b): + return self.reader.readinto1(b) + def readable(self): return self.reader.readable() @@ -1289,6 +1366,10 @@ self.flush() return BufferedReader.read1(self, size) + def readinto1(self, b): + self.flush() + return BufferedReader.readinto1(self, b) + def write(self, b): if self._read_buf: # Undo readahead 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 @@ -911,6 +911,29 @@ self.assertEqual(bufio.readinto(b), 1) self.assertEqual(b, b"cb") + def test_readinto1(self): + buffer_size = 10 + rawio = self.MockRawIO((b"abc", b"de", b"fgh", b"jkl")) + bufio = self.tp(rawio, buffer_size=buffer_size) + b = bytearray(2) + self.assertEqual(bufio.peek(3), b'abc') + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 2) + self.assertEqual(b, b"ab") + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 1) + self.assertEqual(b[:1], b"c") + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 2) + self.assertEqual(b, b"de") + self.assertEqual(rawio._reads, 2) + b = bytearray(2*buffer_size) + self.assertEqual(bufio.peek(3), b'fgh') + self.assertEqual(rawio._reads, 3) + self.assertEqual(bufio.readinto1(b), 6) + self.assertEqual(b[:6], b"fghjkl") + self.assertEqual(rawio._reads, 4) + def test_readlines(self): def bufio(): rawio = self.MockRawIO((b"abc\n", b"d\n", b"ef")) @@ -2985,6 +3008,8 @@ self.assertRaises(ValueError, f.readall) if hasattr(f, "readinto"): self.assertRaises(ValueError, f.readinto, bytearray(1024)) + if hasattr(f, "readinto1"): + self.assertRaises(ValueError, f.readinto1, bytearray(1024)) self.assertRaises(ValueError, f.readline) self.assertRaises(ValueError, f.readlines) self.assertRaises(ValueError, f.seek, 0) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,6 +92,8 @@ Library ------- +- Issue #20578: Add io.BufferedIOBase.readinto1. + - Issue #21515: tempfile.TemporaryFile now uses os.O_TMPFILE flag is available. - Issue #13223: Fix pydoc.writedoc so that the HTML documentation for methods diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -24,6 +24,7 @@ _Py_IDENTIFIER(read1); _Py_IDENTIFIER(readable); _Py_IDENTIFIER(readinto); +_Py_IDENTIFIER(readinto1); _Py_IDENTIFIER(writable); _Py_IDENTIFIER(write); @@ -47,17 +48,21 @@ ); static PyObject * -bufferediobase_readinto(PyObject *self, PyObject *args) +_bufferediobase_readinto_generic(PyObject *self, PyObject *args, char readinto1) { Py_buffer buf; Py_ssize_t len; PyObject *data; - if (!PyArg_ParseTuple(args, "w*:readinto", &buf)) { + if (!PyArg_ParseTuple(args, + readinto1 ? "w*:readinto1" : "w*:readinto", + &buf)) { return NULL; } - data = _PyObject_CallMethodId(self, &PyId_read, "n", buf.len); + data = _PyObject_CallMethodId(self, + readinto1 ? &PyId_read1 : &PyId_read, + "n", buf.len); if (data == NULL) goto error; @@ -89,6 +94,18 @@ } static PyObject * +bufferediobase_readinto(PyObject *self, PyObject *args) +{ + return _bufferediobase_readinto_generic(self, args, 0); +} + +static PyObject * +bufferediobase_readinto1(PyObject *self, PyObject *args) +{ + return _bufferediobase_readinto_generic(self, args, 1); +} + +static PyObject * bufferediobase_unsupported(const char *message) { _PyIO_State *state = IO_STATE(); @@ -167,6 +184,7 @@ {"read", bufferediobase_read, METH_VARARGS, bufferediobase_read_doc}, {"read1", bufferediobase_read1, METH_VARARGS, bufferediobase_read1_doc}, {"readinto", bufferediobase_readinto, METH_VARARGS, NULL}, + {"readinto1", bufferediobase_readinto1, METH_VARARGS, NULL}, {"write", bufferediobase_write, METH_VARARGS, bufferediobase_write_doc}, {NULL, NULL} }; @@ -988,7 +1006,7 @@ } static PyObject * -buffered_readinto(buffered *self, PyObject *args) +_buffered_readinto_generic(buffered *self, PyObject *args, char readinto1) { Py_buffer buf; Py_ssize_t n, written = 0, remaining; @@ -996,7 +1014,9 @@ CHECK_INITIALIZED(self) - if (!PyArg_ParseTuple(args, "w*:readinto", &buf)) + if (!PyArg_ParseTuple(args, + readinto1 ? "w*:readinto1" : "w*:readinto", + &buf)) return NULL; n = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t); @@ -1034,7 +1054,10 @@ n = _bufferedreader_raw_read(self, (char *) buf.buf + written, remaining); } - else { + + /* In readinto1 mode, we do not want to fill the internal + buffer if we already have some data to return */ + else if (!(readinto1 && written)) { n = _bufferedreader_fill_buffer(self); if (n > 0) { if (n > remaining) @@ -1045,6 +1068,10 @@ continue; /* short circuit */ } } + else { + n = 0; + } + if (n == 0 || (n == -2 && written > 0)) break; if (n < 0) { @@ -1054,6 +1081,12 @@ } goto end; } + + /* At most one read in readinto1 mode */ + if (readinto1) { + written += n; + break; + } } res = PyLong_FromSsize_t(written); @@ -1065,6 +1098,19 @@ } static PyObject * +buffered_readinto(buffered *self, PyObject *args) +{ + return _buffered_readinto_generic(self, args, 0); +} + +static PyObject * +buffered_readinto1(buffered *self, PyObject *args) +{ + return _buffered_readinto_generic(self, args, 1); +} + + +static PyObject * _buffered_readline(buffered *self, Py_ssize_t limit) { PyObject *res = NULL; @@ -1749,6 +1795,7 @@ {"peek", (PyCFunction)buffered_peek, METH_VARARGS}, {"read1", (PyCFunction)buffered_read1, METH_VARARGS}, {"readinto", (PyCFunction)buffered_readinto, METH_VARARGS}, + {"readinto1", (PyCFunction)buffered_readinto1, METH_VARARGS}, {"readline", (PyCFunction)buffered_readline, METH_VARARGS}, {"seek", (PyCFunction)buffered_seek, METH_VARARGS}, {"tell", (PyCFunction)buffered_tell, METH_NOARGS}, @@ -2348,6 +2395,12 @@ } static PyObject * +bufferedrwpair_readinto1(rwpair *self, PyObject *args) +{ + return _forward_call(self->reader, &PyId_readinto1, args); +} + +static PyObject * bufferedrwpair_write(rwpair *self, PyObject *args) { return _forward_call(self->writer, &PyId_write, args); @@ -2412,6 +2465,7 @@ {"peek", (PyCFunction)bufferedrwpair_peek, METH_VARARGS}, {"read1", (PyCFunction)bufferedrwpair_read1, METH_VARARGS}, {"readinto", (PyCFunction)bufferedrwpair_readinto, METH_VARARGS}, + {"readinto1", (PyCFunction)bufferedrwpair_readinto1, METH_VARARGS}, {"write", (PyCFunction)bufferedrwpair_write, METH_VARARGS}, {"flush", (PyCFunction)bufferedrwpair_flush, METH_NOARGS}, @@ -2560,6 +2614,7 @@ {"read", (PyCFunction)buffered_read, METH_VARARGS}, {"read1", (PyCFunction)buffered_read1, METH_VARARGS}, {"readinto", (PyCFunction)buffered_readinto, METH_VARARGS}, + {"readinto1", (PyCFunction)buffered_readinto1, METH_VARARGS}, {"readline", (PyCFunction)buffered_readline, METH_VARARGS}, {"peek", (PyCFunction)buffered_peek, METH_VARARGS}, {"write", (PyCFunction)bufferedwriter_write, METH_VARARGS}, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 05:17:44 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 05:17:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_make_sure_the_?= =?utf-8?q?builtin_help_function_doesn=27t_fail_when_sys=2Estdin_is_not_a_?= =?utf-8?q?valid?= Message-ID: <3gmN7r2q6tz7Ljq@mail.python.org> http://hg.python.org/cpython/rev/baca52bb5c74 changeset: 91080:baca52bb5c74 branch: 3.4 parent: 91076:ddc174c4c7e5 user: Benjamin Peterson date: Sat Jun 07 20:14:26 2014 -0700 summary: make sure the builtin help function doesn't fail when sys.stdin is not a valid file (closes #11709) Original patch by Amaury Forgeot d'Arc with a test by bdettmer. files: Lib/pydoc.py | 2 ++ Lib/test/test_pydoc.py | 8 ++++++++ Misc/NEWS | 3 +++ 3 files changed, 13 insertions(+), 0 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1417,6 +1417,8 @@ def getpager(): """Decide what method to use for paging through text.""" + if not hasattr(sys.stdin, "isatty"): + return plainpager if not hasattr(sys.stdout, "isatty"): return plainpager if not sys.stdin.isatty() or not sys.stdout.isatty(): diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -446,6 +446,14 @@ result, doc_loc = get_pydoc_text(xml.etree) self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link") + def test_getpager_with_stdin_none(self): + previous_stdin = sys.stdin + try: + sys.stdin = None + pydoc.getpager() # Shouldn't fail. + finally: + sys.stdin = previous_stdin + def test_non_str_name(self): # issue14638 # Treat illegal (non-str) name like no name diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,9 @@ Library ------- +- Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a + valid file. + - Issue #13223: Fix pydoc.writedoc so that the HTML documentation for methods that use 'self' in the example code is generated correctly. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 05:17:45 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 05:17:45 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_make_sure_the_?= =?utf-8?q?builtin_help_function_doesn=27t_fail_when_sys=2Estdin_is_not_a_?= =?utf-8?q?valid?= Message-ID: <3gmN7s4mQ8z7Lkg@mail.python.org> http://hg.python.org/cpython/rev/1a9c07880a15 changeset: 91081:1a9c07880a15 branch: 2.7 parent: 91078:a0be81607a50 user: Benjamin Peterson date: Sat Jun 07 20:14:26 2014 -0700 summary: make sure the builtin help function doesn't fail when sys.stdin is not a valid file (closes #11709) Original patch by Amaury Forgeot d'Arc with a test by bdettmer. files: Lib/pydoc.py | 2 ++ Lib/test/test_pydoc.py | 8 ++++++++ Misc/NEWS | 3 +++ 3 files changed, 13 insertions(+), 0 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1377,6 +1377,8 @@ """Decide what method to use for paging through text.""" if type(sys.stdout) is not types.FileType: return plainpager + if not hasattr(sys.stdin, "isatty"): + return plainpager if not sys.stdin.isatty() or not sys.stdout.isatty(): return plainpager if 'PAGER' in os.environ: diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -333,6 +333,14 @@ result, doc_loc = get_pydoc_text(xml.etree) self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link") + def test_getpager_with_stdin_none(self): + previous_stdin = sys.stdin + try: + sys.stdin = None + pydoc.getpager() # Shouldn't fail. + finally: + sys.stdin = previous_stdin + def test_non_str_name(self): # issue14638 # Treat illegal (non-str) name like no name diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -28,6 +28,9 @@ - Issue #21304: Backport the key derivation function hashlib.pbkdf2_hmac from Python 3 per PEP 466. +- Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a + valid file. + - Issue #13223: Fix pydoc.writedoc so that the HTML documentation for methods that use 'self' in the example code is generated correctly. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 05:17:46 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 05:17:46 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMTE3MDkp?= Message-ID: <3gmN7t6gFVz7LkF@mail.python.org> http://hg.python.org/cpython/rev/3bbb8cb45f58 changeset: 91082:3bbb8cb45f58 parent: 91079:0fb7789b5eeb parent: 91080:baca52bb5c74 user: Benjamin Peterson date: Sat Jun 07 20:17:29 2014 -0700 summary: merge 3.4 (#11709) files: Lib/pydoc.py | 2 ++ Lib/test/test_pydoc.py | 8 ++++++++ Misc/NEWS | 3 +++ 3 files changed, 13 insertions(+), 0 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1415,6 +1415,8 @@ def getpager(): """Decide what method to use for paging through text.""" + if not hasattr(sys.stdin, "isatty"): + return plainpager if not hasattr(sys.stdout, "isatty"): return plainpager if not sys.stdin.isatty() or not sys.stdout.isatty(): diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -446,6 +446,14 @@ result, doc_loc = get_pydoc_text(xml.etree) self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link") + def test_getpager_with_stdin_none(self): + previous_stdin = sys.stdin + try: + sys.stdin = None + pydoc.getpager() # Shouldn't fail. + finally: + sys.stdin = previous_stdin + def test_non_str_name(self): # issue14638 # Treat illegal (non-str) name like no name diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -94,6 +94,9 @@ - Issue #20578: Add io.BufferedIOBase.readinto1. +- Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a + valid file. + - Issue #21515: tempfile.TemporaryFile now uses os.O_TMPFILE flag is available. - Issue #13223: Fix pydoc.writedoc so that the HTML documentation for methods -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 08:18:21 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Jun 2014 08:18:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_backout_0fb7789b5eeb_for_t?= =?utf-8?q?est_breakage_=28=2320578=29?= Message-ID: <3gmS8F4Hdmz7LjT@mail.python.org> http://hg.python.org/cpython/rev/b1e99b4ec374 changeset: 91083:b1e99b4ec374 user: Benjamin Peterson date: Sat Jun 07 23:18:12 2014 -0700 summary: backout 0fb7789b5eeb for test breakage (#20578) files: Doc/library/io.rst | 31 ++-------- Lib/_pyio.py | 83 +--------------------------- Lib/test/test_io.py | 25 -------- Misc/NEWS | 2 - Modules/_io/bufferedio.c | 67 ++-------------------- 5 files changed, 14 insertions(+), 194 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -385,8 +385,8 @@ .. method:: readinto(b) Read up to ``len(b)`` bytes into :class:`bytearray` *b* and return the - number of bytes read. If the object is in non-blocking mode and no bytes - are available, ``None`` is returned. + number of bytes read. If the object is in non-blocking mode and no + bytes are available, ``None`` is returned. .. method:: write(b) @@ -459,11 +459,10 @@ .. method:: read1(size=-1) - Read and return up to *size* bytes, with at most one call to the - underlying raw stream's :meth:`~RawIOBase.read` (or - :meth:`~RawIOBase.readinto`) method. This can be useful if you - are implementing your own buffering on top of a - :class:`BufferedIOBase` object. + Read and return up to *size* bytes, with at most one call to the underlying + raw stream's :meth:`~RawIOBase.read` method. This can be useful if you + are implementing your own buffering on top of a :class:`BufferedIOBase` + object. .. method:: readinto(b) @@ -473,19 +472,8 @@ Like :meth:`read`, multiple reads may be issued to the underlying raw stream, unless the latter is interactive. - A :exc:`BlockingIOError` is raised if the underlying raw stream is in non - blocking-mode, and has no data available at the moment. - - .. method:: readinto1(b) - - Read up to ``len(b)`` bytes into bytearray *b*, using at most one call to - the underlying raw stream's :meth:`~RawIOBase.read` (or - :meth:`~RawIOBase.readinto`) method. Return the number of bytes read. - A :exc:`BlockingIOError` is raised if the underlying raw stream is in - non-blocking mode and has no data available at the moment. - - .. versionadded:: 3.5 + non blocking-mode, and has no data available at the moment. .. method:: write(b) @@ -602,11 +590,6 @@ In :class:`BytesIO`, this is the same as :meth:`read`. - .. method:: readinto1() - - In :class:`BytesIO`, this is the same as :meth:`readinto`. - - .. versionadded:: 3.5 .. class:: BufferedReader(raw, buffer_size=DEFAULT_BUFFER_SIZE) diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -655,26 +655,8 @@ Raises BlockingIOError if the underlying raw stream has no data at the moment. """ - - return self._readinto(b, read1=False) - - def readinto1(self, b): - """Read up to len(b) bytes into *b*, using at most one system call - - Returns an int representing the number of bytes read (0 for EOF). - - Raises BlockingIOError if the underlying raw stream has no - data at the moment. - """ - - return self._readinto(b, read1=True) - - def _readinto(self, b, read1): # XXX This ought to work with anything that supports the buffer API - if read1: - data = self.read1(len(b)) - else: - data = self.read(len(b)) + data = self.read(len(b)) n = len(data) try: b[:n] = data @@ -1076,62 +1058,6 @@ return self._read_unlocked( min(size, len(self._read_buf) - self._read_pos)) - # Implementing readinto() and readinto1() is not strictly necessary (we - # could rely on the base class that provides an implementation in terms of - # read() and read1()). We do ai anyway to keep the _pyio implementation - # similar to the io implementation (which implements the methods for - # performance reasons). - def readinto(self, buf): - """Read data into *buf*.""" - return self._readinto(buf, read1=False) - def readinto1(self, buf): - """Read data into *buf* with at most one system call.""" - return self._readinto(buf, read1=True) - - def _readinto(self, buf, read1): - """Read data into *buf* with at most one system call.""" - - if len(buf) == 0: - return 0 - - written = 0 - with self._read_lock: - while written < len(buf): - - # First try to read from internal buffer - avail = min(len(self._read_buf) - self._read_pos, len(buf)) - if avail: - buf[written:written+avail] = \ - self._read_buf[self._read_pos:self._read_pos+avail] - self._read_pos += avail - written += avail - if written == len(buf): - break - - # If remaining space in callers buffer is larger than - # internal buffer, read directly into callers buffer - if len(buf) - written > self.buffer_size: - # If we don't use a memoryview, slicing buf will create - # a new object - if not isinstance(buf, memoryview): - buf = memoryview(buf) - n = self.raw.readinto(buf[written:]) - if not n: - break # eof - written += n - - # Otherwise refill internal buffer - unless we're - # in read1 mode and already got some data - elif not (read1 and written): - if not self._peek_unlocked(1): - break # eof - - # In readinto1 mode, return as soon as we have some data - if read1 and written: - break - - return written - def tell(self): return _BufferedIOMixin.tell(self) - len(self._read_buf) + self._read_pos @@ -1281,9 +1207,6 @@ def read1(self, size): return self.reader.read1(size) - def readinto1(self, b): - return self.reader.readinto1(b) - def readable(self): return self.reader.readable() @@ -1366,10 +1289,6 @@ self.flush() return BufferedReader.read1(self, size) - def readinto1(self, b): - self.flush() - return BufferedReader.readinto1(self, b) - def write(self, b): if self._read_buf: # Undo readahead 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 @@ -911,29 +911,6 @@ self.assertEqual(bufio.readinto(b), 1) self.assertEqual(b, b"cb") - def test_readinto1(self): - buffer_size = 10 - rawio = self.MockRawIO((b"abc", b"de", b"fgh", b"jkl")) - bufio = self.tp(rawio, buffer_size=buffer_size) - b = bytearray(2) - self.assertEqual(bufio.peek(3), b'abc') - self.assertEqual(rawio._reads, 1) - self.assertEqual(bufio.readinto1(b), 2) - self.assertEqual(b, b"ab") - self.assertEqual(rawio._reads, 1) - self.assertEqual(bufio.readinto1(b), 1) - self.assertEqual(b[:1], b"c") - self.assertEqual(rawio._reads, 1) - self.assertEqual(bufio.readinto1(b), 2) - self.assertEqual(b, b"de") - self.assertEqual(rawio._reads, 2) - b = bytearray(2*buffer_size) - self.assertEqual(bufio.peek(3), b'fgh') - self.assertEqual(rawio._reads, 3) - self.assertEqual(bufio.readinto1(b), 6) - self.assertEqual(b[:6], b"fghjkl") - self.assertEqual(rawio._reads, 4) - def test_readlines(self): def bufio(): rawio = self.MockRawIO((b"abc\n", b"d\n", b"ef")) @@ -3008,8 +2985,6 @@ self.assertRaises(ValueError, f.readall) if hasattr(f, "readinto"): self.assertRaises(ValueError, f.readinto, bytearray(1024)) - if hasattr(f, "readinto1"): - self.assertRaises(ValueError, f.readinto1, bytearray(1024)) self.assertRaises(ValueError, f.readline) self.assertRaises(ValueError, f.readlines) self.assertRaises(ValueError, f.seek, 0) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,8 +92,6 @@ Library ------- -- Issue #20578: Add io.BufferedIOBase.readinto1. - - Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a valid file. diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -24,7 +24,6 @@ _Py_IDENTIFIER(read1); _Py_IDENTIFIER(readable); _Py_IDENTIFIER(readinto); -_Py_IDENTIFIER(readinto1); _Py_IDENTIFIER(writable); _Py_IDENTIFIER(write); @@ -48,21 +47,17 @@ ); static PyObject * -_bufferediobase_readinto_generic(PyObject *self, PyObject *args, char readinto1) +bufferediobase_readinto(PyObject *self, PyObject *args) { Py_buffer buf; Py_ssize_t len; PyObject *data; - if (!PyArg_ParseTuple(args, - readinto1 ? "w*:readinto1" : "w*:readinto", - &buf)) { + if (!PyArg_ParseTuple(args, "w*:readinto", &buf)) { return NULL; } - data = _PyObject_CallMethodId(self, - readinto1 ? &PyId_read1 : &PyId_read, - "n", buf.len); + data = _PyObject_CallMethodId(self, &PyId_read, "n", buf.len); if (data == NULL) goto error; @@ -94,18 +89,6 @@ } static PyObject * -bufferediobase_readinto(PyObject *self, PyObject *args) -{ - return _bufferediobase_readinto_generic(self, args, 0); -} - -static PyObject * -bufferediobase_readinto1(PyObject *self, PyObject *args) -{ - return _bufferediobase_readinto_generic(self, args, 1); -} - -static PyObject * bufferediobase_unsupported(const char *message) { _PyIO_State *state = IO_STATE(); @@ -184,7 +167,6 @@ {"read", bufferediobase_read, METH_VARARGS, bufferediobase_read_doc}, {"read1", bufferediobase_read1, METH_VARARGS, bufferediobase_read1_doc}, {"readinto", bufferediobase_readinto, METH_VARARGS, NULL}, - {"readinto1", bufferediobase_readinto1, METH_VARARGS, NULL}, {"write", bufferediobase_write, METH_VARARGS, bufferediobase_write_doc}, {NULL, NULL} }; @@ -1006,7 +988,7 @@ } static PyObject * -_buffered_readinto_generic(buffered *self, PyObject *args, char readinto1) +buffered_readinto(buffered *self, PyObject *args) { Py_buffer buf; Py_ssize_t n, written = 0, remaining; @@ -1014,9 +996,7 @@ CHECK_INITIALIZED(self) - if (!PyArg_ParseTuple(args, - readinto1 ? "w*:readinto1" : "w*:readinto", - &buf)) + if (!PyArg_ParseTuple(args, "w*:readinto", &buf)) return NULL; n = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t); @@ -1054,10 +1034,7 @@ n = _bufferedreader_raw_read(self, (char *) buf.buf + written, remaining); } - - /* In readinto1 mode, we do not want to fill the internal - buffer if we already have some data to return */ - else if (!(readinto1 && written)) { + else { n = _bufferedreader_fill_buffer(self); if (n > 0) { if (n > remaining) @@ -1068,10 +1045,6 @@ continue; /* short circuit */ } } - else { - n = 0; - } - if (n == 0 || (n == -2 && written > 0)) break; if (n < 0) { @@ -1081,12 +1054,6 @@ } goto end; } - - /* At most one read in readinto1 mode */ - if (readinto1) { - written += n; - break; - } } res = PyLong_FromSsize_t(written); @@ -1098,19 +1065,6 @@ } static PyObject * -buffered_readinto(buffered *self, PyObject *args) -{ - return _buffered_readinto_generic(self, args, 0); -} - -static PyObject * -buffered_readinto1(buffered *self, PyObject *args) -{ - return _buffered_readinto_generic(self, args, 1); -} - - -static PyObject * _buffered_readline(buffered *self, Py_ssize_t limit) { PyObject *res = NULL; @@ -1795,7 +1749,6 @@ {"peek", (PyCFunction)buffered_peek, METH_VARARGS}, {"read1", (PyCFunction)buffered_read1, METH_VARARGS}, {"readinto", (PyCFunction)buffered_readinto, METH_VARARGS}, - {"readinto1", (PyCFunction)buffered_readinto1, METH_VARARGS}, {"readline", (PyCFunction)buffered_readline, METH_VARARGS}, {"seek", (PyCFunction)buffered_seek, METH_VARARGS}, {"tell", (PyCFunction)buffered_tell, METH_NOARGS}, @@ -2395,12 +2348,6 @@ } static PyObject * -bufferedrwpair_readinto1(rwpair *self, PyObject *args) -{ - return _forward_call(self->reader, &PyId_readinto1, args); -} - -static PyObject * bufferedrwpair_write(rwpair *self, PyObject *args) { return _forward_call(self->writer, &PyId_write, args); @@ -2465,7 +2412,6 @@ {"peek", (PyCFunction)bufferedrwpair_peek, METH_VARARGS}, {"read1", (PyCFunction)bufferedrwpair_read1, METH_VARARGS}, {"readinto", (PyCFunction)bufferedrwpair_readinto, METH_VARARGS}, - {"readinto1", (PyCFunction)bufferedrwpair_readinto1, METH_VARARGS}, {"write", (PyCFunction)bufferedrwpair_write, METH_VARARGS}, {"flush", (PyCFunction)bufferedrwpair_flush, METH_NOARGS}, @@ -2614,7 +2560,6 @@ {"read", (PyCFunction)buffered_read, METH_VARARGS}, {"read1", (PyCFunction)buffered_read1, METH_VARARGS}, {"readinto", (PyCFunction)buffered_readinto, METH_VARARGS}, - {"readinto1", (PyCFunction)buffered_readinto1, METH_VARARGS}, {"readline", (PyCFunction)buffered_readline, METH_VARARGS}, {"peek", (PyCFunction)buffered_peek, METH_VARARGS}, {"write", (PyCFunction)bufferedwriter_write, METH_VARARGS}, -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sun Jun 8 10:49:52 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 08 Jun 2014 10:49:52 +0200 Subject: [Python-checkins] Daily reference leaks (3f2f1ffc3ce2): sum=3 Message-ID: results for 3f2f1ffc3ce2 on branch "default" -------------------------------------------- test_functools leaked [0, 0, 3] memory blocks, sum=3 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogMeNdTY', '-x'] From python-checkins at python.org Sun Jun 8 20:58:15 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNjgy?= =?utf-8?q?=3A_Replace_EditorWindow_with_mock_to_eliminate_memory_leaks=2E?= Message-ID: <3gmn136SRrz7Ljy@mail.python.org> http://hg.python.org/cpython/rev/b8f33440cd5e changeset: 91084:b8f33440cd5e branch: 2.7 parent: 91053:86ba41b7bb46 user: Terry Jan Reedy date: Sat Jun 07 04:27:45 2014 -0400 summary: Issue #21682: Replace EditorWindow with mock to eliminate memory leaks. Patch by Saimadhav Heblikar. files: Lib/idlelib/idle_test/test_autocomplete.py | 13 +++++++-- 1 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -5,7 +5,6 @@ import idlelib.AutoComplete as ac import idlelib.AutoCompleteWindow as acw import idlelib.macosxSupport as mac -from idlelib.EditorWindow import EditorWindow from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Event @@ -13,6 +12,14 @@ def complete(): return +class DummyEditwin: + def __init__(self, root, text): + self.root = root + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + class AutoCompleteTest(unittest.TestCase): @@ -21,8 +28,8 @@ requires('gui') cls.root = Tk() mac.setupApp(cls.root, None) - cls.editor = EditorWindow(root=cls.root) - cls.text = cls.editor.text + cls.text = Text(cls.root) + cls.editor = DummyEditwin(cls.root, cls.text) @classmethod def tearDownClass(cls): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 20:58:17 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjgy?= =?utf-8?q?=3A_Replace_EditorWindow_with_mock_to_eliminate_memory_leaks=2E?= Message-ID: <3gmn151B4Sz7LkV@mail.python.org> http://hg.python.org/cpython/rev/e6cc02d32957 changeset: 91085:e6cc02d32957 branch: 3.4 parent: 91054:5a46ebfa5d90 user: Terry Jan Reedy date: Sat Jun 07 04:27:50 2014 -0400 summary: Issue #21682: Replace EditorWindow with mock to eliminate memory leaks. Patch by Saimadhav Heblikar. files: Lib/idlelib/idle_test/test_autocomplete.py | 13 +++++++-- 1 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -5,7 +5,6 @@ import idlelib.AutoComplete as ac import idlelib.AutoCompleteWindow as acw import idlelib.macosxSupport as mac -from idlelib.EditorWindow import EditorWindow from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Event @@ -13,6 +12,14 @@ def complete(): return +class DummyEditwin: + def __init__(self, root, text): + self.root = root + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + class AutoCompleteTest(unittest.TestCase): @@ -21,8 +28,8 @@ requires('gui') cls.root = Tk() mac.setupApp(cls.root, None) - cls.editor = EditorWindow(root=cls.root) - cls.text = cls.editor.text + cls.text = Text(cls.root) + cls.editor = DummyEditwin(cls.root, cls.text) @classmethod def tearDownClass(cls): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 20:58:18 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gmn163VYwz7LkX@mail.python.org> http://hg.python.org/cpython/rev/f15d48e8e89e changeset: 91086:f15d48e8e89e parent: 91055:585ad5d806bd parent: 91085:e6cc02d32957 user: Terry Jan Reedy date: Sat Jun 07 04:28:16 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/idle_test/test_autocomplete.py | 13 +++++++-- 1 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -5,7 +5,6 @@ import idlelib.AutoComplete as ac import idlelib.AutoCompleteWindow as acw import idlelib.macosxSupport as mac -from idlelib.EditorWindow import EditorWindow from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Event @@ -13,6 +12,14 @@ def complete(): return +class DummyEditwin: + def __init__(self, root, text): + self.root = root + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + class AutoCompleteTest(unittest.TestCase): @@ -21,8 +28,8 @@ requires('gui') cls.root = Tk() mac.setupApp(cls.root, None) - cls.editor = EditorWindow(root=cls.root) - cls.text = cls.editor.text + cls.text = Text(cls.root) + cls.editor = DummyEditwin(cls.root, cls.text) @classmethod def tearDownClass(cls): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 20:58:19 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMi43IC0+IDIuNyk6?= =?utf-8?q?_Issue_=2321682=3A_Replace_EditorWindow_with_mock_to_eliminate_?= =?utf-8?q?memory_leaks=2E?= Message-ID: <3gmn1759yhz7Lk9@mail.python.org> http://hg.python.org/cpython/rev/30c2f65a6346 changeset: 91087:30c2f65a6346 branch: 2.7 parent: 91081:1a9c07880a15 parent: 91084:b8f33440cd5e user: Terry Jan Reedy date: Sun Jun 08 14:47:16 2014 -0400 summary: Issue #21682: Replace EditorWindow with mock to eliminate memory leaks. Patch by Saimadhav Heblikar. (2 head merge) files: Lib/idlelib/idle_test/test_autocomplete.py | 13 +++++++-- 1 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -5,7 +5,6 @@ import idlelib.AutoComplete as ac import idlelib.AutoCompleteWindow as acw import idlelib.macosxSupport as mac -from idlelib.EditorWindow import EditorWindow from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Event @@ -13,6 +12,14 @@ def complete(): return +class DummyEditwin: + def __init__(self, root, text): + self.root = root + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + class AutoCompleteTest(unittest.TestCase): @@ -21,8 +28,8 @@ requires('gui') cls.root = Tk() mac.setupApp(cls.root, None) - cls.editor = EditorWindow(root=cls.root) - cls.text = cls.editor.text + cls.text = Text(cls.root) + cls.editor = DummyEditwin(cls.root, cls.text) @classmethod def tearDownClass(cls): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 20:58:21 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy40IC0+IDMuNCk6?= =?utf-8?q?_Issue_=2321682=3A_Replace_EditorWindow_with_mock_to_eliminate_?= =?utf-8?q?memory_leaks=2E?= Message-ID: <3gmn191Bzxz7Lk9@mail.python.org> http://hg.python.org/cpython/rev/7f14a2c10c09 changeset: 91088:7f14a2c10c09 branch: 3.4 parent: 91080:baca52bb5c74 parent: 91085:e6cc02d32957 user: Terry Jan Reedy date: Sun Jun 08 14:47:26 2014 -0400 summary: Issue #21682: Replace EditorWindow with mock to eliminate memory leaks. Patch by Saimadhav Heblikar. (2 head merge) files: Lib/idlelib/idle_test/test_autocomplete.py | 13 +++++++-- 1 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -5,7 +5,6 @@ import idlelib.AutoComplete as ac import idlelib.AutoCompleteWindow as acw import idlelib.macosxSupport as mac -from idlelib.EditorWindow import EditorWindow from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Event @@ -13,6 +12,14 @@ def complete(): return +class DummyEditwin: + def __init__(self, root, text): + self.root = root + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + class AutoCompleteTest(unittest.TestCase): @@ -21,8 +28,8 @@ requires('gui') cls.root = Tk() mac.setupApp(cls.root, None) - cls.editor = EditorWindow(root=cls.root) - cls.text = cls.editor.text + cls.text = Text(cls.root) + cls.editor = DummyEditwin(cls.root, cls.text) @classmethod def tearDownClass(cls): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 20:58:22 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:22 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?b?KTogTWVyZ2UgaGVhZHMu?= Message-ID: <3gmn1B2w9gz7Lk4@mail.python.org> http://hg.python.org/cpython/rev/10736d02797c changeset: 91089:10736d02797c parent: 91083:b1e99b4ec374 parent: 91086:f15d48e8e89e user: Terry Jan Reedy date: Sun Jun 08 14:49:07 2014 -0400 summary: Merge heads. files: Lib/idlelib/idle_test/test_autocomplete.py | 13 +++++++-- 1 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -5,7 +5,6 @@ import idlelib.AutoComplete as ac import idlelib.AutoCompleteWindow as acw import idlelib.macosxSupport as mac -from idlelib.EditorWindow import EditorWindow from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Event @@ -13,6 +12,14 @@ def complete(): return +class DummyEditwin: + def __init__(self, root, text): + self.root = root + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + class AutoCompleteTest(unittest.TestCase): @@ -21,8 +28,8 @@ requires('gui') cls.root = Tk() mac.setupApp(cls.root, None) - cls.editor = EditorWindow(root=cls.root) - cls.text = cls.editor.text + cls.text = Text(cls.root) + cls.editor = DummyEditwin(cls.root, cls.text) @classmethod def tearDownClass(cls): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 20:58:23 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gmn1C4F22z7Ll2@mail.python.org> http://hg.python.org/cpython/rev/a6546dc3e186 changeset: 91090:a6546dc3e186 parent: 91089:10736d02797c parent: 91088:7f14a2c10c09 user: Terry Jan Reedy date: Sun Jun 08 14:49:30 2014 -0400 summary: Merge with 3.4 files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 20:58:24 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:24 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_whitespace?= Message-ID: <3gmn1D5kZDz7Lkv@mail.python.org> http://hg.python.org/cpython/rev/c7375bb2e04a changeset: 91091:c7375bb2e04a branch: 2.7 parent: 91087:30c2f65a6346 user: Terry Jan Reedy date: Sun Jun 08 14:57:18 2014 -0400 summary: whitespace files: Lib/idlelib/idle_test/test_autocomplete.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -19,7 +19,7 @@ self.indentwidth = 8 self.tabwidth = 8 self.context_use_ps1 = True - + class AutoCompleteTest(unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 20:58:26 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_whitespace?= Message-ID: <3gmn1G0LR7z7LlD@mail.python.org> http://hg.python.org/cpython/rev/94c927c14379 changeset: 91092:94c927c14379 branch: 3.4 parent: 91088:7f14a2c10c09 user: Terry Jan Reedy date: Sun Jun 08 14:57:24 2014 -0400 summary: whitespace files: Lib/idlelib/idle_test/test_autocomplete.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -19,7 +19,7 @@ self.indentwidth = 8 self.tabwidth = 8 self.context_use_ps1 = True - + class AutoCompleteTest(unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 20:58:27 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 8 Jun 2014 20:58:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4_whitespace?= Message-ID: <3gmn1H20NLz7Ll8@mail.python.org> http://hg.python.org/cpython/rev/cd688d7a6c59 changeset: 91093:cd688d7a6c59 parent: 91090:a6546dc3e186 parent: 91092:94c927c14379 user: Terry Jan Reedy date: Sun Jun 08 14:57:42 2014 -0400 summary: Merge with 3.4 whitespace files: Lib/idlelib/idle_test/test_autocomplete.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -19,7 +19,7 @@ self.indentwidth = 8 self.tabwidth = 8 self.context_use_ps1 = True - + class AutoCompleteTest(unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 21:44:43 2014 From: python-checkins at python.org (zach.ware) Date: Sun, 8 Jun 2014 21:44:43 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjgz?= =?utf-8?q?=3A_Add_Tix_build_to_the_Windows_buildbot_scripts=2E?= Message-ID: <3gmp2g16fFz7Ljr@mail.python.org> http://hg.python.org/cpython/rev/31dbdd7596aa changeset: 91094:31dbdd7596aa branch: 3.4 parent: 91092:94c927c14379 user: Zachary Ware date: Sun Jun 08 14:28:13 2014 -0500 summary: Issue #21683: Add Tix build to the Windows buildbot scripts. files: Tools/buildbot/external-amd64.bat | 9 ++++++++- Tools/buildbot/external-common.bat | 4 +++- Tools/buildbot/external.bat | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Tools/buildbot/external-amd64.bat b/Tools/buildbot/external-amd64.bat --- a/Tools/buildbot/external-amd64.bat +++ b/Tools/buildbot/external-amd64.bat @@ -12,10 +12,17 @@ ) if not exist tcltk64\bin\tk86tg.dll ( - cd tk-8.6.1.0\win + cd tk-8.6.1.0\win nmake -f makefile.vc OPTS=symbols MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.6.1.0 clean nmake -f makefile.vc OPTS=symbols MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.6.1.0 all nmake -f makefile.vc OPTS=symbols MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.6.1.0 install-binaries install-libraries cd ..\.. ) +if not exist tcltk64\lib\tix8.4.3\tix84g.dll ( + cd tix-8.4.3.4\win + nmake -f python.mak DEBUG=1 MACHINE=AMD64 TCL_DIR=..\..\tcl-8.6.1.0 TK_DIR=..\..\tk-8.6.1.0 INSTALL_DIR=..\..\tcltk64 clean + nmake -f python.mak DEBUG=1 MACHINE=AMD64 TCL_DIR=..\..\tcl-8.6.1.0 TK_DIR=..\..\tk-8.6.1.0 INSTALL_DIR=..\..\tcltk64 all + nmake -f python.mak DEBUG=1 MACHINE=AMD64 TCL_DIR=..\..\tcl-8.6.1.0 TK_DIR=..\..\tk-8.6.1.0 INSTALL_DIR=..\..\tcltk64 install + cd ..\.. +) diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -9,9 +9,10 @@ @rem if exist tcltk64 rd /s/q tcltk64 @rem if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 @rem if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 + at rem if exist tix-8.4.3.4 rd /s/q tix-8.4.3.4 @rem if exist db-4.4.20 rd /s/q db-4.4.20 @rem if exist openssl-1.0.1h rd /s/q openssl-1.0.1h - at rem if exist sqlite-3.7.12 rd /s/q sqlite-3.7.12 + at rem if exist sqlite-3.7.12 rd /s/q sqlite-3.7.12 @rem bzip if not exist bzip2-1.0.6 ( @@ -31,6 +32,7 @@ svn export http://svn.python.org/projects/external/tcl-8.6.1.0 ) if not exist tk-8.6.1.0 svn export http://svn.python.org/projects/external/tk-8.6.1.0 +if not exist tix-8.4.3.4 svn export http://svn.python.org/projects/external/tix-8.4.3.4 @rem sqlite3 if not exist sqlite-3.8.3.1 ( diff --git a/Tools/buildbot/external.bat b/Tools/buildbot/external.bat --- a/Tools/buildbot/external.bat +++ b/Tools/buildbot/external.bat @@ -19,3 +19,11 @@ nmake -f makefile.vc OPTS=symbols INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.6.1.0 install-binaries install-libraries cd ..\.. ) + +if not exist tcltk\lib\tix8.4.3\tix84g.dll ( + cd tix-8.4.3.4\win + nmake -f python.mak DEBUG=1 MACHINE=IX86 TCL_DIR=..\..\tcl-8.6.1.0 TK_DIR=..\..\tk-8.6.1.0 INSTALL_DIR=..\..\tcltk clean + nmake -f python.mak DEBUG=1 MACHINE=IX86 TCL_DIR=..\..\tcl-8.6.1.0 TK_DIR=..\..\tk-8.6.1.0 INSTALL_DIR=..\..\tcltk all + nmake -f python.mak DEBUG=1 MACHINE=IX86 TCL_DIR=..\..\tcl-8.6.1.0 TK_DIR=..\..\tk-8.6.1.0 INSTALL_DIR=..\..\tcltk install + cd ..\.. +) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 21:44:44 2014 From: python-checkins at python.org (zach.ware) Date: Sun, 8 Jun 2014 21:44:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNjgz?= =?utf-8?q?=3A_Add_Tix_build_to_the_Windows_buildbot_scripts=2E?= Message-ID: <3gmp2h33TBz7Ljd@mail.python.org> http://hg.python.org/cpython/rev/8bafb707d549 changeset: 91095:8bafb707d549 branch: 2.7 parent: 91091:c7375bb2e04a user: Zachary Ware date: Sun Jun 08 14:28:13 2014 -0500 summary: Issue #21683: Add Tix build to the Windows buildbot scripts. files: Tools/buildbot/external-amd64.bat | 8 ++++++++ Tools/buildbot/external-common.bat | 2 ++ Tools/buildbot/external.bat | 8 ++++++++ 3 files changed, 18 insertions(+), 0 deletions(-) diff --git a/Tools/buildbot/external-amd64.bat b/Tools/buildbot/external-amd64.bat --- a/Tools/buildbot/external-amd64.bat +++ b/Tools/buildbot/external-amd64.bat @@ -18,3 +18,11 @@ nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.5.15.0 install cd ..\.. ) + +if not exist tcltk64\lib\tix8.4.3\tix84g.dll ( + cd tix-8.4.3.5\win + nmake -f python.mak DEBUG=1 MACHINE=AMD64 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk64 clean + nmake -f python.mak DEBUG=1 MACHINE=AMD64 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk64 all + nmake -f python.mak DEBUG=1 MACHINE=AMD64 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk64 install + cd ..\.. +) \ No newline at end of file diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -17,6 +17,7 @@ @rem if exist tk-8.4.18.1 rd /s/q tk-8.4.18.1 @rem if exist tk-8.5.2.0 rd /s/q tk-8.5.2.0 @rem if exist tk-8.5.15.0 rd /s/q tk-8.5.15.0 + at rem if exist tix-8.4.3.5 rd /s/q tix-8.4.3.5 @rem if exist db-4.4.20 rd /s/q db-4.4.20 @rem if exist db-4.7.25.0 rd /s/q db-4.7.25.0 @rem if exist openssl-0.9.8y rd /s/q openssl-0.9.8y @@ -44,6 +45,7 @@ svn export http://svn.python.org/projects/external/tcl-8.5.15.0 ) if not exist tk-8.5.15.0 svn export http://svn.python.org/projects/external/tk-8.5.15.0 +if not exist tix-8.4.3.5 svn export http://svn.python.org/projects/external/tix-8.4.3.5 @rem sqlite3 if not exist sqlite-3.6.21 ( diff --git a/Tools/buildbot/external.bat b/Tools/buildbot/external.bat --- a/Tools/buildbot/external.bat +++ b/Tools/buildbot/external.bat @@ -19,3 +19,11 @@ nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.5.15.0 install cd ..\.. ) + +if not exist tcltk\lib\tix8.4.3\tix84g.dll ( + cd tix-8.4.3.5\win + nmake -f python.mak DEBUG=1 MACHINE=IX86 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk clean + nmake -f python.mak DEBUG=1 MACHINE=IX86 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk all + nmake -f python.mak DEBUG=1 MACHINE=IX86 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk install + cd ..\.. +) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 8 21:44:45 2014 From: python-checkins at python.org (zach.ware) Date: Sun, 8 Jun 2014 21:44:45 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gmp2j4v0Xz7LkQ@mail.python.org> http://hg.python.org/cpython/rev/1a5dbc902daa changeset: 91096:1a5dbc902daa parent: 91093:cd688d7a6c59 parent: 91094:31dbdd7596aa user: Zachary Ware date: Sun Jun 08 14:43:08 2014 -0500 summary: Merge with 3.4 files: Tools/buildbot/external-common.bat | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -9,6 +9,7 @@ @rem if exist tcltk64 rd /s/q tcltk64 @rem if exist tcl-8.6.1.0 rd /s/q tcl-8.6.1.0 @rem if exist tk-8.6.1.0 rd /s/q tk-8.6.1.0 + at rem if exist tix-8.4.3.4 rd /s/q tix-8.4.3.4 @rem if exist db-4.4.20 rd /s/q db-4.4.20 @rem if exist openssl-1.0.1h rd /s/q openssl-1.0.1h @rem if exist sqlite-3.7.12 rd /s/q sqlite-3.7.12 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 00:06:02 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 9 Jun 2014 00:06:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321515=3A_Fix_typo?= =?utf-8?q?_in_a_comment=2C_thanks_Arfrever_for_the_report?= Message-ID: <3gms9k4BYwz7LjM@mail.python.org> http://hg.python.org/cpython/rev/8b93cdccd872 changeset: 91097:8b93cdccd872 user: Victor Stinner date: Mon Jun 09 00:05:47 2014 +0200 summary: Issue #21515: Fix typo in a comment, thanks Arfrever for the report files: Lib/tempfile.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/tempfile.py b/Lib/tempfile.py --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -505,7 +505,7 @@ fd = _os.open(dir, flags2, 0o600) except IsADirectoryError: # Linux kernel older than 3.11 ignores O_TMPFILE flag. - # Set flag to None to not try again. + # Set flag to False to not try again. _O_TMPFILE_WORKS = False except OSError: # The filesystem of the directory does not support O_TMPFILE. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 05:30:52 2014 From: python-checkins at python.org (nick.coghlan) Date: Mon, 9 Jun 2014 05:30:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNTY5?= =?utf-8?q?=3A_Fix_incorrect_cross_reference?= Message-ID: <3gn0NX1CBBz7LjP@mail.python.org> http://hg.python.org/cpython/rev/454d4a9a3088 changeset: 91098:454d4a9a3088 branch: 3.4 parent: 91094:31dbdd7596aa user: Nick Coghlan date: Mon Jun 09 13:14:54 2014 +1000 summary: Issue #21569: Fix incorrect cross reference files: Doc/whatsnew/2.7.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -437,7 +437,7 @@ :mod:`argparse` documentation The documentation page of the argparse module. - :ref:`argparse-from-optparse` + :ref:`upgrading-optparse-code` Part of the Python documentation, describing how to convert code that uses :mod:`optparse`. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 05:30:53 2014 From: python-checkins at python.org (nick.coghlan) Date: Mon, 9 Jun 2014 05:30:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321569=3A_merge_from_3=2E4?= Message-ID: <3gn0NY2xtGz7LkG@mail.python.org> http://hg.python.org/cpython/rev/524d73b8f29e changeset: 91099:524d73b8f29e parent: 91097:8b93cdccd872 parent: 91098:454d4a9a3088 user: Nick Coghlan date: Mon Jun 09 13:30:33 2014 +1000 summary: Issue #21569: merge from 3.4 files: Doc/whatsnew/2.7.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -437,7 +437,7 @@ :mod:`argparse` documentation The documentation page of the argparse module. - :ref:`argparse-from-optparse` + :ref:`upgrading-optparse-code` Part of the Python documentation, describing how to convert code that uses :mod:`optparse`. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 08:34:18 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 9 Jun 2014 08:34:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjc3?= =?utf-8?q?=3A_Fixed_chaining_nonnormalized_exceptions_in_io_close=28=29_m?= =?utf-8?q?ethods=2E?= Message-ID: <3gn4SB6Wn9z7Ljx@mail.python.org> http://hg.python.org/cpython/rev/a3b7b89da34f changeset: 91100:a3b7b89da34f branch: 3.4 parent: 91098:454d4a9a3088 user: Serhiy Storchaka date: Mon Jun 09 09:13:04 2014 +0300 summary: Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. files: Lib/test/test_io.py | 51 ++++++++++++++++++++++++++++ Misc/NEWS | 2 + Modules/_io/bufferedio.c | 1 + Modules/_io/textio.c | 1 + 4 files changed, 55 insertions(+), 0 deletions(-) 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 @@ -792,9 +792,27 @@ with self.assertRaises(OSError) as err: # exception not swallowed b.close() self.assertEqual(err.exception.args, ('close',)) + self.assertIsInstance(err.exception.__context__, OSError) self.assertEqual(err.exception.__context__.args, ('flush',)) self.assertFalse(b.closed) + def test_nonnormalized_close_error_on_close(self): + # Issue #21677 + raw = self.MockRawIO() + def bad_flush(): + raise non_existing_flush + def bad_close(): + raise non_existing_close + raw.close = bad_close + b = self.tp(raw) + b.flush = bad_flush + with self.assertRaises(NameError) as err: # exception not swallowed + b.close() + self.assertIn('non_existing_close', str(err.exception)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertIn('non_existing_flush', str(err.exception.__context__)) + self.assertFalse(b.closed) + def test_multi_close(self): raw = self.MockRawIO() b = self.tp(raw) @@ -2576,6 +2594,39 @@ self.assertRaises(OSError, txt.close) # exception not swallowed self.assertTrue(txt.closed) + def test_close_error_on_close(self): + buffer = self.BytesIO(self.testdata) + def bad_flush(): + raise OSError('flush') + def bad_close(): + raise OSError('close') + buffer.close = bad_close + txt = self.TextIOWrapper(buffer, encoding="ascii") + txt.flush = bad_flush + with self.assertRaises(OSError) as err: # exception not swallowed + txt.close() + self.assertEqual(err.exception.args, ('close',)) + self.assertIsInstance(err.exception.__context__, OSError) + self.assertEqual(err.exception.__context__.args, ('flush',)) + self.assertFalse(txt.closed) + + def test_nonnormalized_close_error_on_close(self): + # Issue #21677 + buffer = self.BytesIO(self.testdata) + def bad_flush(): + raise non_existing_flush + def bad_close(): + raise non_existing_close + buffer.close = bad_close + txt = self.TextIOWrapper(buffer, encoding="ascii") + txt.flush = bad_flush + with self.assertRaises(NameError) as err: # exception not swallowed + txt.close() + self.assertIn('non_existing_close', str(err.exception)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertIn('non_existing_flush', str(err.exception.__context__)) + self.assertFalse(txt.closed) + def test_multi_close(self): txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") txt.close() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,8 @@ Library ------- +- Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. + - Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a valid file. diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -549,6 +549,7 @@ } else { PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); PyErr_Fetch(&exc, &val2, &tb); diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2614,6 +2614,7 @@ } else { PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); PyErr_Fetch(&exc, &val2, &tb); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 08:34:20 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 9 Jun 2014 08:34:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321677=3A_Fixed_chaining_nonnormalized_exception?= =?utf-8?q?s_in_io_close=28=29_methods=2E?= Message-ID: <3gn4SD1ysDz7Ll2@mail.python.org> http://hg.python.org/cpython/rev/d6ac4b6020b9 changeset: 91101:d6ac4b6020b9 parent: 91099:524d73b8f29e parent: 91100:a3b7b89da34f user: Serhiy Storchaka date: Mon Jun 09 09:15:42 2014 +0300 summary: Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. files: Lib/test/test_io.py | 51 ++++++++++++++++++++++++++++ Misc/NEWS | 2 + Modules/_io/bufferedio.c | 1 + Modules/_io/textio.c | 1 + 4 files changed, 55 insertions(+), 0 deletions(-) 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 @@ -792,9 +792,27 @@ with self.assertRaises(OSError) as err: # exception not swallowed b.close() self.assertEqual(err.exception.args, ('close',)) + self.assertIsInstance(err.exception.__context__, OSError) self.assertEqual(err.exception.__context__.args, ('flush',)) self.assertFalse(b.closed) + def test_nonnormalized_close_error_on_close(self): + # Issue #21677 + raw = self.MockRawIO() + def bad_flush(): + raise non_existing_flush + def bad_close(): + raise non_existing_close + raw.close = bad_close + b = self.tp(raw) + b.flush = bad_flush + with self.assertRaises(NameError) as err: # exception not swallowed + b.close() + self.assertIn('non_existing_close', str(err.exception)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertIn('non_existing_flush', str(err.exception.__context__)) + self.assertFalse(b.closed) + def test_multi_close(self): raw = self.MockRawIO() b = self.tp(raw) @@ -2576,6 +2594,39 @@ self.assertRaises(OSError, txt.close) # exception not swallowed self.assertTrue(txt.closed) + def test_close_error_on_close(self): + buffer = self.BytesIO(self.testdata) + def bad_flush(): + raise OSError('flush') + def bad_close(): + raise OSError('close') + buffer.close = bad_close + txt = self.TextIOWrapper(buffer, encoding="ascii") + txt.flush = bad_flush + with self.assertRaises(OSError) as err: # exception not swallowed + txt.close() + self.assertEqual(err.exception.args, ('close',)) + self.assertIsInstance(err.exception.__context__, OSError) + self.assertEqual(err.exception.__context__.args, ('flush',)) + self.assertFalse(txt.closed) + + def test_nonnormalized_close_error_on_close(self): + # Issue #21677 + buffer = self.BytesIO(self.testdata) + def bad_flush(): + raise non_existing_flush + def bad_close(): + raise non_existing_close + buffer.close = bad_close + txt = self.TextIOWrapper(buffer, encoding="ascii") + txt.flush = bad_flush + with self.assertRaises(NameError) as err: # exception not swallowed + txt.close() + self.assertIn('non_existing_close', str(err.exception)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertIn('non_existing_flush', str(err.exception.__context__)) + self.assertFalse(txt.closed) + def test_multi_close(self): txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") txt.close() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,6 +92,8 @@ Library ------- +- Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. + - Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a valid file. diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -549,6 +549,7 @@ } else { PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); PyErr_Fetch(&exc, &val2, &tb); diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2620,6 +2620,7 @@ } else { PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); PyErr_Fetch(&exc, &val2, &tb); -- Repository URL: http://hg.python.org/cpython From root at python.org Mon Jun 9 09:05:23 2014 From: root at python.org (Cron Daemon) Date: Mon, 09 Jun 2014 09:05:23 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From root at python.org Mon Jun 9 09:10:24 2014 From: root at python.org (Cron Daemon) Date: Mon, 09 Jun 2014 09:10:24 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From root at python.org Mon Jun 9 09:15:22 2014 From: root at python.org (Cron Daemon) Date: Mon, 09 Jun 2014 09:15:22 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From python-checkins at python.org Mon Jun 9 10:16:18 2014 From: python-checkins at python.org (kushal.das) Date: Mon, 9 Jun 2014 10:16:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Closes_=2321256=3A_Printou?= =?utf-8?q?t_of_keyword_args_in_deterministic_order_in_mock_calls=2E?= Message-ID: <3gn6jt4bdPz7LjP@mail.python.org> http://hg.python.org/cpython/rev/8e05e15901a8 changeset: 91102:8e05e15901a8 user: Kushal Das date: Mon Jun 09 13:45:56 2014 +0530 summary: Closes #21256: Printout of keyword args in deterministic order in mock calls. Printout of keyword args should be in deterministic order in a mock function call. This will help to write better doctests. files: Lib/unittest/mock.py | 2 +- Lib/unittest/test/testmock/testmock.py | 6 ++++++ Misc/NEWS | 3 +++ 3 files changed, 10 insertions(+), 1 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1894,7 +1894,7 @@ formatted_args = '' args_string = ', '.join([repr(arg) for arg in args]) kwargs_string = ', '.join([ - '%s=%r' % (key, value) for key, value in kwargs.items() + '%s=%r' % (key, value) for key, value in sorted(kwargs.items()) ]) if args_string: formatted_args = args_string diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1206,6 +1206,12 @@ with self.assertRaises(AssertionError): m.hello.assert_not_called() + #Issue21256 printout of keyword args should be in deterministic order + def test_sorted_call_signature(self): + m = Mock() + m.hello(name='hello', daddy='hero') + text = "call(daddy='hero', name='hello')" + self.assertEquals(repr(m.hello.call_args), text) def test_mock_add_spec(self): class _One(object): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,6 +92,9 @@ Library ------- +- Issue #21256: Printout of keyword args should be in deterministic order in + a mock function call. This will help to write better doctests. + - Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. - Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Jun 9 10:35:16 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 09 Jun 2014 10:35:16 +0200 Subject: [Python-checkins] Daily reference leaks (8b93cdccd872): sum=11 Message-ID: results for 8b93cdccd872 on branch "default" -------------------------------------------- test_asyncio leaked [4, 0, 0] memory blocks, sum=4 test_collections leaked [2, 2, 0] references, sum=4 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_site leaked [0, -2, 2] references, sum=0 test_site leaked [0, -2, 2] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogsrTwXA', '-x'] From python-checkins at python.org Mon Jun 9 12:52:09 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 9 Jun 2014 12:52:09 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxMzEw?= =?utf-8?q?=3A_Fixed_possible_resource_leak_in_failed_open=28=29=2E?= Message-ID: <3gnB9j6G5Bz7LkW@mail.python.org> http://hg.python.org/cpython/rev/1e30ecbfe181 changeset: 91103:1e30ecbfe181 branch: 2.7 parent: 91095:8bafb707d549 user: Serhiy Storchaka date: Mon Jun 09 13:32:08 2014 +0300 summary: Issue #21310: Fixed possible resource leak in failed open(). files: Lib/_pyio.py | 67 ++++++++++++++++------------ Lib/test/test_io.py | 15 ++++++ Misc/NEWS | 2 + Modules/_io/_iomodule.c | 30 +++++++++--- 4 files changed, 75 insertions(+), 39 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -192,38 +192,45 @@ (appending and "a" or "") + (updating and "+" or ""), closefd) - line_buffering = False - if buffering == 1 or buffering < 0 and raw.isatty(): - buffering = -1 - line_buffering = True - if buffering < 0: - buffering = DEFAULT_BUFFER_SIZE - try: - bs = os.fstat(raw.fileno()).st_blksize - except (os.error, AttributeError): - pass + result = raw + try: + line_buffering = False + if buffering == 1 or buffering < 0 and raw.isatty(): + buffering = -1 + line_buffering = True + if buffering < 0: + buffering = DEFAULT_BUFFER_SIZE + try: + bs = os.fstat(raw.fileno()).st_blksize + except (os.error, AttributeError): + pass + else: + if bs > 1: + buffering = bs + if buffering < 0: + raise ValueError("invalid buffering size") + if buffering == 0: + if binary: + return result + raise ValueError("can't have unbuffered text I/O") + if updating: + buffer = BufferedRandom(raw, buffering) + elif writing or appending: + buffer = BufferedWriter(raw, buffering) + elif reading: + buffer = BufferedReader(raw, buffering) else: - if bs > 1: - buffering = bs - if buffering < 0: - raise ValueError("invalid buffering size") - if buffering == 0: + raise ValueError("unknown mode: %r" % mode) + result = buffer if binary: - return raw - raise ValueError("can't have unbuffered text I/O") - if updating: - buffer = BufferedRandom(raw, buffering) - elif writing or appending: - buffer = BufferedWriter(raw, buffering) - elif reading: - buffer = BufferedReader(raw, buffering) - else: - raise ValueError("unknown mode: %r" % mode) - if binary: - return buffer - text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering) - text.mode = mode - return text + return result + text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering) + result = text + text.mode = mode + return result + except: + result.close() + raise class DocDescriptor: 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 @@ -29,6 +29,7 @@ import random import unittest import weakref +import warnings import abc import signal import errno @@ -603,6 +604,20 @@ fileio.close() f2.readline() + def test_nonbuffered_textio(self): + with warnings.catch_warnings(record=True) as recorded: + with self.assertRaises(ValueError): + self.open(support.TESTFN, 'w', buffering=0) + support.gc_collect() + self.assertEqual(recorded, []) + + def test_invalid_newline(self): + with warnings.catch_warnings(record=True) as recorded: + with self.assertRaises(ValueError): + self.open(support.TESTFN, 'w', newline='invalid') + support.gc_collect() + self.assertEqual(recorded, []) + class CIOTest(IOTest): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -25,6 +25,8 @@ Library ------- +- Issue #21310: Fixed possible resource leak in failed open(). + - Issue #21304: Backport the key derivation function hashlib.pbkdf2_hmac from Python 3 per PEP 466. diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -303,7 +303,7 @@ int line_buffering; long isatty; - PyObject *raw, *modeobj = NULL, *buffer = NULL, *wrapper = NULL; + PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzzi:open", kwlist, &file, &mode, &buffering, @@ -416,6 +416,7 @@ "Osi", file, rawmode, closefd); if (raw == NULL) return NULL; + result = raw; modeobj = PyUnicode_FromString(mode); if (modeobj == NULL) @@ -474,7 +475,7 @@ } Py_DECREF(modeobj); - return raw; + return result; } /* wraps into a buffered file */ @@ -495,15 +496,16 @@ buffer = PyObject_CallFunction(Buffered_class, "Oi", raw, buffering); } - Py_CLEAR(raw); if (buffer == NULL) goto error; + result = buffer; + Py_DECREF(raw); /* if binary, returns the buffered file */ if (binary) { Py_DECREF(modeobj); - return buffer; + return result; } /* wraps into a TextIOWrapper */ @@ -512,20 +514,30 @@ buffer, encoding, errors, newline, line_buffering); - Py_CLEAR(buffer); if (wrapper == NULL) goto error; + result = wrapper; + Py_DECREF(buffer); if (PyObject_SetAttrString(wrapper, "mode", modeobj) < 0) goto error; Py_DECREF(modeobj); - return wrapper; + return result; error: - Py_XDECREF(raw); + if (result != NULL) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (PyObject_CallMethod(result, "close", NULL) != NULL) + PyErr_Restore(exc, val, tb); + else { + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + } + Py_DECREF(result); + } Py_XDECREF(modeobj); - Py_XDECREF(buffer); - Py_XDECREF(wrapper); return NULL; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 12:52:11 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 9 Jun 2014 12:52:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxMzEw?= =?utf-8?q?=3A_Fixed_possible_resource_leak_in_failed_open=28=29=2E?= Message-ID: <3gnB9l2GbRz7Lkw@mail.python.org> http://hg.python.org/cpython/rev/17e7934905ab changeset: 91104:17e7934905ab branch: 3.4 parent: 91100:a3b7b89da34f user: Serhiy Storchaka date: Mon Jun 09 13:32:34 2014 +0300 summary: Issue #21310: Fixed possible resource leak in failed open(). files: Lib/_pyio.py | 67 ++++++++++++++++------------ Lib/test/test_io.py | 14 ++++++ Misc/NEWS | 2 + Modules/_io/_iomodule.c | 36 +++++++++++--- 4 files changed, 80 insertions(+), 39 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -200,38 +200,45 @@ (appending and "a" or "") + (updating and "+" or ""), closefd, opener=opener) - line_buffering = False - if buffering == 1 or buffering < 0 and raw.isatty(): - buffering = -1 - line_buffering = True - if buffering < 0: - buffering = DEFAULT_BUFFER_SIZE - try: - bs = os.fstat(raw.fileno()).st_blksize - except (OSError, AttributeError): - pass + result = raw + try: + line_buffering = False + if buffering == 1 or buffering < 0 and raw.isatty(): + buffering = -1 + line_buffering = True + if buffering < 0: + buffering = DEFAULT_BUFFER_SIZE + try: + bs = os.fstat(raw.fileno()).st_blksize + except (OSError, AttributeError): + pass + else: + if bs > 1: + buffering = bs + if buffering < 0: + raise ValueError("invalid buffering size") + if buffering == 0: + if binary: + return result + raise ValueError("can't have unbuffered text I/O") + if updating: + buffer = BufferedRandom(raw, buffering) + elif creating or writing or appending: + buffer = BufferedWriter(raw, buffering) + elif reading: + buffer = BufferedReader(raw, buffering) else: - if bs > 1: - buffering = bs - if buffering < 0: - raise ValueError("invalid buffering size") - if buffering == 0: + raise ValueError("unknown mode: %r" % mode) + result = buffer if binary: - return raw - raise ValueError("can't have unbuffered text I/O") - if updating: - buffer = BufferedRandom(raw, buffering) - elif creating or writing or appending: - buffer = BufferedWriter(raw, buffering) - elif reading: - buffer = BufferedReader(raw, buffering) - else: - raise ValueError("unknown mode: %r" % mode) - if binary: - return buffer - text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering) - text.mode = mode - return text + return result + text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering) + result = text + text.mode = mode + return result + except: + result.close() + raise class DocDescriptor: 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 @@ -653,6 +653,20 @@ fileio.close() f2.readline() + def test_nonbuffered_textio(self): + with warnings.catch_warnings(record=True) as recorded: + with self.assertRaises(ValueError): + self.open(support.TESTFN, 'w', buffering=0) + support.gc_collect() + self.assertEqual(recorded, []) + + def test_invalid_newline(self): + with warnings.catch_warnings(record=True) as recorded: + with self.assertRaises(ValueError): + self.open(support.TESTFN, 'w', newline='invalid') + support.gc_collect() + self.assertEqual(recorded, []) + class CIOTest(IOTest): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,8 @@ Library ------- +- Issue #21310: Fixed possible resource leak in failed open(). + - Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. - Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -235,11 +235,12 @@ char rawmode[6], *m; int line_buffering, isatty; - PyObject *raw, *modeobj = NULL, *buffer = NULL, *wrapper = NULL; + PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL; _Py_IDENTIFIER(isatty); _Py_IDENTIFIER(fileno); _Py_IDENTIFIER(mode); + _Py_IDENTIFIER(close); if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziO:open", kwlist, &file, &mode, &buffering, @@ -354,6 +355,7 @@ "OsiO", file, rawmode, closefd, opener); if (raw == NULL) return NULL; + result = raw; modeobj = PyUnicode_FromString(mode); if (modeobj == NULL) @@ -412,7 +414,7 @@ } Py_DECREF(modeobj); - return raw; + return result; } /* wraps into a buffered file */ @@ -433,15 +435,16 @@ buffer = PyObject_CallFunction(Buffered_class, "Oi", raw, buffering); } - Py_CLEAR(raw); if (buffer == NULL) goto error; + result = buffer; + Py_DECREF(raw); /* if binary, returns the buffered file */ if (binary) { Py_DECREF(modeobj); - return buffer; + return result; } /* wraps into a TextIOWrapper */ @@ -450,20 +453,35 @@ buffer, encoding, errors, newline, line_buffering); - Py_CLEAR(buffer); if (wrapper == NULL) goto error; + result = wrapper; + Py_DECREF(buffer); if (_PyObject_SetAttrId(wrapper, &PyId_mode, modeobj) < 0) goto error; Py_DECREF(modeobj); - return wrapper; + return result; error: - Py_XDECREF(raw); + if (result != NULL) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (_PyObject_CallMethodId(result, &PyId_close, NULL) != NULL) + PyErr_Restore(exc, val, tb); + else { + PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); + Py_XDECREF(exc); + Py_XDECREF(tb); + PyErr_Fetch(&exc, &val2, &tb); + PyErr_NormalizeException(&exc, &val2, &tb); + PyException_SetContext(val2, val); + PyErr_Restore(exc, val2, tb); + } + Py_DECREF(result); + } Py_XDECREF(modeobj); - Py_XDECREF(buffer); - Py_XDECREF(wrapper); return NULL; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 12:52:12 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 9 Jun 2014 12:52:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321310=3A_Fixed_possible_resource_leak_in_failed?= =?utf-8?b?IG9wZW4oKS4=?= Message-ID: <3gnB9m5KtMz7LlD@mail.python.org> http://hg.python.org/cpython/rev/9c724c428e1f changeset: 91105:9c724c428e1f parent: 91102:8e05e15901a8 parent: 91104:17e7934905ab user: Serhiy Storchaka date: Mon Jun 09 13:35:43 2014 +0300 summary: Issue #21310: Fixed possible resource leak in failed open(). files: Lib/_pyio.py | 67 ++++++++++++++++------------ Lib/test/test_io.py | 14 ++++++ Misc/NEWS | 2 + Modules/_io/_iomodule.c | 36 +++++++++++--- 4 files changed, 80 insertions(+), 39 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -200,38 +200,45 @@ (appending and "a" or "") + (updating and "+" or ""), closefd, opener=opener) - line_buffering = False - if buffering == 1 or buffering < 0 and raw.isatty(): - buffering = -1 - line_buffering = True - if buffering < 0: - buffering = DEFAULT_BUFFER_SIZE - try: - bs = os.fstat(raw.fileno()).st_blksize - except (OSError, AttributeError): - pass + result = raw + try: + line_buffering = False + if buffering == 1 or buffering < 0 and raw.isatty(): + buffering = -1 + line_buffering = True + if buffering < 0: + buffering = DEFAULT_BUFFER_SIZE + try: + bs = os.fstat(raw.fileno()).st_blksize + except (OSError, AttributeError): + pass + else: + if bs > 1: + buffering = bs + if buffering < 0: + raise ValueError("invalid buffering size") + if buffering == 0: + if binary: + return result + raise ValueError("can't have unbuffered text I/O") + if updating: + buffer = BufferedRandom(raw, buffering) + elif creating or writing or appending: + buffer = BufferedWriter(raw, buffering) + elif reading: + buffer = BufferedReader(raw, buffering) else: - if bs > 1: - buffering = bs - if buffering < 0: - raise ValueError("invalid buffering size") - if buffering == 0: + raise ValueError("unknown mode: %r" % mode) + result = buffer if binary: - return raw - raise ValueError("can't have unbuffered text I/O") - if updating: - buffer = BufferedRandom(raw, buffering) - elif creating or writing or appending: - buffer = BufferedWriter(raw, buffering) - elif reading: - buffer = BufferedReader(raw, buffering) - else: - raise ValueError("unknown mode: %r" % mode) - if binary: - return buffer - text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering) - text.mode = mode - return text + return result + text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering) + result = text + text.mode = mode + return result + except: + result.close() + raise class DocDescriptor: 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 @@ -653,6 +653,20 @@ fileio.close() f2.readline() + def test_nonbuffered_textio(self): + with warnings.catch_warnings(record=True) as recorded: + with self.assertRaises(ValueError): + self.open(support.TESTFN, 'w', buffering=0) + support.gc_collect() + self.assertEqual(recorded, []) + + def test_invalid_newline(self): + with warnings.catch_warnings(record=True) as recorded: + with self.assertRaises(ValueError): + self.open(support.TESTFN, 'w', newline='invalid') + support.gc_collect() + self.assertEqual(recorded, []) + class CIOTest(IOTest): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,6 +92,8 @@ Library ------- +- Issue #21310: Fixed possible resource leak in failed open(). + - Issue #21256: Printout of keyword args should be in deterministic order in a mock function call. This will help to write better doctests. diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -235,11 +235,12 @@ char rawmode[6], *m; int line_buffering, isatty; - PyObject *raw, *modeobj = NULL, *buffer = NULL, *wrapper = NULL; + PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL; _Py_IDENTIFIER(isatty); _Py_IDENTIFIER(fileno); _Py_IDENTIFIER(mode); + _Py_IDENTIFIER(close); if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziO:open", kwlist, &file, &mode, &buffering, @@ -354,6 +355,7 @@ "OsiO", file, rawmode, closefd, opener); if (raw == NULL) return NULL; + result = raw; modeobj = PyUnicode_FromString(mode); if (modeobj == NULL) @@ -412,7 +414,7 @@ } Py_DECREF(modeobj); - return raw; + return result; } /* wraps into a buffered file */ @@ -433,15 +435,16 @@ buffer = PyObject_CallFunction(Buffered_class, "Oi", raw, buffering); } - Py_CLEAR(raw); if (buffer == NULL) goto error; + result = buffer; + Py_DECREF(raw); /* if binary, returns the buffered file */ if (binary) { Py_DECREF(modeobj); - return buffer; + return result; } /* wraps into a TextIOWrapper */ @@ -450,20 +453,35 @@ buffer, encoding, errors, newline, line_buffering); - Py_CLEAR(buffer); if (wrapper == NULL) goto error; + result = wrapper; + Py_DECREF(buffer); if (_PyObject_SetAttrId(wrapper, &PyId_mode, modeobj) < 0) goto error; Py_DECREF(modeobj); - return wrapper; + return result; error: - Py_XDECREF(raw); + if (result != NULL) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (_PyObject_CallMethodId(result, &PyId_close, NULL) != NULL) + PyErr_Restore(exc, val, tb); + else { + PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); + Py_XDECREF(exc); + Py_XDECREF(tb); + PyErr_Fetch(&exc, &val2, &tb); + PyErr_NormalizeException(&exc, &val2, &tb); + PyException_SetContext(val2, val); + PyErr_Restore(exc, val2, tb); + } + Py_DECREF(result); + } Py_XDECREF(modeobj); - Py_XDECREF(buffer); - Py_XDECREF(wrapper); return NULL; } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 22:41:44 2014 From: python-checkins at python.org (r.david.murray) Date: Mon, 9 Jun 2014 22:41:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzIwOTAzOiBjbGFy?= =?utf-8?q?ify_what_happens_when_an_smtp_connection_timeout_occurs=2E?= Message-ID: <3gnRG02Fbgz7Lkk@mail.python.org> http://hg.python.org/cpython/rev/6cd64ef6fc95 changeset: 91106:6cd64ef6fc95 branch: 2.7 parent: 91103:1e30ecbfe181 user: R David Murray date: Mon Jun 09 16:40:47 2014 -0400 summary: #20903: clarify what happens when an smtp connection timeout occurs. Patch by Milan Oberkirch. files: Doc/library/smtplib.rst | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -32,7 +32,8 @@ than a success code, an :exc:`SMTPConnectError` is raised. The optional *timeout* parameter specifies a timeout in seconds for blocking operations like the connection attempt (if not specified, the global default timeout - setting will be used). + setting will be used). If the timeout expires, :exc:`socket.timeout` + is raised. For normal use, you should only require the initialization/connect, :meth:`sendmail`, and :meth:`~smtplib.quit` methods. @@ -54,7 +55,8 @@ formatted private key and certificate chain file for the SSL connection. The optional *timeout* parameter specifies a timeout in seconds for blocking operations like the connection attempt (if not specified, the global default - timeout setting will be used). + timeout setting will be used). If the timeout expires, :exc:`socket.timeout` + is raised. .. versionadded:: 2.6 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 22:41:45 2014 From: python-checkins at python.org (r.david.murray) Date: Mon, 9 Jun 2014 22:41:45 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogIzIwOTAzOiBjbGFy?= =?utf-8?q?ify_what_happens_when_an_smtp_connection_timeout_occurs=2E?= Message-ID: <3gnRG142lMz7Lln@mail.python.org> http://hg.python.org/cpython/rev/ca88bcfa5c15 changeset: 91107:ca88bcfa5c15 branch: 3.4 parent: 91104:17e7934905ab user: R David Murray date: Mon Jun 09 16:41:06 2014 -0400 summary: #20903: clarify what happens when an smtp connection timeout occurs. Patch by Milan Oberkirch. files: Doc/library/smtplib.rst | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -32,7 +32,8 @@ than a success code, an :exc:`SMTPConnectError` is raised. The optional *timeout* parameter specifies a timeout in seconds for blocking operations like the connection attempt (if not specified, the global default timeout - setting will be used). The optional source_address parameter allows to bind + setting will be used). If the timeout expires, :exc:`socket.timeout` is + raised. The optional source_address parameter allows to bind to some specific source address in a machine with multiple network interfaces, and/or to some specific source TCP port. It takes a 2-tuple (host, port), for the socket to bind to as its source address before -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 9 22:41:46 2014 From: python-checkins at python.org (r.david.murray) Date: Mon, 9 Jun 2014 22:41:46 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge=3A_=2320903=3A_clarify_what_happens_when_an_smtp_c?= =?utf-8?q?onnection_timeout_occurs=2E?= Message-ID: <3gnRG25snPz7Lm2@mail.python.org> http://hg.python.org/cpython/rev/20225460ae0f changeset: 91108:20225460ae0f parent: 91105:9c724c428e1f parent: 91107:ca88bcfa5c15 user: R David Murray date: Mon Jun 09 16:41:27 2014 -0400 summary: Merge: #20903: clarify what happens when an smtp connection timeout occurs. files: Doc/library/smtplib.rst | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -32,7 +32,8 @@ than a success code, an :exc:`SMTPConnectError` is raised. The optional *timeout* parameter specifies a timeout in seconds for blocking operations like the connection attempt (if not specified, the global default timeout - setting will be used). The optional source_address parameter allows to bind + setting will be used). If the timeout expires, :exc:`socket.timeout` is + raised. The optional source_address parameter allows to bind to some specific source address in a machine with multiple network interfaces, and/or to some specific source TCP port. It takes a 2-tuple (host, port), for the socket to bind to as its source address before -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 02:02:39 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 10 Jun 2014 02:02:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Closes_Issue_2?= =?utf-8?q?1659=3A_Improve_Idle_calltips_for_*args=2C_**kwargs_in_2=2E7=2C?= =?utf-8?q?_where?= Message-ID: <3gnWjq2SR1z7Lk6@mail.python.org> http://hg.python.org/cpython/rev/0b181c02df7c changeset: 91109:0b181c02df7c branch: 2.7 parent: 91106:6cd64ef6fc95 user: Terry Jan Reedy date: Mon Jun 09 20:02:18 2014 -0400 summary: Closes Issue 21659: Improve Idle calltips for *args, **kwargs in 2.7, where actual names are not available. Initial patch by Serhiy Storchaka. files: Lib/idlelib/CallTips.py | 14 ++++++++--- Lib/idlelib/idle_test/test_calltips.py | 16 +++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -183,10 +183,16 @@ defaults = list(map(lambda name: "=%s" % repr(name), defaults)) defaults = [""] * (len(real_args) - len(defaults)) + defaults items = map(lambda arg, dflt: arg + dflt, real_args, defaults) - if fob.func_code.co_flags & 0x4: - items.append("*args") - if fob.func_code.co_flags & 0x8: - items.append("**kwds") + for flag, pre, name in ((0x4, '*', 'args'), (0x8, '**', 'kwargs')): + if fob.func_code.co_flags & flag: + pre_name = pre + name + if name not in real_args: + items.append(pre_name) + else: + i = 1 + while ((name+'%s') % i) in real_args: + i += 1 + items.append((pre_name+'%s') % i) argspec = ", ".join(items) argspec = "(%s)" % re.sub("(?", argspec) diff --git a/Lib/idlelib/idle_test/test_calltips.py b/Lib/idlelib/idle_test/test_calltips.py --- a/Lib/idlelib/idle_test/test_calltips.py +++ b/Lib/idlelib/idle_test/test_calltips.py @@ -22,7 +22,7 @@ def t4(self, *args): 'doc' t4.tip = "(self, *args)" def t5(self, ai, b=None, *args, **kw): 'doc' - t5.tip = "(self, ai, b=None, *args, **kwds)" + t5.tip = "(self, ai, b=None, *args, **kwargs)" def t6(no, self): 'doc' t6.tip = "(no, self)" def __call__(self, ci): 'doc' @@ -104,7 +104,7 @@ def t4(*args): 'doc' t4.tip = "(*args)" def t5(a, b=None, *args, **kwds): 'doc' - t5.tip = "(a, b=None, *args, **kwds)" + t5.tip = "(a, b=None, *args, **kwargs)" for func in (t1, t2, t3, t4, t5, TC): self.assertEqual(signature(func), func.tip + '\ndoc') @@ -126,10 +126,16 @@ class C: def m1(*args): pass def m2(**kwds): pass + def f1(args, kwargs, *a, **k): pass + def f2(args, kwargs, args1, kwargs1, *a, **k): pass c = C() - for meth, mtip in ((C.m1, '(*args)'), (c.m1, "(*args)"), - (C.m2, "(**kwds)"), (c.m2, "(**kwds)"),): - self.assertEqual(signature(meth), mtip) + self.assertEqual(signature(C.m1), '(*args)') + self.assertEqual(signature(c.m1), '(*args)') + self.assertEqual(signature(C.m2), '(**kwargs)') + self.assertEqual(signature(c.m2), '(**kwargs)') + self.assertEqual(signature(f1), '(args, kwargs, *args1, **kwargs1)') + self.assertEqual(signature(f2), + '(args, kwargs, args1, kwargs1, *args2, **kwargs2)') def test_no_docstring(self): def nd(s): pass -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Tue Jun 10 06:23:21 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 10 Jun 2014 06:23:21 +0200 Subject: [Python-checkins] Daily reference leaks (20225460ae0f): sum=1 Message-ID: results for 20225460ae0f on branch "default" -------------------------------------------- test_collections leaked [-2, 0, 0] references, sum=-2 test_functools leaked [0, 0, 3] memory blocks, sum=3 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogIRk58c', '-x'] From python-checkins at python.org Tue Jun 10 08:50:17 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 10 Jun 2014 08:50:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNjk1?= =?utf-8?q?=3A_Catch_AttributeError_created_when_user_closes_grep_output_w?= =?utf-8?q?indow?= Message-ID: <3gnhm957Flz7Ljg@mail.python.org> http://hg.python.org/cpython/rev/ec91ee7d9d8d changeset: 91110:ec91ee7d9d8d branch: 2.7 user: Terry Jan Reedy date: Tue Jun 10 02:49:29 2014 -0400 summary: Issue #21695: Catch AttributeError created when user closes grep output window while still being written to. With no console, this closed Idle. Also add missing import and a few other changes. files: Lib/idlelib/GrepDialog.py | 54 +++++++++++++++----------- 1 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Lib/idlelib/GrepDialog.py b/Lib/idlelib/GrepDialog.py --- a/Lib/idlelib/GrepDialog.py +++ b/Lib/idlelib/GrepDialog.py @@ -1,9 +1,14 @@ import os import fnmatch +import re # for htest import sys -from Tkinter import * +from Tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog +from Tkinter import Tk, Text, Button, SEL, END # for htest from idlelib import SearchEngine +import itertools from idlelib.SearchDialogBase import SearchDialogBase +# Importing OutputWindow fails due to import loop +# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow def grep(text, io=None, flist=None): root = text._root() @@ -63,7 +68,7 @@ if not path: self.top.bell() return - from idlelib.OutputWindow import OutputWindow + from idlelib.OutputWindow import OutputWindow # leave here! save = sys.stdout try: sys.stdout = OutputWindow(self.flist) @@ -77,29 +82,34 @@ list.sort() self.close() pat = self.engine.getpat() - print "Searching %r in %s ..." % (pat, path) + print("Searching %r in %s ..." % (pat, path)) hits = 0 - for fn in list: - try: - with open(fn) as f: - for lineno, line in enumerate(f, 1): - if line[-1:] == '\n': - line = line[:-1] - if prog.search(line): - sys.stdout.write("%s: %s: %s\n" % - (fn, lineno, line)) - hits += 1 - except IOError as msg: - print msg - print(("Hits found: %s\n" - "(Hint: right-click to open locations.)" - % hits) if hits else "No hits.") + try: + for fn in list: + try: + with open(fn) as f: + for lineno, line in enumerate(f, 1): + if line[-1:] == '\n': + line = line[:-1] + if prog.search(line): + sys.stdout.write("%s: %s: %s\n" % + (fn, lineno, line)) + hits += 1 + except IOError as msg: + print(msg) + print(("Hits found: %s\n" + "(Hint: right-click to open locations.)" + % hits) if hits else "No hits.") + except AttributeError: + # Tk window has been closed, OutputWindow.text = None, + # so in OW.write, OW.text.insert fails. + pass def findfiles(self, dir, base, rec): try: names = os.listdir(dir or os.curdir) except os.error as msg: - print msg + print(msg) return [] list = [] subdirs = [] @@ -120,7 +130,8 @@ self.top.grab_release() self.top.withdraw() -def _grep_dialog(parent): + +def _grep_dialog(parent): # for htest from idlelib.PyShell import PyShellFileList root = Tk() root.title("Test GrepDialog") @@ -140,10 +151,7 @@ button.pack() root.mainloop() - if __name__ == "__main__": - # A human test is a bit tricky since EditorWindow() imports this module. - # Hence Idle must be restarted after editing this file for a live test. import unittest unittest.main('idlelib.idle_test.test_grep', verbosity=2, exit=False) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 08:50:19 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 10 Jun 2014 08:50:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjk1?= =?utf-8?q?=3A_Catch_AttributeError_created_when_user_closes_grep_output_w?= =?utf-8?q?indow?= Message-ID: <3gnhmC0pXvz7Lm2@mail.python.org> http://hg.python.org/cpython/rev/d9c1f36494b6 changeset: 91111:d9c1f36494b6 branch: 3.4 parent: 91107:ca88bcfa5c15 user: Terry Jan Reedy date: Tue Jun 10 02:49:35 2014 -0400 summary: Issue #21695: Catch AttributeError created when user closes grep output window while still being written to. With no console, this closed Idle. Also add missing import and a few other changes. files: Lib/idlelib/GrepDialog.py | 49 +++++++++++++++----------- 1 files changed, 29 insertions(+), 20 deletions(-) diff --git a/Lib/idlelib/GrepDialog.py b/Lib/idlelib/GrepDialog.py --- a/Lib/idlelib/GrepDialog.py +++ b/Lib/idlelib/GrepDialog.py @@ -1,9 +1,14 @@ import os import fnmatch +import re # for htest import sys -from tkinter import * +from tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog +from tkinter import Tk, Text, Button, SEL, END # for htest from idlelib import SearchEngine +import itertools from idlelib.SearchDialogBase import SearchDialogBase +# Importing OutputWindow fails due to import loop +# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow def grep(text, io=None, flist=None): root = text._root() @@ -63,7 +68,7 @@ if not path: self.top.bell() return - from idlelib.OutputWindow import OutputWindow + from idlelib.OutputWindow import OutputWindow # leave here! save = sys.stdout try: sys.stdout = OutputWindow(self.flist) @@ -79,21 +84,26 @@ pat = self.engine.getpat() print("Searching %r in %s ..." % (pat, path)) hits = 0 - for fn in list: - try: - with open(fn, errors='replace') as f: - for lineno, line in enumerate(f, 1): - if line[-1:] == '\n': - line = line[:-1] - if prog.search(line): - sys.stdout.write("%s: %s: %s\n" % - (fn, lineno, line)) - hits += 1 - except OSError as msg: - print(msg) - print(("Hits found: %s\n" - "(Hint: right-click to open locations.)" - % hits) if hits else "No hits.") + try: + for fn in list: + try: + with open(fn, errors='replace') as f: + for lineno, line in enumerate(f, 1): + if line[-1:] == '\n': + line = line[:-1] + if prog.search(line): + sys.stdout.write("%s: %s: %s\n" % + (fn, lineno, line)) + hits += 1 + except OSError as msg: + print(msg) + print(("Hits found: %s\n" + "(Hint: right-click to open locations.)" + % hits) if hits else "No hits.") + except AttributeError: + # Tk window has been closed, OutputWindow.text = None, + # so in OW.write, OW.text.insert fails. + pass def findfiles(self, dir, base, rec): try: @@ -120,7 +130,8 @@ self.top.grab_release() self.top.withdraw() -def _grep_dialog(parent): + +def _grep_dialog(parent): # for htest from idlelib.PyShell import PyShellFileList root = Tk() root.title("Test GrepDialog") @@ -141,8 +152,6 @@ root.mainloop() if __name__ == "__main__": - # A human test is a bit tricky since EditorWindow() imports this module. - # Hence Idle must be restarted after editing this file for a live test. import unittest unittest.main('idlelib.idle_test.test_grep', verbosity=2, exit=False) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 08:50:20 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 10 Jun 2014 08:50:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gnhmD2XrJz7LmH@mail.python.org> http://hg.python.org/cpython/rev/4103556acb4e changeset: 91112:4103556acb4e parent: 91108:20225460ae0f parent: 91111:d9c1f36494b6 user: Terry Jan Reedy date: Tue Jun 10 02:49:54 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/GrepDialog.py | 49 +++++++++++++++----------- 1 files changed, 29 insertions(+), 20 deletions(-) diff --git a/Lib/idlelib/GrepDialog.py b/Lib/idlelib/GrepDialog.py --- a/Lib/idlelib/GrepDialog.py +++ b/Lib/idlelib/GrepDialog.py @@ -1,9 +1,14 @@ import os import fnmatch +import re # for htest import sys -from tkinter import * +from tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog +from tkinter import Tk, Text, Button, SEL, END # for htest from idlelib import SearchEngine +import itertools from idlelib.SearchDialogBase import SearchDialogBase +# Importing OutputWindow fails due to import loop +# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow def grep(text, io=None, flist=None): root = text._root() @@ -63,7 +68,7 @@ if not path: self.top.bell() return - from idlelib.OutputWindow import OutputWindow + from idlelib.OutputWindow import OutputWindow # leave here! save = sys.stdout try: sys.stdout = OutputWindow(self.flist) @@ -79,21 +84,26 @@ pat = self.engine.getpat() print("Searching %r in %s ..." % (pat, path)) hits = 0 - for fn in list: - try: - with open(fn, errors='replace') as f: - for lineno, line in enumerate(f, 1): - if line[-1:] == '\n': - line = line[:-1] - if prog.search(line): - sys.stdout.write("%s: %s: %s\n" % - (fn, lineno, line)) - hits += 1 - except OSError as msg: - print(msg) - print(("Hits found: %s\n" - "(Hint: right-click to open locations.)" - % hits) if hits else "No hits.") + try: + for fn in list: + try: + with open(fn, errors='replace') as f: + for lineno, line in enumerate(f, 1): + if line[-1:] == '\n': + line = line[:-1] + if prog.search(line): + sys.stdout.write("%s: %s: %s\n" % + (fn, lineno, line)) + hits += 1 + except OSError as msg: + print(msg) + print(("Hits found: %s\n" + "(Hint: right-click to open locations.)" + % hits) if hits else "No hits.") + except AttributeError: + # Tk window has been closed, OutputWindow.text = None, + # so in OW.write, OW.text.insert fails. + pass def findfiles(self, dir, base, rec): try: @@ -120,7 +130,8 @@ self.top.grab_release() self.top.withdraw() -def _grep_dialog(parent): + +def _grep_dialog(parent): # for htest from idlelib.PyShell import PyShellFileList root = Tk() root.title("Test GrepDialog") @@ -141,8 +152,6 @@ root.mainloop() if __name__ == "__main__": - # A human test is a bit tricky since EditorWindow() imports this module. - # Hence Idle must be restarted after editing this file for a live test. import unittest unittest.main('idlelib.idle_test.test_grep', verbosity=2, exit=False) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 09:21:18 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Jun 2014 09:21:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzAw?= =?utf-8?q?=3A_Fix_asyncio_doc=2C_add_DatagramProtocol?= Message-ID: <3gnjRy5Th8z7LpT@mail.python.org> http://hg.python.org/cpython/rev/79562a31e5a6 changeset: 91113:79562a31e5a6 branch: 3.4 parent: 91111:d9c1f36494b6 user: Victor Stinner date: Tue Jun 10 09:19:26 2014 +0200 summary: Issue #21700: Fix asyncio doc, add DatagramProtocol files: Doc/library/asyncio-protocol.rst | 12 ++++++------ 1 files changed, 6 insertions(+), 6 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 @@ -272,8 +272,8 @@ Connection callbacks -------------------- -These callbacks may be called on :class:`Protocol` and -:class:`SubprocessProtocol` instances: +These callbacks may be called on :class:`Protocol`, :class:`DatagramProtocol` +and :class:`SubprocessProtocol` instances: .. method:: BaseProtocol.connection_made(transport) @@ -291,10 +291,10 @@ The latter means a regular EOF is received, or the connection was aborted or closed by this side of the connection. -:meth:`connection_made` and :meth:`connection_lost` are called exactly once -per successful connection. All other callbacks will be called between those -two methods, which allows for easier resource management in your protocol -implementation. +:meth:`~BaseProtocol.connection_made` and :meth:`~BaseProtocol.connection_lost` +are called exactly once per successful connection. All other callbacks will be +called between those two methods, which allows for easier resource management +in your protocol implementation. The following callbacks may be called only on :class:`SubprocessProtocol` instances: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 09:21:20 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Jun 2014 09:21:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321700=3A_Fix_asyncio_doc=2C_a?= =?utf-8?q?dd_DatagramProtocol?= Message-ID: <3gnjS001Qyz7Lpb@mail.python.org> http://hg.python.org/cpython/rev/a8dfdae4c4a0 changeset: 91114:a8dfdae4c4a0 parent: 91112:4103556acb4e parent: 91113:79562a31e5a6 user: Victor Stinner date: Tue Jun 10 09:21:07 2014 +0200 summary: (Merge 3.4) Issue #21700: Fix asyncio doc, add DatagramProtocol files: Doc/library/asyncio-protocol.rst | 12 ++++++------ 1 files changed, 6 insertions(+), 6 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 @@ -272,8 +272,8 @@ Connection callbacks -------------------- -These callbacks may be called on :class:`Protocol` and -:class:`SubprocessProtocol` instances: +These callbacks may be called on :class:`Protocol`, :class:`DatagramProtocol` +and :class:`SubprocessProtocol` instances: .. method:: BaseProtocol.connection_made(transport) @@ -291,10 +291,10 @@ The latter means a regular EOF is received, or the connection was aborted or closed by this side of the connection. -:meth:`connection_made` and :meth:`connection_lost` are called exactly once -per successful connection. All other callbacks will be called between those -two methods, which allows for easier resource management in your protocol -implementation. +:meth:`~BaseProtocol.connection_made` and :meth:`~BaseProtocol.connection_lost` +are called exactly once per successful connection. All other callbacks will be +called between those two methods, which allows for easier resource management +in your protocol implementation. The following callbacks may be called only on :class:`SubprocessProtocol` instances: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 10:27:24 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Jun 2014 10:27:24 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxMzI2?= =?utf-8?q?=3A_Add_a_new_is=5Fclosed=28=29_method_to_asyncio=2EBaseEventLo?= =?utf-8?q?op?= Message-ID: <3gnkwD5Y0xz7LlY@mail.python.org> http://hg.python.org/cpython/rev/7912179335cc changeset: 91115:7912179335cc branch: 3.4 parent: 91113:79562a31e5a6 user: Victor Stinner date: Tue Jun 10 10:23:10 2014 +0200 summary: Issue #21326: Add a new is_closed() method to asyncio.BaseEventLoop Add BaseEventLoop._closed attribute and use it to check if the event loop was closed or not, instead of checking different attributes in each subclass of BaseEventLoop. run_forever() and run_until_complete() methods now raise a RuntimeError('Event loop is closed') exception if the event loop was closed. BaseProactorEventLoop.close() now also cancels "accept futures". files: Doc/library/asyncio-eventloop.rst | 6 ++ Lib/asyncio/base_events.py | 19 ++++++++ Lib/asyncio/proactor_events.py | 23 ++++++--- Lib/asyncio/selector_events.py | 16 +++--- Lib/test/test_asyncio/test_base_events.py | 14 ++++++ Lib/test/test_asyncio/test_selector_events.py | 17 ++++++- Misc/NEWS | 4 + 7 files changed, 80 insertions(+), 19 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 @@ -119,6 +119,12 @@ Callback scheduled after :meth:`stop` is called won't. However, those callbacks will run if :meth:`run_forever` is called again later. +.. method:: BaseEventLoop.is_closed() + + Returns ``True`` if the event loop was closed. + + .. versionadded:: 3.4.2 + .. method:: BaseEventLoop.close() Close the event loop. The loop should not be running. 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 @@ -119,6 +119,7 @@ class BaseEventLoop(events.AbstractEventLoop): def __init__(self): + self._closed = False self._ready = collections.deque() self._scheduled = [] self._default_executor = None @@ -128,6 +129,11 @@ self._exception_handler = None self._debug = False + def __repr__(self): + return ('<%s running=%s closed=%s debug=%s>' + % (self.__class__.__name__, self.is_running(), + self.is_closed(), self.get_debug())) + def _make_socket_transport(self, sock, protocol, waiter=None, *, extra=None, server=None): """Create socket transport.""" @@ -173,8 +179,13 @@ """Process selector events.""" raise NotImplementedError + def _check_closed(self): + if self._closed: + raise RuntimeError('Event loop is closed') + def run_forever(self): """Run until stop() is called.""" + self._check_closed() if self._running: raise RuntimeError('Event loop is running.') self._running = True @@ -198,6 +209,7 @@ Return the Future's result, or raise its exception. """ + self._check_closed() future = tasks.async(future, loop=self) future.add_done_callback(_raise_stop_error) self.run_forever() @@ -222,6 +234,9 @@ This clears the queues and shuts down the executor, but does not wait for the executor to finish. """ + if self._closed: + return + self._closed = True self._ready.clear() self._scheduled.clear() executor = self._default_executor @@ -229,6 +244,10 @@ self._default_executor = None executor.shutdown(wait=False) + def is_closed(self): + """Returns True if the event loop was closed.""" + return self._closed + def is_running(self): """Returns running status of event loop.""" return self._running 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 @@ -353,13 +353,14 @@ sock, protocol, waiter, extra) def close(self): - if self._proactor is not None: - self._close_self_pipe() - self._proactor.close() - self._proactor = None - self._selector = None - super().close() - self._accept_futures.clear() + if self.is_closed(): + return + self._stop_accept_futures() + self._close_self_pipe() + self._proactor.close() + self._proactor = None + self._selector = None + super().close() def sock_recv(self, sock, n): return self._proactor.recv(sock, n) @@ -428,6 +429,8 @@ self._make_socket_transport( conn, protocol, extra={'peername': addr}, server=server) + if self.is_closed(): + return f = self._proactor.accept(sock) except OSError as exc: if sock.fileno() != -1: @@ -448,8 +451,12 @@ def _process_events(self, event_list): pass # XXX hard work currently done in poll - def _stop_serving(self, sock): + def _stop_accept_futures(self): for future in self._accept_futures.values(): future.cancel() + self._accept_futures.clear() + + def _stop_serving(self, sock): + self._stop_accept_futures() self._proactor._stop_serving(sock) sock.close() 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 @@ -55,11 +55,13 @@ return _SelectorDatagramTransport(self, sock, protocol, address, extra) def close(self): + if self.is_closed(): + return + self._close_self_pipe() if self._selector is not None: - self._close_self_pipe() self._selector.close() self._selector = None - super().close() + super().close() def _socketpair(self): raise NotImplementedError @@ -143,8 +145,7 @@ def add_reader(self, fd, callback, *args): """Add a reader callback.""" - if self._selector is None: - raise RuntimeError('Event loop is closed') + self._check_closed() handle = events.Handle(callback, args, self) try: key = self._selector.get_key(fd) @@ -160,7 +161,7 @@ def remove_reader(self, fd): """Remove a reader callback.""" - if self._selector is None: + if self.is_closed(): return False try: key = self._selector.get_key(fd) @@ -182,8 +183,7 @@ def add_writer(self, fd, callback, *args): """Add a writer callback..""" - if self._selector is None: - raise RuntimeError('Event loop is closed') + self._check_closed() handle = events.Handle(callback, args, self) try: key = self._selector.get_key(fd) @@ -199,7 +199,7 @@ def remove_writer(self, fd): """Remove a writer callback.""" - if self._selector is None: + if self.is_closed(): return False try: key = self._selector.get_key(fd) 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 @@ -52,6 +52,20 @@ gen = self.loop._make_subprocess_transport(m, m, m, m, m, m, m) self.assertRaises(NotImplementedError, next, iter(gen)) + def test_close(self): + self.assertFalse(self.loop.is_closed()) + self.loop.close() + self.assertTrue(self.loop.is_closed()) + + # it should be possible to call close() more than once + self.loop.close() + self.loop.close() + + # operation blocked when the loop is closed + f = asyncio.Future(loop=self.loop) + self.assertRaises(RuntimeError, self.loop.run_forever) + self.assertRaises(RuntimeError, self.loop.run_until_complete, f) + def test__add_callback_handle(self): h = asyncio.Handle(lambda: False, (), self.loop) 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 @@ -80,7 +80,10 @@ self.loop._selector.close() self.loop._selector = selector = mock.Mock() + self.assertFalse(self.loop.is_closed()) + self.loop.close() + self.assertTrue(self.loop.is_closed()) self.assertIsNone(self.loop._selector) self.assertIsNone(self.loop._csock) self.assertIsNone(self.loop._ssock) @@ -89,9 +92,20 @@ csock.close.assert_called_with() remove_reader.assert_called_with(7) + # it should be possible to call close() more than once self.loop.close() self.loop.close() + # operation blocked when the loop is closed + f = asyncio.Future(loop=self.loop) + self.assertRaises(RuntimeError, self.loop.run_forever) + self.assertRaises(RuntimeError, self.loop.run_until_complete, f) + fd = 0 + def callback(): + pass + self.assertRaises(RuntimeError, self.loop.add_reader, fd, callback) + self.assertRaises(RuntimeError, self.loop.add_writer, fd, callback) + def test_close_no_selector(self): ssock = self.loop._ssock csock = self.loop._csock @@ -101,9 +115,6 @@ self.loop._selector = None self.loop.close() self.assertIsNone(self.loop._selector) - self.assertFalse(ssock.close.called) - self.assertFalse(csock.close.called) - self.assertFalse(remove_reader.called) def test_socketpair(self): self.assertRaises(NotImplementedError, self.loop._socketpair) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,10 @@ Library ------- +- Issue #21326: Add a new is_closed() method to asyncio.BaseEventLoop. + run_forever() and run_until_complete() methods of asyncio.BaseEventLoop now + raise an exception if the event loop was closed. + - Issue #21310: Fixed possible resource leak in failed open(). - Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 10:27:26 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Jun 2014 10:27:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogTWVyZ2UgMy40?= Message-ID: <3gnkwG07BXz7LmS@mail.python.org> http://hg.python.org/cpython/rev/7982a7c89d66 changeset: 91116:7982a7c89d66 parent: 91114:a8dfdae4c4a0 parent: 91115:7912179335cc user: Victor Stinner date: Tue Jun 10 10:26:38 2014 +0200 summary: Merge 3.4 files: Doc/library/asyncio-eventloop.rst | 2 +- Misc/NEWS | 4 ++++ 2 files changed, 5 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 @@ -123,7 +123,7 @@ Returns ``True`` if the event loop was closed. - .. versionadded:: 3.5 + .. versionadded:: 3.4.2 .. method:: BaseEventLoop.close() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,6 +92,10 @@ Library ------- +- Issue #21326: Add a new is_closed() method to asyncio.BaseEventLoop. + run_forever() and run_until_complete() methods of asyncio.BaseEventLoop now + raise an exception if the event loop was closed. + - Issue #21310: Fixed possible resource leak in failed open(). - Issue #21256: Printout of keyword args should be in deterministic order in -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 11:16:28 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Jun 2014 11:16:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNTk2?= =?utf-8?q?=3A_asyncio=2Ewait=28=29=3A_mention_that_the_sequence_of_future?= =?utf-8?q?s_must_not?= Message-ID: <3gnm0r4szzz7LnX@mail.python.org> http://hg.python.org/cpython/rev/2b3f8b6d6e5c changeset: 91117:2b3f8b6d6e5c branch: 3.4 parent: 91115:7912179335cc user: Victor Stinner date: Tue Jun 10 11:16:05 2014 +0200 summary: Issue #21596: asyncio.wait(): mention that the sequence of futures must not be empty. files: Doc/library/asyncio-task.rst | 2 ++ Lib/asyncio/tasks.py | 2 ++ 2 files changed, 4 insertions(+), 0 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 @@ -521,6 +521,8 @@ to complete. Coroutines will be wrapped in Tasks. Returns two sets of :class:`Future`: (done, pending). + The sequence *futures* must not be empty. + *timeout* can be used to control the maximum number of seconds to wait before returning. *timeout* can be an int or float. If *timeout* is not specified or ``None``, there is no limit to the wait time. diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -387,6 +387,8 @@ def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED): """Wait for the Futures and coroutines given by fs to complete. + The sequence futures must not be empty. + Coroutines will be wrapped in Tasks. Returns two sets of Future: (done, pending). -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 11:16:29 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Jun 2014 11:16:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogKE1lcmdlIDMuNCkgSXNzdWUgIzIxNTk2OiBhc3luY2lvLndhaXQoKTog?= =?utf-8?q?mention_that_the_sequence_of_futures?= Message-ID: <3gnm0s6YVXz7LpK@mail.python.org> http://hg.python.org/cpython/rev/68d45a1a3ce0 changeset: 91118:68d45a1a3ce0 parent: 91116:7982a7c89d66 parent: 91117:2b3f8b6d6e5c user: Victor Stinner date: Tue Jun 10 11:16:18 2014 +0200 summary: (Merge 3.4) Issue #21596: asyncio.wait(): mention that the sequence of futures must not be empty. files: Doc/library/asyncio-task.rst | 2 ++ Lib/asyncio/tasks.py | 2 ++ 2 files changed, 4 insertions(+), 0 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 @@ -521,6 +521,8 @@ to complete. Coroutines will be wrapped in Tasks. Returns two sets of :class:`Future`: (done, pending). + The sequence *futures* must not be empty. + *timeout* can be used to control the maximum number of seconds to wait before returning. *timeout* can be an int or float. If *timeout* is not specified or ``None``, there is no limit to the wait time. diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -387,6 +387,8 @@ def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED): """Wait for the Futures and coroutines given by fs to complete. + The sequence futures must not be empty. + Coroutines will be wrapped in Tasks. Returns two sets of Future: (done, pending). -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 19:08:37 2014 From: python-checkins at python.org (zach.ware) Date: Tue, 10 Jun 2014 19:08:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjg4?= =?utf-8?q?=3A_Give_informative_error_message_when_hhc=2Eexe_cannot_be_fou?= =?utf-8?b?bmQu?= Message-ID: <3gnyTd0xVRz7LjP@mail.python.org> http://hg.python.org/cpython/rev/b841b80e6421 changeset: 91119:b841b80e6421 branch: 3.4 parent: 91117:2b3f8b6d6e5c user: Zachary Ware date: Tue Jun 10 12:07:45 2014 -0500 summary: Issue #21688: Give informative error message when hhc.exe cannot be found. Initial patch by Olive Kilburn. files: Doc/make.bat | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Doc/make.bat b/Doc/make.bat --- a/Doc/make.bat +++ b/Doc/make.bat @@ -76,6 +76,15 @@ cmd /C %SPHINXBUILD% %SPHINXOPTS% -b%1 -dbuild\doctrees . %BUILDDIR%\%* if "%1" EQU "htmlhelp" ( + if not exist "%HTMLHELP%" ( + echo. + echo.The HTML Help Workshop was not found. Set the HTMLHELP variable + echo.to the path to hhc.exe or download and install it from + echo.http://msdn.microsoft.com/en-us/library/ms669985 + rem Set errorlevel to 1 and exit + cmd /C exit /b 1 + goto end + ) cmd /C "%HTMLHELP%" build\htmlhelp\python%DISTVERSION:.=%.hhp rem hhc.exe seems to always exit with code 1, reset to 0 for less than 2 if not errorlevel 2 cmd /C exit /b 0 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 19:08:38 2014 From: python-checkins at python.org (zach.ware) Date: Tue, 10 Jun 2014 19:08:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321688=3A_Merge_with_3=2E4?= Message-ID: <3gnyTf2Yfyz7LjY@mail.python.org> http://hg.python.org/cpython/rev/e5594e751a2e changeset: 91120:e5594e751a2e parent: 91118:68d45a1a3ce0 parent: 91119:b841b80e6421 user: Zachary Ware date: Tue Jun 10 12:08:20 2014 -0500 summary: Issue #21688: Merge with 3.4 files: Doc/make.bat | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Doc/make.bat b/Doc/make.bat --- a/Doc/make.bat +++ b/Doc/make.bat @@ -76,6 +76,15 @@ cmd /C %SPHINXBUILD% %SPHINXOPTS% -b%1 -dbuild\doctrees . %BUILDDIR%\%* if "%1" EQU "htmlhelp" ( + if not exist "%HTMLHELP%" ( + echo. + echo.The HTML Help Workshop was not found. Set the HTMLHELP variable + echo.to the path to hhc.exe or download and install it from + echo.http://msdn.microsoft.com/en-us/library/ms669985 + rem Set errorlevel to 1 and exit + cmd /C exit /b 1 + goto end + ) cmd /C "%HTMLHELP%" build\htmlhelp\python%DISTVERSION:.=%.hhp rem hhc.exe seems to always exit with code 1, reset to 0 for less than 2 if not errorlevel 2 cmd /C exit /b 0 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 10 20:30:07 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Tue, 10 Jun 2014 20:30:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2318039=3A_dbm=2Edu?= =?utf-8?q?mp=2Eopen=28=29_now_always_creates_a_new_database_when_the?= Message-ID: <3gp0Hg0171z7LjR@mail.python.org> http://hg.python.org/cpython/rev/3f944f44ee41 changeset: 91121:3f944f44ee41 user: Serhiy Storchaka date: Tue Jun 10 21:16:00 2014 +0300 summary: Issue #18039: dbm.dump.open() now always creates a new database when the flag has the value 'n'. Patch by Claudiu Popa. files: Doc/library/dbm.rst | 9 +++++++-- Lib/dbm/dumb.py | 27 ++++++++++++++++++--------- Lib/test/test_dbm_dumb.py | 8 ++++++++ Misc/NEWS | 3 +++ 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -316,13 +316,18 @@ dumbdbm database is created, files with :file:`.dat` and :file:`.dir` extensions are created. - The optional *flag* argument is currently ignored; the database is always opened - for update, and will be created if it does not exist. + The optional *flag* argument supports only the semantics of ``'c'`` + and ``'n'`` values. Other values will default to database being always + opened for update, and will be created if it does not exist. The optional *mode* argument is the Unix mode of the file, used only when the database has to be created. It defaults to octal ``0o666`` (and will be modified by the prevailing umask). + .. versionchanged:: 3.5 + :func:`.open` always creates a new database when the flag has the value + ``'n'``. + In addition to the methods provided by the :class:`collections.abc.MutableMapping` class, :class:`dumbdbm` objects provide the following method: diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -44,7 +44,7 @@ _os = _os # for _commit() _io = _io # for _commit() - def __init__(self, filebasename, mode): + def __init__(self, filebasename, mode, flag='c'): self._mode = mode # The directory file is a text file. Each line looks like @@ -64,6 +64,17 @@ # The index is an in-memory dict, mirroring the directory file. self._index = None # maps keys to (pos, siz) pairs + # Handle the creation + self._create(flag) + self._update() + + def _create(self, flag): + if flag == 'n': + for filename in (self._datfile, self._bakfile, self._dirfile): + try: + _os.remove(filename) + except OSError: + pass # Mod by Jack: create data file if needed try: f = _io.open(self._datfile, 'r', encoding="Latin-1") @@ -71,7 +82,6 @@ f = _io.open(self._datfile, 'w', encoding="Latin-1") self._chmod(self._datfile) f.close() - self._update() # Read directory file into the in-memory index dict. def _update(self): @@ -266,20 +276,20 @@ self.close() -def open(file, flag=None, mode=0o666): +def open(file, flag='c', mode=0o666): """Open the database file, filename, and return corresponding object. The flag argument, used to control how the database is opened in the - other DBM implementations, is ignored in the dbm.dumb module; the - database is always opened for update, and will be created if it does - not exist. + other DBM implementations, supports only the semantics of 'c' and 'n' + values. Other values will default to the semantics of 'c' value: + the database will always opened for update and will be created if it + does not exist. The optional mode argument is the UNIX mode of the file, used only when the database has to be created. It defaults to octal code 0o666 (and will be modified by the prevailing umask). """ - # flag argument is currently ignored # Modify mode depending on the umask try: @@ -290,5 +300,4 @@ else: # Turn off any bits that are set in the umask mode = mode & (~um) - - return _Database(file, mode) + return _Database(file, mode, flag=flag) diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -217,6 +217,14 @@ self.assertEqual(str(cm.exception), "DBM object has already been closed") + def test_create_new(self): + with dumbdbm.open(_fname, 'n') as f: + for k in self._dict: + f[k] = self._dict[k] + + with dumbdbm.open(_fname, 'n') as f: + self.assertEqual(f.keys(), []) + def tearDown(self): _delete_files() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,6 +92,9 @@ Library ------- +- Issue #18039: dbm.dump.open() now always creates a new database when the + flag has the value 'n'. Patch by Claudiu Popa. + - Issue #21326: Add a new is_closed() method to asyncio.BaseEventLoop. run_forever() and run_until_complete() methods of asyncio.BaseEventLoop now raise an exception if the event loop was closed. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 03:54:44 2014 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 11 Jun 2014 03:54:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_fix_issue_=2317552=3A_add_?= =?utf-8?q?socket=2Esendfile=28=29_method_allowing_to_send_a_file_over_a?= Message-ID: <3gpB8h1pNlz7LjR@mail.python.org> http://hg.python.org/cpython/rev/001895c39fea changeset: 91122:001895c39fea user: Giampaolo Rodola' date: Wed Jun 11 03:54:30 2014 +0200 summary: fix issue #17552: add socket.sendfile() method allowing to send a file over a socket by using high-performance os.sendfile() on UNIX. Patch by Giampaolo Rodola'? files: Doc/library/os.rst | 4 + Doc/library/socket.rst | 15 + Doc/library/ssl.rst | 3 + Doc/whatsnew/3.5.rst | 11 +- Lib/socket.py | 148 +++++++++++++++- Lib/ssl.py | 10 + Lib/test/test_socket.py | 273 ++++++++++++++++++++++++++++ Lib/test/test_ssl.py | 17 + Misc/NEWS | 4 + 9 files changed, 483 insertions(+), 2 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1092,6 +1092,10 @@ Availability: Unix. + .. note:: + + For a higher-level version of this see :mod:`socket.socket.sendfile`. + .. versionadded:: 3.3 diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1148,6 +1148,21 @@ .. versionadded:: 3.3 +.. method:: socket.sendfile(file, offset=0, count=None) + + Send a file until EOF is reached by using high-performance + :mod:`os.sendfile` and return the total number of bytes which were sent. + *file* must be a regular file object opened in binary mode. If + :mod:`os.sendfile` is not available (e.g. Windows) or *file* is not a + regular file :meth:`send` will be used instead. *offset* tells from where to + start reading the file. If specified, *count* is the total number of bytes + to transmit as opposed to sending the file until EOF is reached. File + position is updated on return or also in case of error in which case + :meth:`file.tell() ` can be used to figure out the number of + bytes which were sent. The socket must be of :const:`SOCK_STREAM` type. Non- + blocking sockets are not supported. + + .. versionadded:: 3.5 .. method:: socket.set_inheritable(inheritable) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -789,6 +789,9 @@ (but passing a non-zero ``flags`` argument is not allowed) - :meth:`~socket.socket.send()`, :meth:`~socket.socket.sendall()` (with the same limitation) +- :meth:`~socket.socket.sendfile()` (but :mod:`os.sendfile` will be used + for plain-text sockets only, else :meth:`~socket.socket.send()` will be used) + .. versionadded:: 3.5 - :meth:`~socket.socket.shutdown()` However, since the SSL (and TLS) protocol has its own framing atop diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -181,9 +181,18 @@ * Different constants of :mod:`signal` module are now enumeration values using the :mod:`enum` module. This allows meaningful names to be printed during - debugging, instead of integer ?magic numbers?. (contribute by Giampaolo + debugging, instead of integer ?magic numbers?. (contributed by Giampaolo Rodola' in :issue:`21076`) +socket +------ + +* New :meth:`socket.socket.sendfile` method allows to send a file over a socket + by using high-performance :func:`os.sendfile` function on UNIX resulting in + uploads being from 2x to 3x faster than when using plain + :meth:`socket.socket.send`. + (contributed by Giampaolo Rodola' in :issue:`17552`) + xmlrpc ------ diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -47,7 +47,7 @@ import _socket from _socket import * -import os, sys, io +import os, sys, io, selectors from enum import IntEnum try: @@ -109,6 +109,9 @@ __all__.append("errorTab") +class _GiveupOnSendfile(Exception): pass + + class socket(_socket.socket): """A subclass of _socket.socket adding the makefile() method.""" @@ -233,6 +236,149 @@ text.mode = mode return text + if hasattr(os, 'sendfile'): + + def _sendfile_use_sendfile(self, file, offset=0, count=None): + self._check_sendfile_params(file, offset, count) + sockno = self.fileno() + try: + fileno = file.fileno() + except (AttributeError, io.UnsupportedOperation) as err: + raise _GiveupOnSendfile(err) # not a regular file + try: + fsize = os.fstat(fileno).st_size + except OSError: + raise _GiveupOnSendfile(err) # not a regular file + if not fsize: + return 0 # empty file + blocksize = fsize if not count else count + + timeout = self.gettimeout() + if timeout == 0: + raise ValueError("non-blocking sockets are not supported") + # poll/select have the advantage of not requiring any + # extra file descriptor, contrarily to epoll/kqueue + # (also, they require a single syscall). + if hasattr(selectors, 'PollSelector'): + selector = selectors.PollSelector() + else: + selector = selectors.SelectSelector() + selector.register(sockno, selectors.EVENT_WRITE) + + total_sent = 0 + # localize variable access to minimize overhead + selector_select = selector.select + os_sendfile = os.sendfile + try: + while True: + if timeout and not selector_select(timeout): + raise _socket.timeout('timed out') + if count: + blocksize = count - total_sent + if blocksize <= 0: + break + try: + sent = os_sendfile(sockno, fileno, offset, blocksize) + except BlockingIOError: + if not timeout: + # Block until the socket is ready to send some + # data; avoids hogging CPU resources. + selector_select() + continue + except OSError as err: + if total_sent == 0: + # We can get here for different reasons, the main + # one being 'file' is not a regular mmap(2)-like + # file, in which case we'll fall back on using + # plain send(). + raise _GiveupOnSendfile(err) + raise err from None + else: + if sent == 0: + break # EOF + offset += sent + total_sent += sent + return total_sent + finally: + if total_sent > 0 and hasattr(file, 'seek'): + file.seek(offset) + else: + def _sendfile_use_sendfile(self, file, offset=0, count=None): + raise _GiveupOnSendfile( + "os.sendfile() not available on this platform") + + def _sendfile_use_send(self, file, offset=0, count=None): + self._check_sendfile_params(file, offset, count) + if self.gettimeout() == 0: + raise ValueError("non-blocking sockets are not supported") + if offset: + file.seek(offset) + blocksize = min(count, 8192) if count else 8192 + total_sent = 0 + # localize variable access to minimize overhead + file_read = file.read + sock_send = self.send + try: + while True: + if count: + blocksize = min(count - total_sent, blocksize) + if blocksize <= 0: + break + data = memoryview(file_read(blocksize)) + if not data: + break # EOF + while True: + try: + sent = sock_send(data) + except BlockingIOError: + continue + else: + total_sent += sent + if sent < len(data): + data = data[sent:] + else: + break + return total_sent + finally: + if total_sent > 0 and hasattr(file, 'seek'): + file.seek(offset + total_sent) + + def _check_sendfile_params(self, file, offset, count): + if 'b' not in getattr(file, 'mode', 'b'): + raise ValueError("file should be opened in binary mode") + if not self.type & SOCK_STREAM: + raise ValueError("only SOCK_STREAM type sockets are supported") + if count is not None: + if not isinstance(count, int): + raise TypeError( + "count must be a positive integer (got {!r})".format(count)) + if count <= 0: + raise ValueError( + "count must be a positive integer (got {!r})".format(count)) + + def sendfile(self, file, offset=0, count=None): + """sendfile(file[, offset[, count]]) -> sent + + Send a file until EOF is reached by using high-performance + os.sendfile() and return the total number of bytes which + were sent. + *file* must be a regular file object opened in binary mode. + If os.sendfile() is not available (e.g. Windows) or file is + not a regular file socket.send() will be used instead. + *offset* tells from where to start reading the file. + If specified, *count* is the total number of bytes to transmit + as opposed to sending the file until EOF is reached. + File position is updated on return or also in case of error in + which case file.tell() can be used to figure out the number of + bytes which were sent. + The socket must be of SOCK_STREAM type. + Non-blocking sockets are not supported. + """ + try: + return self._sendfile_use_sendfile(file, offset, count) + except _GiveupOnSendfile: + return self._sendfile_use_send(file, offset, count) + def _decref_socketios(self): if self._io_refs > 0: self._io_refs -= 1 diff --git a/Lib/ssl.py b/Lib/ssl.py --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -700,6 +700,16 @@ else: return socket.sendall(self, data, flags) + def sendfile(self, file, offset=0, count=None): + """Send a file, possibly by using os.sendfile() if this is a + clear-text socket. Return the total number of bytes sent. + """ + if self._sslobj is None: + # os.sendfile() works with plain sockets only + return super().sendfile(file, offset, count) + else: + return self._sendfile_use_send(file, offset, count) + def recv(self, buflen=1024, flags=0): self._checkClosed() if self._sslobj: 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 @@ -19,6 +19,8 @@ import math import pickle import struct +import random +import string try: import multiprocessing except ImportError: @@ -5077,6 +5079,275 @@ source.close() + at unittest.skipUnless(thread, 'Threading required for this test.') +class SendfileUsingSendTest(ThreadedTCPSocketTest): + """ + Test the send() implementation of socket.sendfile(). + """ + + FILESIZE = (10 * 1024 * 1024) # 10MB + BUFSIZE = 8192 + FILEDATA = b"" + TIMEOUT = 2 + + @classmethod + def setUpClass(cls): + def chunks(total, step): + assert total >= step + while total > step: + yield step + total -= step + if total: + yield total + + chunk = b"".join([random.choice(string.ascii_letters).encode() + for i in range(cls.BUFSIZE)]) + with open(support.TESTFN, 'wb') as f: + for csize in chunks(cls.FILESIZE, cls.BUFSIZE): + f.write(chunk) + with open(support.TESTFN, 'rb') as f: + cls.FILEDATA = f.read() + assert len(cls.FILEDATA) == cls.FILESIZE + + @classmethod + def tearDownClass(cls): + support.unlink(support.TESTFN) + + def accept_conn(self): + self.serv.settimeout(self.TIMEOUT) + conn, addr = self.serv.accept() + conn.settimeout(self.TIMEOUT) + self.addCleanup(conn.close) + return conn + + def recv_data(self, conn): + received = [] + while True: + chunk = conn.recv(self.BUFSIZE) + if not chunk: + break + received.append(chunk) + return b''.join(received) + + def meth_from_sock(self, sock): + # Depending on the mixin class being run return either send() + # or sendfile() method implementation. + return getattr(sock, "_sendfile_use_send") + + # regular file + + def _testRegularFile(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address) as sock, file as file: + meth = self.meth_from_sock(sock) + sent = meth(file) + self.assertEqual(sent, self.FILESIZE) + self.assertEqual(file.tell(), self.FILESIZE) + + def testRegularFile(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), self.FILESIZE) + self.assertEqual(data, self.FILEDATA) + + # non regular file + + def _testNonRegularFile(self): + address = self.serv.getsockname() + file = io.BytesIO(self.FILEDATA) + with socket.create_connection(address) as sock, file as file: + sent = sock.sendfile(file) + self.assertEqual(sent, self.FILESIZE) + self.assertEqual(file.tell(), self.FILESIZE) + self.assertRaises(socket._GiveupOnSendfile, + sock._sendfile_use_sendfile, file) + + def testNonRegularFile(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), self.FILESIZE) + self.assertEqual(data, self.FILEDATA) + + # empty file + + def _testEmptyFileSend(self): + address = self.serv.getsockname() + filename = support.TESTFN + "2" + with open(filename, 'wb'): + self.addCleanup(support.unlink, filename) + file = open(filename, 'rb') + with socket.create_connection(address) as sock, file as file: + meth = self.meth_from_sock(sock) + sent = meth(file) + self.assertEqual(sent, 0) + self.assertEqual(file.tell(), 0) + + def testEmptyFileSend(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(data, b"") + + # offset + + def _testOffset(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address) as sock, file as file: + meth = self.meth_from_sock(sock) + sent = meth(file, offset=5000) + self.assertEqual(sent, self.FILESIZE - 5000) + self.assertEqual(file.tell(), self.FILESIZE) + + def testOffset(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), self.FILESIZE - 5000) + self.assertEqual(data, self.FILEDATA[5000:]) + + # count + + def _testCount(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=2) as sock, file as file: + count = 5000007 + meth = self.meth_from_sock(sock) + sent = meth(file, count=count) + self.assertEqual(sent, count) + self.assertEqual(file.tell(), count) + + def testCount(self): + count = 5000007 + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), count) + self.assertEqual(data, self.FILEDATA[:count]) + + # count small + + def _testCountSmall(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=2) as sock, file as file: + count = 1 + meth = self.meth_from_sock(sock) + sent = meth(file, count=count) + self.assertEqual(sent, count) + self.assertEqual(file.tell(), count) + + def testCountSmall(self): + count = 1 + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), count) + self.assertEqual(data, self.FILEDATA[:count]) + + # count + offset + + def _testCountWithOffset(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=2) as sock, file as file: + count = 100007 + meth = self.meth_from_sock(sock) + sent = meth(file, offset=2007, count=count) + self.assertEqual(sent, count) + self.assertEqual(file.tell(), count + 2007) + + def testCountWithOffset(self): + count = 100007 + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), count) + self.assertEqual(data, self.FILEDATA[2007:count+2007]) + + # non blocking sockets are not supposed to work + + def _testNonBlocking(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address) as sock, file as file: + sock.setblocking(False) + meth = self.meth_from_sock(sock) + self.assertRaises(ValueError, meth, file) + self.assertRaises(ValueError, sock.sendfile, file) + + def testNonBlocking(self): + conn = self.accept_conn() + if conn.recv(8192): + self.fail('was not supposed to receive any data') + + # timeout (non-triggered) + + def _testWithTimeout(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=2) as sock, file as file: + meth = self.meth_from_sock(sock) + sent = meth(file) + self.assertEqual(sent, self.FILESIZE) + + def testWithTimeout(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), self.FILESIZE) + self.assertEqual(data, self.FILEDATA) + + # timeout (triggered) + + def _testWithTimeoutTriggeredSend(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=0.01) as sock, \ + file as file: + meth = self.meth_from_sock(sock) + self.assertRaises(socket.timeout, meth, file) + + def testWithTimeoutTriggeredSend(self): + conn = self.accept_conn() + conn.recv(88192) + + # errors + + def _test_errors(self): + pass + + def test_errors(self): + with open(support.TESTFN, 'rb') as file: + with socket.socket(type=socket.SOCK_DGRAM) as s: + meth = self.meth_from_sock(s) + self.assertRaisesRegex( + ValueError, "SOCK_STREAM", meth, file) + with open(support.TESTFN, 'rt') as file: + with socket.socket() as s: + meth = self.meth_from_sock(s) + self.assertRaisesRegex( + ValueError, "binary mode", meth, file) + with open(support.TESTFN, 'rb') as file: + with socket.socket() as s: + meth = self.meth_from_sock(s) + self.assertRaisesRegex(TypeError, "positive integer", + meth, file, count='2') + self.assertRaisesRegex(TypeError, "positive integer", + meth, file, count=0.1) + self.assertRaisesRegex(ValueError, "positive integer", + meth, file, count=0) + self.assertRaisesRegex(ValueError, "positive integer", + meth, file, count=-1) + + + at unittest.skipUnless(thread, 'Threading required for this test.') + at unittest.skipUnless(hasattr(os, "sendfile"), + 'os.sendfile() required for this test.') +class SendfileUsingSendfileTest(SendfileUsingSendTest): + """ + Test the sendfile() implementation of socket.sendfile(). + """ + def meth_from_sock(self, sock): + return getattr(sock, "_sendfile_use_sendfile") + + def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, TestExceptions, BufferIOTest, BasicTCPTest2, BasicUDPTest, UDPTimeoutTest ] @@ -5129,6 +5400,8 @@ InterruptedRecvTimeoutTest, InterruptedSendTimeoutTest, TestSocketSharing, + SendfileUsingSendTest, + SendfileUsingSendfileTest, ]) thread_info = support.threading_setup() diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2957,6 +2957,23 @@ self.assertRaises(ValueError, s.read, 1024) self.assertRaises(ValueError, s.write, b'hello') + def test_sendfile(self): + TEST_DATA = b"x" * 512 + with open(support.TESTFN, 'wb') as f: + f.write(TEST_DATA) + self.addCleanup(support.unlink, support.TESTFN) + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(CERTFILE) + context.load_cert_chain(CERTFILE) + server = ThreadedEchoServer(context=context, chatty=False) + with server: + with context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + with open(support.TESTFN, 'rb') as file: + s.sendfile(file) + self.assertEqual(s.recv(1024), TEST_DATA) + def test_main(verbose=False): if support.verbose: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,6 +92,10 @@ Library ------- +- Issue 17552: new socket.sendfile() method allowing to send a file over a + socket by using high-performance os.sendfile() on UNIX. + Patch by Giampaolo Rodola'. + - Issue #18039: dbm.dump.open() now always creates a new database when the flag has the value 'n'. Patch by Claudiu Popa. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 06:20:07 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 11 Jun 2014 06:20:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogUHlFcnJfTm9ybWFs?= =?utf-8?q?izeException_doesn=27t_like_being_called_with_an_exception_set?= Message-ID: <3gpFNR16VRz7Ljk@mail.python.org> http://hg.python.org/cpython/rev/a98fd4eeed40 changeset: 91123:a98fd4eeed40 branch: 3.4 parent: 91119:b841b80e6421 user: Serhiy Storchaka date: Wed Jun 11 07:18:53 2014 +0300 summary: PyErr_NormalizeException doesn't like being called with an exception set (issues #21677, #21310). files: Modules/_io/_iomodule.c | 8 ++++---- Modules/_io/bufferedio.c | 8 ++++---- Modules/_io/textio.c | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -470,14 +470,14 @@ if (_PyObject_CallMethodId(result, &PyId_close, NULL) != NULL) PyErr_Restore(exc, val, tb); else { - PyObject *val2; + PyObject *exc2, *val2, *tb2; + PyErr_Fetch(&exc2, &val2, &tb2); PyErr_NormalizeException(&exc, &val, &tb); Py_XDECREF(exc); Py_XDECREF(tb); - PyErr_Fetch(&exc, &val2, &tb); - PyErr_NormalizeException(&exc, &val2, &tb); + PyErr_NormalizeException(&exc2, &val2, &tb2); PyException_SetContext(val2, val); - PyErr_Restore(exc, val2, tb); + PyErr_Restore(exc2, val2, tb2); } Py_DECREF(result); } diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -548,14 +548,14 @@ PyErr_Restore(exc, val, tb); } else { - PyObject *val2; + PyObject *exc2, *val2, *tb2; + PyErr_Fetch(&exc2, &val2, &tb2); PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); - PyErr_Fetch(&exc, &val2, &tb); - PyErr_NormalizeException(&exc, &val2, &tb); + PyErr_NormalizeException(&exc2, &val2, &tb2); PyException_SetContext(val2, val); - PyErr_Restore(exc, val2, tb); + PyErr_Restore(exc2, val2, tb2); } } diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2613,14 +2613,14 @@ PyErr_Restore(exc, val, tb); } else { - PyObject *val2; + PyObject *exc2, *val2, *tb2; + PyErr_Fetch(&exc2, &val2, &tb2); PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); - PyErr_Fetch(&exc, &val2, &tb); - PyErr_NormalizeException(&exc, &val2, &tb); + PyErr_NormalizeException(&exc2, &val2, &tb2); PyException_SetContext(val2, val); - PyErr_Restore(exc, val2, tb); + PyErr_Restore(exc2, val2, tb2); } } return res; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 06:20:08 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 11 Jun 2014 06:20:08 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_PyErr=5FNormalizeException_doesn=27t_like_being_called_w?= =?utf-8?q?ith_an_exception_set?= Message-ID: <3gpFNS2PHxz7Ljr@mail.python.org> http://hg.python.org/cpython/rev/55c50c570098 changeset: 91124:55c50c570098 parent: 91122:001895c39fea parent: 91123:a98fd4eeed40 user: Serhiy Storchaka date: Wed Jun 11 07:19:39 2014 +0300 summary: PyErr_NormalizeException doesn't like being called with an exception set (issues #21677, #21310). files: Modules/_io/_iomodule.c | 8 ++++---- Modules/_io/bufferedio.c | 8 ++++---- Modules/_io/textio.c | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -470,14 +470,14 @@ if (_PyObject_CallMethodId(result, &PyId_close, NULL) != NULL) PyErr_Restore(exc, val, tb); else { - PyObject *val2; + PyObject *exc2, *val2, *tb2; + PyErr_Fetch(&exc2, &val2, &tb2); PyErr_NormalizeException(&exc, &val, &tb); Py_XDECREF(exc); Py_XDECREF(tb); - PyErr_Fetch(&exc, &val2, &tb); - PyErr_NormalizeException(&exc, &val2, &tb); + PyErr_NormalizeException(&exc2, &val2, &tb2); PyException_SetContext(val2, val); - PyErr_Restore(exc, val2, tb); + PyErr_Restore(exc2, val2, tb2); } Py_DECREF(result); } diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -548,14 +548,14 @@ PyErr_Restore(exc, val, tb); } else { - PyObject *val2; + PyObject *exc2, *val2, *tb2; + PyErr_Fetch(&exc2, &val2, &tb2); PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); - PyErr_Fetch(&exc, &val2, &tb); - PyErr_NormalizeException(&exc, &val2, &tb); + PyErr_NormalizeException(&exc2, &val2, &tb2); PyException_SetContext(val2, val); - PyErr_Restore(exc, val2, tb); + PyErr_Restore(exc2, val2, tb2); } } diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2619,14 +2619,14 @@ PyErr_Restore(exc, val, tb); } else { - PyObject *val2; + PyObject *exc2, *val2, *tb2; + PyErr_Fetch(&exc2, &val2, &tb2); PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); - PyErr_Fetch(&exc, &val2, &tb); - PyErr_NormalizeException(&exc, &val2, &tb); + PyErr_NormalizeException(&exc2, &val2, &tb2); PyException_SetContext(val2, val); - PyErr_Restore(exc, val2, tb); + PyErr_Restore(exc2, val2, tb2); } } return res; -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Wed Jun 11 06:29:27 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 11 Jun 2014 06:29:27 +0200 Subject: [Python-checkins] Daily reference leaks (3f944f44ee41): sum=3 Message-ID: results for 3f944f44ee41 on branch "default" -------------------------------------------- test_functools leaked [0, 0, 3] memory blocks, sum=3 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog21Wr1T', '-x'] From python-checkins at python.org Wed Jun 11 09:04:29 2014 From: python-checkins at python.org (vinay.sajip) Date: Wed, 11 Jun 2014 09:04:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzA5?= =?utf-8?q?=3A_Remove_references_to_=5F=5Ffile=5F=5F_when_part_of_a_frozen?= =?utf-8?q?_application=2E?= Message-ID: <3gpK251Mvpz7LkB@mail.python.org> http://hg.python.org/cpython/rev/11a920a26f13 changeset: 91125:11a920a26f13 branch: 3.4 parent: 91123:a98fd4eeed40 user: Vinay Sajip date: Wed Jun 11 08:01:32 2014 +0100 summary: Issue #21709: Remove references to __file__ when part of a frozen application. files: Lib/logging/__init__.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -55,8 +55,8 @@ # _srcfile is used when walking the stack to check when we've got the first # caller stack frame. # -if hasattr(sys, 'frozen'): #support for py2exe - _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:]) +if hasattr(sys, 'frozen'): + _srcfile = os.path.join('logging', '__init__.py') else: _srcfile = __file__ _srcfile = os.path.normcase(_srcfile) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 09:04:30 2014 From: python-checkins at python.org (vinay.sajip) Date: Wed, 11 Jun 2014 09:04:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Closes_=2321709=3A_Merged_fix_from_3=2E4=2E?= Message-ID: <3gpK2632hsz7LkS@mail.python.org> http://hg.python.org/cpython/rev/149cc6364180 changeset: 91126:149cc6364180 parent: 91124:55c50c570098 parent: 91125:11a920a26f13 user: Vinay Sajip date: Wed Jun 11 08:04:16 2014 +0100 summary: Closes #21709: Merged fix from 3.4. files: Lib/logging/__init__.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -55,8 +55,8 @@ # _srcfile is used when walking the stack to check when we've got the first # caller stack frame. # -if hasattr(sys, 'frozen'): #support for py2exe - _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:]) +if hasattr(sys, 'frozen'): + _srcfile = os.path.join('logging', '__init__.py') else: _srcfile = __file__ _srcfile = os.path.normcase(_srcfile) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 13:31:05 2014 From: python-checkins at python.org (larry.hastings) Date: Wed, 11 Jun 2014 13:31:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjI5?= =?utf-8?q?=3A_Fix_Argument_Clinic=27s_=22--converters=22_feature=2E?= Message-ID: <3gpQxj1ykhz7LjY@mail.python.org> http://hg.python.org/cpython/rev/6b2db7fc17f7 changeset: 91127:6b2db7fc17f7 branch: 3.4 parent: 91125:11a920a26f13 user: Larry Hastings date: Wed Jun 11 04:31:29 2014 -0700 summary: Issue #21629: Fix Argument Clinic's "--converters" feature. files: Misc/NEWS | 5 +++++ Tools/clinic/clinic.py | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -118,6 +118,11 @@ - Issue #21671, CVE-2014-0224: The bundled version of OpenSSL has been updated to 1.0.1h. +Tools/Demos +----------- + +- Issue #21629: Fix Argument Clinic's "--converters" feature. + What's New in Python 3.4.1? =========================== diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -2044,11 +2044,9 @@ # automatically add converter for default format unit # (but without stomping on the existing one if it's already # set, in case you subclass) - if ((cls.format_unit != 'O&') and + if ((cls.format_unit not in ('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): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 13:35:37 2014 From: python-checkins at python.org (larry.hastings) Date: Wed, 11 Jun 2014 13:35:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321629=3A_Merge_from_3=2E4=2E?= Message-ID: <3gpR2x51Hkz7LjY@mail.python.org> http://hg.python.org/cpython/rev/8b4b8f5d7321 changeset: 91128:8b4b8f5d7321 parent: 91126:149cc6364180 parent: 91127:6b2db7fc17f7 user: Larry Hastings date: Wed Jun 11 04:36:09 2014 -0700 summary: Issue #21629: Merge from 3.4. files: Misc/NEWS | 2 ++ Tools/clinic/clinic.py | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -623,6 +623,8 @@ Tools/Demos ----------- +- Issue #21629: Fix Argument Clinic's "--converters" feature. + - Add support for ``yield from`` to 2to3. - Add support for the PEP 465 matrix multiplication operator to 2to3. diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -2044,11 +2044,9 @@ # automatically add converter for default format unit # (but without stomping on the existing one if it's already # set, in case you subclass) - if ((cls.format_unit != 'O&') and + if ((cls.format_unit not in ('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): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 15:19:58 2014 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 11 Jun 2014 15:19:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzIxNjkzIC0gRml4?= =?utf-8?q?_the_broken_link_for_pylons_project=2E?= Message-ID: <3gpTML1lBGz7LjZ@mail.python.org> http://hg.python.org/cpython/rev/9438a8aa3622 changeset: 91129:9438a8aa3622 branch: 2.7 parent: 91110:ec91ee7d9d8d user: Senthil Kumaran date: Wed Jun 11 06:18:43 2014 -0700 summary: #21693 - Fix the broken link for pylons project. files: Doc/howto/webservers.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/howto/webservers.rst b/Doc/howto/webservers.rst --- a/Doc/howto/webservers.rst +++ b/Doc/howto/webservers.rst @@ -691,7 +691,7 @@ The newest version of TurboGears, version 2.0, moves even further in direction of WSGI support and a component-based architecture. TurboGears 2 is based on the WSGI stack of another popular component-based web framework, `Pylons -`_. +`_. Zope -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 15:19:59 2014 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 11 Jun 2014 15:19:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogIzIxNjkzIC0gRml4?= =?utf-8?q?_the_broken_link_for_pylons_project=2E?= Message-ID: <3gpTMM3Vk4z7Ljh@mail.python.org> http://hg.python.org/cpython/rev/08fa17130fb3 changeset: 91130:08fa17130fb3 branch: 3.4 parent: 91127:6b2db7fc17f7 user: Senthil Kumaran date: Wed Jun 11 06:19:21 2014 -0700 summary: #21693 - Fix the broken link for pylons project. files: Doc/howto/webservers.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/howto/webservers.rst b/Doc/howto/webservers.rst --- a/Doc/howto/webservers.rst +++ b/Doc/howto/webservers.rst @@ -687,7 +687,7 @@ The newest version of TurboGears, version 2.0, moves even further in direction of WSGI support and a component-based architecture. TurboGears 2 is based on the WSGI stack of another popular component-based web framework, `Pylons -`_. +`_. Zope -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 15:20:00 2014 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 11 Jun 2014 15:20:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E4?= Message-ID: <3gpTMN5KyPz7LkF@mail.python.org> http://hg.python.org/cpython/rev/a6892bd4f752 changeset: 91131:a6892bd4f752 parent: 91128:8b4b8f5d7321 parent: 91130:08fa17130fb3 user: Senthil Kumaran date: Wed Jun 11 06:19:47 2014 -0700 summary: merge from 3.4 files: Doc/howto/webservers.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/howto/webservers.rst b/Doc/howto/webservers.rst --- a/Doc/howto/webservers.rst +++ b/Doc/howto/webservers.rst @@ -687,7 +687,7 @@ The newest version of TurboGears, version 2.0, moves even further in direction of WSGI support and a component-based architecture. TurboGears 2 is based on the WSGI stack of another popular component-based web framework, `Pylons -`_. +`_. Zope -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 17:18:35 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 11 Jun 2014 17:18:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2319662=3A_add_decode=5Fd?= =?utf-8?q?ata_to_smtpd_so_you_can_get_at_DATA_in_bytes_form=2E?= Message-ID: <3gpX0C2fJFz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/4e22213ca275 changeset: 91132:4e22213ca275 user: R David Murray date: Wed Jun 11 11:18:08 2014 -0400 summary: #19662: add decode_data to smtpd so you can get at DATA in bytes form. Otherwise smtpd is restricted to 7bit clean data, since even if the incoming data is actually utf-8, it will often break things to decode it before parsing the message. Patch by Maciej Szulik, with some adjustments (mostly the warning support). files: Doc/library/smtpd.rst | 24 +++++- Doc/whatsnew/3.5.rst | 16 +++ Lib/smtpd.py | 45 ++++++++-- Lib/test/test_smtpd.py | 118 +++++++++++++++++++++++++++- 4 files changed, 185 insertions(+), 18 deletions(-) diff --git a/Doc/library/smtpd.rst b/Doc/library/smtpd.rst --- a/Doc/library/smtpd.rst +++ b/Doc/library/smtpd.rst @@ -28,7 +28,7 @@ .. class:: SMTPServer(localaddr, remoteaddr, data_size_limit=33554432,\ - map=None) + map=None, decode_data=True) Create a new :class:`SMTPServer` object, which binds to local address *localaddr*. It will treat *remoteaddr* as an upstream SMTP relayer. It @@ -41,6 +41,11 @@ A dictionary can be specified in *map* to avoid using a global socket map. + *decode_data* specifies whether the data portion of the SMTP transaction + should be decoded using UTF-8. The default is ``True`` for backward + compatibility reasons, but will change to ``False`` in Python 3.6. Specify + the keyword value explicitly to avoid the :exc:`DeprecationWarning`. + .. method:: process_message(peer, mailfrom, rcpttos, data) Raise :exc:`NotImplementedError` exception. Override this in subclasses to @@ -51,6 +56,10 @@ containing the contents of the e-mail (which should be in :rfc:`2822` format). + If the *decode_data* constructor keyword is set to ``True``, the *data* + argument will be a unicode string. If it is set to ``False``, it + will be a bytes object. + .. attribute:: channel_class Override this in subclasses to use a custom :class:`SMTPChannel` for @@ -59,6 +68,9 @@ .. versionchanged:: 3.4 The *map* argument was added. + .. versionchanged:: 3.5 + the *decode_data* argument was added. + DebuggingServer Objects ----------------------- @@ -97,7 +109,7 @@ ------------------- .. class:: SMTPChannel(server, conn, addr, data_size_limit=33554432,\ - map=None)) + map=None, decode_data=True) Create a new :class:`SMTPChannel` object which manages the communication between the server and a single SMTP client. @@ -110,9 +122,17 @@ A dictionary can be specified in *map* to avoid using a global socket map. + *decode_data* specifies whether the data portion of the SMTP transaction + should be decoded using UTF-8. The default is ``True`` for backward + compatibility reasons, but will change to ``False`` in Python 3.6. Specify + the keyword value explicitly to avoid the :exc:`DeprecationWarning`. + To use a custom SMTPChannel implementation you need to override the :attr:`SMTPServer.channel_class` of your :class:`SMTPServer`. + .. versionchanged:: 3.5 + the *decode_data* argument was added. + The :class:`SMTPChannel` has the following instance variables: .. attribute:: smtp_server diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -184,6 +184,16 @@ debugging, instead of integer ?magic numbers?. (contributed by Giampaolo Rodola' in :issue:`21076`) +smtpd +----- + +* Both :class:`~smtpd.SMTPServer` and :class:`smtpd.SMTPChannel` now accept a + *decode_data* keyword to determine if the DATA portion of the SMTP + transaction is decoded using the ``utf-8`` codec or is instead provided to + :meth:`~smtpd.SMTPServer.process_message` as a byte string. The default + is ``True`` for backward compatibility reasons, but will change to ``False`` + in Python 3.6. (Contributed by Maciej Szulik in :issue:`19662`.) + socket ------ @@ -245,6 +255,12 @@ * The :mod:`formatter` module has now graduated to full deprecation and is still slated for removal in Python 3.6. +* :mod:`smtpd` has in the past always decoded the DATA portion of email + messages using the ``utf-8`` codec. This can now be controlled by the new + *decode_data* keyword to :class:`~smtpd.SMTPServer`. The default value is + ``True``, but this default is deprecated. Specify the *decode_data* keyword + with an appropriate value to avoid the deprecation warning. + Deprecated functions and types of the C API ------------------------------------------- diff --git a/Lib/smtpd.py b/Lib/smtpd.py --- a/Lib/smtpd.py +++ b/Lib/smtpd.py @@ -98,7 +98,6 @@ DEBUGSTREAM = Devnull() NEWLINE = '\n' -EMPTYSTRING = '' COMMASPACE = ', ' DATA_SIZE_DEFAULT = 33554432 @@ -122,12 +121,28 @@ max_command_size_limit = max(command_size_limits.values()) def __init__(self, server, conn, addr, data_size_limit=DATA_SIZE_DEFAULT, - map=None): + map=None, decode_data=None): asynchat.async_chat.__init__(self, conn, map=map) self.smtp_server = server self.conn = conn self.addr = addr self.data_size_limit = data_size_limit + if decode_data is None: + warn("The decode_data default of True will change to False in 3.6;" + " specify an explicit value for this keyword", + DeprecationWarning, 2) + decode_data = True + self._decode_data = decode_data + if decode_data: + self._emptystring = '' + self._linesep = '\r\n' + self._dotsep = '.' + self._newline = NEWLINE + else: + self._emptystring = b'' + self._linesep = b'\r\n' + self._dotsep = b'.' + self._newline = b'\n' self.received_lines = [] self.smtp_state = self.COMMAND self.seen_greeting = '' @@ -287,11 +302,14 @@ return elif limit: self.num_bytes += len(data) - self.received_lines.append(str(data, "utf-8")) + if self._decode_data: + self.received_lines.append(str(data, 'utf-8')) + else: + self.received_lines.append(data) # Implementation of base class abstract method def found_terminator(self): - line = EMPTYSTRING.join(self.received_lines) + line = self._emptystring.join(self.received_lines) print('Data:', repr(line), file=DEBUGSTREAM) self.received_lines = [] if self.smtp_state == self.COMMAND: @@ -300,6 +318,8 @@ self.push('500 Error: bad syntax') return method = None + if not self._decode_data: + line = str(line, 'utf-8') i = line.find(' ') if i < 0: command = line.upper() @@ -330,12 +350,12 @@ # Remove extraneous carriage returns and de-transparency according # to RFC 5321, Section 4.5.2. data = [] - for text in line.split('\r\n'): - if text and text[0] == '.': + for text in line.split(self._linesep): + if text and text[0] == self._dotsep: data.append(text[1:]) else: data.append(text) - self.received_data = NEWLINE.join(data) + self.received_data = self._newline.join(data) status = self.smtp_server.process_message(self.peer, self.mailfrom, self.rcpttos, @@ -577,10 +597,17 @@ channel_class = SMTPChannel def __init__(self, localaddr, remoteaddr, - data_size_limit=DATA_SIZE_DEFAULT, map=None): + data_size_limit=DATA_SIZE_DEFAULT, map=None, + decode_data=None): self._localaddr = localaddr self._remoteaddr = remoteaddr self.data_size_limit = data_size_limit + if decode_data is None: + warn("The decode_data default of True will change to False in 3.6;" + " specify an explicit value for this keyword", + DeprecationWarning, 2) + decode_data = True + self._decode_data = decode_data asyncore.dispatcher.__init__(self, map=map) try: self.create_socket(socket.AF_INET, socket.SOCK_STREAM) @@ -599,7 +626,7 @@ def handle_accepted(self, conn, addr): print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM) channel = self.channel_class(self, conn, addr, self.data_size_limit, - self._map) + self._map, self._decode_data) # API for "doing something useful with the message" def process_message(self, peer, mailfrom, rcpttos, data): diff --git a/Lib/test/test_smtpd.py b/Lib/test/test_smtpd.py --- a/Lib/test/test_smtpd.py +++ b/Lib/test/test_smtpd.py @@ -7,13 +7,18 @@ class DummyServer(smtpd.SMTPServer): - def __init__(self, localaddr, remoteaddr): - smtpd.SMTPServer.__init__(self, localaddr, remoteaddr) + def __init__(self, localaddr, remoteaddr, decode_data=True): + smtpd.SMTPServer.__init__(self, localaddr, remoteaddr, + decode_data=decode_data) self.messages = [] + if decode_data: + self.return_status = 'return status' + else: + self.return_status = b'return status' def process_message(self, peer, mailfrom, rcpttos, data): self.messages.append((peer, mailfrom, rcpttos, data)) - if data == 'return status': + if data == self.return_status: return '250 Okish' @@ -31,9 +36,9 @@ smtpd.socket = asyncore.socket = mock_socket def test_process_message_unimplemented(self): - server = smtpd.SMTPServer('a', 'b') + server = smtpd.SMTPServer('a', 'b', decode_data=True) conn, addr = server.accept() - channel = smtpd.SMTPChannel(server, conn, addr) + channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True) def write_line(line): channel.socket.queue_recv(line) @@ -45,6 +50,10 @@ write_line(b'DATA') self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n') + def test_decode_data_default_warns(self): + with self.assertWarns(DeprecationWarning): + smtpd.SMTPServer('a', 'b') + def tearDown(self): asyncore.close_all() asyncore.socket = smtpd.socket = socket @@ -57,7 +66,8 @@ self.debug = smtpd.DEBUGSTREAM = io.StringIO() self.server = DummyServer('a', 'b') conn, addr = self.server.accept() - self.channel = smtpd.SMTPChannel(self.server, conn, addr) + self.channel = smtpd.SMTPChannel(self.server, conn, addr, + decode_data=True) def tearDown(self): asyncore.close_all() @@ -502,6 +512,12 @@ with support.check_warnings(('', DeprecationWarning)): self.channel._SMTPChannel__addr = 'spam' + def test_decode_data_default_warning(self): + server = DummyServer('a', 'b') + conn, addr = self.server.accept() + with self.assertWarns(DeprecationWarning): + smtpd.SMTPChannel(server, conn, addr) + class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase): @@ -512,7 +528,8 @@ self.server = DummyServer('a', 'b') conn, addr = self.server.accept() # Set DATA size limit to 32 bytes for easy testing - self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32) + self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32, + decode_data=True) def tearDown(self): asyncore.close_all() @@ -553,5 +570,92 @@ b'552 Error: Too much mail data\r\n') +class SMTPDChannelWithDecodeDataFalse(unittest.TestCase): + + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + self.old_debugstream = smtpd.DEBUGSTREAM + self.debug = smtpd.DEBUGSTREAM = io.StringIO() + self.server = DummyServer('a', 'b', decode_data=False) + conn, addr = self.server.accept() + # Set decode_data to False + self.channel = smtpd.SMTPChannel(self.server, conn, addr, + decode_data=False) + + def tearDown(self): + asyncore.close_all() + asyncore.socket = smtpd.socket = socket + smtpd.DEBUGSTREAM = self.old_debugstream + + def write_line(self, line): + self.channel.socket.queue_recv(line) + self.channel.handle_read() + + def test_ascii_data(self): + self.write_line(b'HELO example') + self.write_line(b'MAIL From:eggs at example') + self.write_line(b'RCPT To:spam at example') + self.write_line(b'DATA') + self.write_line(b'plain ascii text') + self.write_line(b'.') + self.assertEqual(self.channel.received_data, b'plain ascii text') + + def test_utf8_data(self): + self.write_line(b'HELO example') + self.write_line(b'MAIL From:eggs at example') + self.write_line(b'RCPT To:spam at example') + self.write_line(b'DATA') + self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87') + self.write_line(b'and some plain ascii') + self.write_line(b'.') + self.assertEqual( + self.channel.received_data, + b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87\n' + b'and some plain ascii') + + +class SMTPDChannelWithDecodeDataTrue(unittest.TestCase): + + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + self.old_debugstream = smtpd.DEBUGSTREAM + self.debug = smtpd.DEBUGSTREAM = io.StringIO() + self.server = DummyServer('a', 'b') + conn, addr = self.server.accept() + # Set decode_data to True + self.channel = smtpd.SMTPChannel(self.server, conn, addr, + decode_data=True) + + def tearDown(self): + asyncore.close_all() + asyncore.socket = smtpd.socket = socket + smtpd.DEBUGSTREAM = self.old_debugstream + + def write_line(self, line): + self.channel.socket.queue_recv(line) + self.channel.handle_read() + + def test_ascii_data(self): + self.write_line(b'HELO example') + self.write_line(b'MAIL From:eggs at example') + self.write_line(b'RCPT To:spam at example') + self.write_line(b'DATA') + self.write_line(b'plain ascii text') + self.write_line(b'.') + self.assertEqual(self.channel.received_data, 'plain ascii text') + + def test_utf8_data(self): + self.write_line(b'HELO example') + self.write_line(b'MAIL From:eggs at example') + self.write_line(b'RCPT To:spam at example') + self.write_line(b'DATA') + self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87') + self.write_line(b'and some plain ascii') + self.write_line(b'.') + self.assertEqual( + self.channel.received_data, + 'utf8 enriched text: ???\nand some plain ascii') + + if __name__ == "__main__": unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 18:27:58 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 11 Jun 2014 18:27:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2319662=3A_Eliminate_warn?= =?utf-8?q?ings_in_other_test_modules_that_use_smtpd=2E?= Message-ID: <3gpYXG3Rqzz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/a6c846ec5fd3 changeset: 91133:a6c846ec5fd3 user: R David Murray date: Wed Jun 11 12:27:40 2014 -0400 summary: #19662: Eliminate warnings in other test modules that use smtpd. Eventually these will want to convert to decode_data=False, I think. files: Lib/test/test_logging.py | 3 ++- Lib/test/test_smtplib.py | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -679,7 +679,8 @@ """ def __init__(self, addr, handler, poll_interval, sockmap): - smtpd.SMTPServer.__init__(self, addr, None, map=sockmap) + smtpd.SMTPServer.__init__(self, addr, None, map=sockmap, + decode_data=True) self.port = self.socket.getsockname()[1] self._handler = handler self._thread = None 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 @@ -184,7 +184,8 @@ self.old_DEBUGSTREAM = smtpd.DEBUGSTREAM smtpd.DEBUGSTREAM = io.StringIO() # Pick a random unused port by passing 0 for the port number - self.serv = smtpd.DebuggingServer((HOST, 0), ('nowhere', -1)) + self.serv = smtpd.DebuggingServer((HOST, 0), ('nowhere', -1), + decode_data=True) # Keep a note of what port was assigned self.port = self.serv.socket.getsockname()[1] serv_args = (self.serv, self.serv_evt, self.client_evt) @@ -719,7 +720,8 @@ def handle_accepted(self, conn, addr): self._SMTPchannel = self.channel_class( - self._extra_features, self, conn, addr) + self._extra_features, self, conn, addr, + decode_data=self._decode_data) def process_message(self, peer, mailfrom, rcpttos, data): pass @@ -742,7 +744,7 @@ self.serv_evt = threading.Event() self.client_evt = threading.Event() # Pick a random unused port by passing 0 for the port number - self.serv = SimSMTPServer((HOST, 0), ('nowhere', -1)) + self.serv = SimSMTPServer((HOST, 0), ('nowhere', -1), decode_data=True) # Keep a note of what port was assigned self.port = self.serv.socket.getsockname()[1] serv_args = (self.serv, self.serv_evt, self.client_evt) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 19:49:14 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 11 Jun 2014 19:49:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2314758=3A_add_IPv6_suppo?= =?utf-8?q?rt_to_smtpd=2E?= Message-ID: <3gpbL23xXcz7LjS@mail.python.org> http://hg.python.org/cpython/rev/1efbc86a200a changeset: 91134:1efbc86a200a user: R David Murray date: Wed Jun 11 13:48:58 2014 -0400 summary: #14758: add IPv6 support to smtpd. Patch by Milan Oberkirch. files: Doc/library/smtpd.rst | 4 +- Doc/whatsnew/3.5.rst | 4 ++ Lib/smtpd.py | 3 +- Lib/test/mock_socket.py | 11 +++-- Lib/test/test_smtpd.py | 51 ++++++++++++++++++++++++---- 5 files changed, 58 insertions(+), 15 deletions(-) diff --git a/Doc/library/smtpd.rst b/Doc/library/smtpd.rst --- a/Doc/library/smtpd.rst +++ b/Doc/library/smtpd.rst @@ -68,8 +68,8 @@ .. versionchanged:: 3.4 The *map* argument was added. - .. versionchanged:: 3.5 - the *decode_data* argument was added. + .. versionchanged:: 3.5 the *decode_data* argument was added, and *localaddr* + and *remoteaddr* may now contain IPv6 addresses. DebuggingServer Objects diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -194,6 +194,10 @@ is ``True`` for backward compatibility reasons, but will change to ``False`` in Python 3.6. (Contributed by Maciej Szulik in :issue:`19662`.) +* It is now possible to provide, directly or via name resolution, IPv6 + addresses in the :class:`~smtpd.SMTPServer` constructor, and have it + successfully connect. (Contributed by Milan Oberkirch in :issue:`14758`.) + socket ------ diff --git a/Lib/smtpd.py b/Lib/smtpd.py --- a/Lib/smtpd.py +++ b/Lib/smtpd.py @@ -610,7 +610,8 @@ self._decode_data = decode_data asyncore.dispatcher.__init__(self, map=map) try: - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + gai_results = socket.getaddrinfo(*localaddr) + self.create_socket(gai_results[0][0], gai_results[0][1]) # try to re-use a server port if possible self.set_reuse_addr() self.bind(localaddr) diff --git a/Lib/test/mock_socket.py b/Lib/test/mock_socket.py --- a/Lib/test/mock_socket.py +++ b/Lib/test/mock_socket.py @@ -35,8 +35,9 @@ class MockSocket: """Mock socket object used by smtpd and smtplib tests. """ - def __init__(self): + def __init__(self, family=None): global _reply_data + self.family = family self.output = [] self.lines = [] if _reply_data: @@ -108,8 +109,7 @@ def socket(family=None, type=None, proto=None): - return MockSocket() - + return MockSocket(family) def create_connection(address, timeout=socket_module._GLOBAL_DEFAULT_TIMEOUT, source_address=None): @@ -144,13 +144,16 @@ def gethostbyname(name): return "" +def getaddrinfo(host, port): + return socket_module.getaddrinfo(host, port) gaierror = socket_module.gaierror error = socket_module.error # Constants -AF_INET = None +AF_INET = socket_module.AF_INET +AF_INET6 = socket_module.AF_INET6 SOCK_STREAM = None SOL_SOCKET = None SO_REUSEADDR = None diff --git a/Lib/test/test_smtpd.py b/Lib/test/test_smtpd.py --- a/Lib/test/test_smtpd.py +++ b/Lib/test/test_smtpd.py @@ -36,7 +36,8 @@ smtpd.socket = asyncore.socket = mock_socket def test_process_message_unimplemented(self): - server = smtpd.SMTPServer('a', 'b', decode_data=True) + server = smtpd.SMTPServer((support.HOST, 0), ('b', 0), + decode_data=True) conn, addr = server.accept() channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True) @@ -52,19 +53,39 @@ def test_decode_data_default_warns(self): with self.assertWarns(DeprecationWarning): - smtpd.SMTPServer('a', 'b') + smtpd.SMTPServer((support.HOST, 0), ('b', 0)) def tearDown(self): asyncore.close_all() asyncore.socket = smtpd.socket = socket +class TestFamilyDetection(unittest.TestCase): + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + + def tearDown(self): + asyncore.close_all() + asyncore.socket = smtpd.socket = socket + + @unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled") + def test_socket_uses_IPv6(self): + server = smtpd.SMTPServer((support.HOSTv6, 0), (support.HOST, 0), + decode_data=False) + self.assertEqual(server.socket.family, socket.AF_INET6) + + def test_socket_uses_IPv4(self): + server = smtpd.SMTPServer((support.HOST, 0), (support.HOSTv6, 0), + decode_data=False) + self.assertEqual(server.socket.family, socket.AF_INET) + + class SMTPDChannelTest(unittest.TestCase): def setUp(self): smtpd.socket = asyncore.socket = mock_socket self.old_debugstream = smtpd.DEBUGSTREAM self.debug = smtpd.DEBUGSTREAM = io.StringIO() - self.server = DummyServer('a', 'b') + self.server = DummyServer((support.HOST, 0), ('b', 0)) conn, addr = self.server.accept() self.channel = smtpd.SMTPChannel(self.server, conn, addr, decode_data=True) @@ -79,7 +100,9 @@ self.channel.handle_read() def test_broken_connect(self): - self.assertRaises(DummyDispatcherBroken, BrokenDummyServer, 'a', 'b') + self.assertRaises( + DummyDispatcherBroken, BrokenDummyServer, + (support.HOST, 0), ('b', 0)) def test_server_accept(self): self.server.handle_accept() @@ -513,11 +536,21 @@ self.channel._SMTPChannel__addr = 'spam' def test_decode_data_default_warning(self): - server = DummyServer('a', 'b') + server = DummyServer((support.HOST, 0), ('b', 0)) conn, addr = self.server.accept() with self.assertWarns(DeprecationWarning): smtpd.SMTPChannel(server, conn, addr) + at unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled") +class SMTPDChannelIPv6Test(SMTPDChannelTest): + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + self.old_debugstream = smtpd.DEBUGSTREAM + self.debug = smtpd.DEBUGSTREAM = io.StringIO() + self.server = DummyServer((support.HOSTv6, 0), ('b', 0)) + conn, addr = self.server.accept() + self.channel = smtpd.SMTPChannel(self.server, conn, addr, + decode_data=True) class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase): @@ -525,7 +558,7 @@ smtpd.socket = asyncore.socket = mock_socket self.old_debugstream = smtpd.DEBUGSTREAM self.debug = smtpd.DEBUGSTREAM = io.StringIO() - self.server = DummyServer('a', 'b') + self.server = DummyServer((support.HOST, 0), ('b', 0)) conn, addr = self.server.accept() # Set DATA size limit to 32 bytes for easy testing self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32, @@ -576,7 +609,8 @@ smtpd.socket = asyncore.socket = mock_socket self.old_debugstream = smtpd.DEBUGSTREAM self.debug = smtpd.DEBUGSTREAM = io.StringIO() - self.server = DummyServer('a', 'b', decode_data=False) + self.server = DummyServer((support.HOST, 0), ('b', 0), + decode_data=False) conn, addr = self.server.accept() # Set decode_data to False self.channel = smtpd.SMTPChannel(self.server, conn, addr, @@ -620,7 +654,8 @@ smtpd.socket = asyncore.socket = mock_socket self.old_debugstream = smtpd.DEBUGSTREAM self.debug = smtpd.DEBUGSTREAM = io.StringIO() - self.server = DummyServer('a', 'b') + self.server = DummyServer((support.HOST, 0), ('b', 0), + decode_data=True) conn, addr = self.server.accept() # Set decode_data to True self.channel = smtpd.SMTPChannel(self.server, conn, addr, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 20:40:28 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 11 Jun 2014 20:40:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2319840=3A_Add_copy=5Ffun?= =?utf-8?q?ction_to_shutil=2Emove=2E?= Message-ID: <3gpcT80bHLz7LjP@mail.python.org> http://hg.python.org/cpython/rev/0d61a2a50f9f changeset: 91135:0d61a2a50f9f user: R David Murray date: Wed Jun 11 14:40:13 2014 -0400 summary: #19840: Add copy_function to shutil.move. Patch by Claudiu Popa. files: Doc/library/shutil.rst | 22 +++++++++++++++++----- Doc/whatsnew/3.5.rst | 8 ++++++++ Lib/shutil.py | 15 +++++++++++---- Lib/test/test_shutil.py | 18 ++++++++++++++++++ 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -191,7 +191,8 @@ match one of the glob-style *patterns* provided. See the example below. -.. function:: copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False) +.. function:: copytree(src, dst, symlinks=False, ignore=None, \ + copy_function=copy2, ignore_dangling_symlinks=False) Recursively copy an entire directory tree rooted at *src*, returning the destination directory. The destination @@ -282,7 +283,7 @@ .. versionadded:: 3.3 -.. function:: move(src, dst) +.. function:: move(src, dst, copy_function=copy2) Recursively move a file or directory (*src*) to another location (*dst*) and return the destination. @@ -295,15 +296,26 @@ :func:`os.rename` semantics. If the destination is on the current filesystem, then :func:`os.rename` is - used. Otherwise, *src* is copied (using :func:`shutil.copy2`) to *dst* and - then removed. In case of symlinks, a new symlink pointing to the target of - *src* will be created in or as *dst* and *src* will be removed. + used. Otherwise, *src* is copied to *dst* using *copy_function* and then + removed. In case of symlinks, a new symlink pointing to the target of *src* + will be created in or as *dst* and *src* will be removed. + + If *copy_function* is given, it must be a callable that takes two arguments + *src* and *dst*, and will be used to copy *src* to *dest* if + :func:`os.rename` cannot be used. If the source is a directory, + :func:`copytree` is called, passing it the :func:`copy_function`. The + default *copy_function* is :func:`copy2`. Using :func:`copy` as the + *copy_function* allows the move to succeed when it is not possible to also + copy the metadata, at the expense of not copying any of the metadata. .. versionchanged:: 3.3 Added explicit symlink handling for foreign filesystems, thus adapting it to the behavior of GNU's :program:`mv`. Now returns *dst*. + .. versionchanged:: 3.5 + Added the *copy_function* keyword argument. + .. function:: disk_usage(path) Return disk usage statistics about the given path as a :term:`named tuple` diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -176,6 +176,14 @@ network objects from existing addresses (contributed by Peter Moody and Antoine Pitrou in :issue:`16531`). +shutil +------ + +* :func:`~shutil.move` now accepts a *copy_function* argument, allowing, + for example, :func:`~shutil.copy` to be used instead of the default + :func:`~shutil.copy2` if there is a need to ignore metadata. (Contributed by + Claudiu Popa in :issue:`19840`.) + signal ------ diff --git a/Lib/shutil.py b/Lib/shutil.py --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -486,7 +486,7 @@ sep = os.path.sep + (os.path.altsep or '') return os.path.basename(path.rstrip(sep)) -def move(src, dst): +def move(src, dst, copy_function=copy2): """Recursively move a file or directory to another location. This is similar to the Unix "mv" command. Return the file or directory's destination. @@ -503,6 +503,11 @@ recreated under the new name if os.rename() fails because of cross filesystem renames. + The optional `copy_function` argument is a callable that will be used + to copy the source or it will be delegated to `copytree`. + By default, copy2() is used, but any function that supports the same + signature (like copy()) can be used. + A lot more could be done here... A look at a mv.c shows a lot of the issues this implementation glosses over. @@ -527,11 +532,13 @@ os.unlink(src) elif os.path.isdir(src): if _destinsrc(src, dst): - raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst)) - copytree(src, real_dst, symlinks=True) + raise Error("Cannot move a directory '%s' into itself" + " '%s'." % (src, dst)) + copytree(src, real_dst, copy_function=copy_function, + symlinks=True) rmtree(src) else: - copy2(src, real_dst) + copy_function(src, real_dst) os.unlink(src) return real_dst diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1592,6 +1592,24 @@ rv = shutil.move(self.src_file, os.path.join(self.dst_dir, 'bar')) self.assertEqual(rv, os.path.join(self.dst_dir, 'bar')) + @mock_rename + def test_move_file_special_function(self): + moved = [] + def _copy(src, dst): + moved.append((src, dst)) + shutil.move(self.src_file, self.dst_dir, copy_function=_copy) + self.assertEqual(len(moved), 1) + + @mock_rename + def test_move_dir_special_function(self): + moved = [] + def _copy(src, dst): + moved.append((src, dst)) + support.create_empty_file(os.path.join(self.src_dir, 'child')) + support.create_empty_file(os.path.join(self.src_dir, 'child1')) + shutil.move(self.src_dir, self.dst_dir, copy_function=_copy) + self.assertEqual(len(moved), 3) + class TestCopyFile(unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 21:18:42 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 11 Jun 2014 21:18:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2314758=3A_Need_to_specif?= =?utf-8?q?y_the_desired_socket_type_in_the_getaddrinfo_call=2E?= Message-ID: <3gpdKG3srRz7LjS@mail.python.org> http://hg.python.org/cpython/rev/d8e0fca7cbe3 changeset: 91136:d8e0fca7cbe3 user: R David Murray date: Wed Jun 11 15:17:50 2014 -0400 summary: #14758: Need to specify the desired socket type in the getaddrinfo call. This worked by accident on Linux because the SOCK_STREAM was returned first, but on the FreeBSD the SOCK_DGRAM is first in the list. files: Lib/smtpd.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/smtpd.py b/Lib/smtpd.py --- a/Lib/smtpd.py +++ b/Lib/smtpd.py @@ -610,7 +610,8 @@ self._decode_data = decode_data asyncore.dispatcher.__init__(self, map=map) try: - gai_results = socket.getaddrinfo(*localaddr) + gai_results = socket.getaddrinfo(*localaddr, + type=socket.SOCK_STREAM) self.create_socket(gai_results[0][0], gai_results[0][1]) # try to re-use a server port if possible self.set_reuse_addr() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 22:03:01 2014 From: python-checkins at python.org (zach.ware) Date: Wed, 11 Jun 2014 22:03:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_doc_build_warning?= Message-ID: <3gpfJP178dz7LjX@mail.python.org> http://hg.python.org/cpython/rev/59e4af72a3c7 changeset: 91137:59e4af72a3c7 user: Zachary Ware date: Wed Jun 11 15:02:25 2014 -0500 summary: Fix doc build warning files: Doc/library/ssl.rst | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -791,7 +791,9 @@ the same limitation) - :meth:`~socket.socket.sendfile()` (but :mod:`os.sendfile` will be used for plain-text sockets only, else :meth:`~socket.socket.send()` will be used) - .. versionadded:: 3.5 + + .. versionadded:: 3.5 + - :meth:`~socket.socket.shutdown()` However, since the SSL (and TLS) protocol has its own framing atop -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 22:21:38 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 11 Jun 2014 22:21:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2314758=3A_Fix_the_fix_?= =?utf-8?q?=28fix_getaddrinfo_in_mock=5Fsocket=29?= Message-ID: <3gpfjt3fdrz7LjX@mail.python.org> http://hg.python.org/cpython/rev/9b0d58b0c712 changeset: 91138:9b0d58b0c712 user: R David Murray date: Wed Jun 11 16:10:10 2014 -0400 summary: #14758: Fix the fix (fix getaddrinfo in mock_socket) I forgot to run all the affected tests when I fixed smtpd. files: Lib/test/mock_socket.py | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/mock_socket.py b/Lib/test/mock_socket.py --- a/Lib/test/mock_socket.py +++ b/Lib/test/mock_socket.py @@ -144,8 +144,8 @@ def gethostbyname(name): return "" -def getaddrinfo(host, port): - return socket_module.getaddrinfo(host, port) +def getaddrinfo(*args, **kw): + return socket_module.getaddrinfo(*args, **kw) gaierror = socket_module.gaierror error = socket_module.error @@ -154,6 +154,6 @@ # Constants AF_INET = socket_module.AF_INET AF_INET6 = socket_module.AF_INET6 -SOCK_STREAM = None +SOCK_STREAM = socket_module.SOCK_STREAM SOL_SOCKET = None SO_REUSEADDR = None -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 22:25:28 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 11 Jun 2014 22:25:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Eliminate_DeprecationWarni?= =?utf-8?q?ng_in_test=5Fconcurrent=5Ffutures=2E?= Message-ID: <3gpfpJ6fm9z7LjX@mail.python.org> http://hg.python.org/cpython/rev/d578228f0dcc changeset: 91139:d578228f0dcc user: R David Murray date: Wed Jun 11 16:25:05 2014 -0400 summary: Eliminate DeprecationWarning in test_concurrent_futures. files: Lib/test/test_concurrent_futures.py | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -427,9 +427,9 @@ def test_max_workers_negative(self): for number in (0, -1): - with self.assertRaisesRegexp(ValueError, - "max_workers must be greater " - "than 0"): + with self.assertRaisesRegex(ValueError, + "max_workers must be greater " + "than 0"): self.executor_type(max_workers=number) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 22:29:07 2014 From: python-checkins at python.org (zach.ware) Date: Wed, 11 Jun 2014 22:29:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzEz?= =?utf-8?q?=3A_Fix_typo_in_a_comment=2E__Found_by_Joseph_Shen=2E?= Message-ID: <3gpftW2Kp9z7LjZ@mail.python.org> http://hg.python.org/cpython/rev/61a00b7eac5d changeset: 91140:61a00b7eac5d branch: 3.4 parent: 91130:08fa17130fb3 user: Zachary Ware date: Wed Jun 11 15:27:04 2014 -0500 summary: Issue #21713: Fix typo in a comment. Found by Joseph Shen. files: PC/pyconfig.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/PC/pyconfig.h b/PC/pyconfig.h --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -390,7 +390,7 @@ #else /* VC6, VS 2002 and eVC4 don't support the C99 LL suffix for 64-bit integer literals */ #define Py_LL(x) x##I64 -#endif /* _MSC_VER > 1200 */ +#endif /* _MSC_VER > 1300 */ #endif /* _MSC_VER */ #endif -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 22:29:08 2014 From: python-checkins at python.org (zach.ware) Date: Wed, 11 Jun 2014 22:29:08 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Closes_=2321713=3A_Merge_with_3=2E4?= Message-ID: <3gpftX3c8mz7Ljk@mail.python.org> http://hg.python.org/cpython/rev/b94384c4f9d0 changeset: 91141:b94384c4f9d0 parent: 91139:d578228f0dcc parent: 91140:61a00b7eac5d user: Zachary Ware date: Wed Jun 11 15:28:31 2014 -0500 summary: Closes #21713: Merge with 3.4 files: PC/pyconfig.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/PC/pyconfig.h b/PC/pyconfig.h --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -390,7 +390,7 @@ #else /* VC6, VS 2002 and eVC4 don't support the C99 LL suffix for 64-bit integer literals */ #define Py_LL(x) x##I64 -#endif /* _MSC_VER > 1200 */ +#endif /* _MSC_VER > 1300 */ #endif /* _MSC_VER */ #endif -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 11 23:10:27 2014 From: python-checkins at python.org (r.david.murray) Date: Wed, 11 Jun 2014 23:10:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Suppress_a_couple_more_Dep?= =?utf-8?q?recationWarnings_in_the_test_suite=2E?= Message-ID: <3gpgpC4Rgpz7LkV@mail.python.org> http://hg.python.org/cpython/rev/38a325c84564 changeset: 91142:38a325c84564 user: R David Murray date: Wed Jun 11 17:09:43 2014 -0400 summary: Suppress a couple more DeprecationWarnings in the test suite. files: Lib/test/test_urllibnet.py | 3 ++- Lib/unittest/test/testmock/testmock.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -91,7 +91,8 @@ # test getcode() with the fancy opener to get 404 error codes URL = "http://www.example.com/XXXinvalidXXX" with support.transient_internet(URL): - open_url = urllib.request.FancyURLopener().open(URL) + with self.assertWarns(DeprecationWarning): + open_url = urllib.request.FancyURLopener().open(URL) try: code = open_url.getcode() finally: diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1211,7 +1211,7 @@ m = Mock() m.hello(name='hello', daddy='hero') text = "call(daddy='hero', name='hello')" - self.assertEquals(repr(m.hello.call_args), text) + self.assertEqual(repr(m.hello.call_args), text) def test_mock_add_spec(self): class _One(object): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 12 07:04:00 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 12 Jun 2014 07:04:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzEyMzg3?= =?utf-8?q?=3A_Add_missing_upper=28lower=29case_versions_of_default_Window?= =?utf-8?q?s_key?= Message-ID: <3gptJc1XkJz7LjW@mail.python.org> http://hg.python.org/cpython/rev/55fed3eae14b changeset: 91143:55fed3eae14b branch: 2.7 parent: 91129:9438a8aa3622 user: Terry Jan Reedy date: Thu Jun 12 01:03:01 2014 -0400 summary: Issue #12387: Add missing upper(lower)case versions of default Windows key bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy. files: Lib/idlelib/config-keys.def | 28 ++++++++++++------------ 1 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -13,37 +13,37 @@ paste= beginning-of-line= center-insert= -close-all-windows= +close-all-windows= close-window= do-nothing= end-of-file= python-docs= python-context-help= -history-next= -history-previous= +history-next= +history-previous= interrupt-execution= view-restart= restart-shell= -open-class-browser= -open-module= +open-class-browser= +open-module= open-new-window= open-window-from-file= plain-newline-and-indent= print-window= -redo= +redo= remove-selection= -save-copy-of-window-as-file= -save-window-as-file= -save-window= -select-all= +save-copy-of-window-as-file= +save-window-as-file= +save-window= +select-all= toggle-auto-coloring= undo= find= -find-again= +find-again= find-in-files= find-selection= replace= -goto-line= +goto-line= smart-backspace= newline-and-indent= smart-indent= @@ -53,8 +53,8 @@ uncomment-region= tabify-region= untabify-region= -toggle-tabs= -change-indentwidth= +toggle-tabs= +change-indentwidth= del-word-left= del-word-right= -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 12 07:04:01 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 12 Jun 2014 07:04:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzEyMzg3?= =?utf-8?q?=3A_Add_missing_upper=28lower=29case_versions_of_default_Window?= =?utf-8?q?s_key?= Message-ID: <3gptJd4RmQz7LjY@mail.python.org> http://hg.python.org/cpython/rev/25fd9aeeff91 changeset: 91144:25fd9aeeff91 branch: 3.4 parent: 91140:61a00b7eac5d user: Terry Jan Reedy date: Thu Jun 12 01:03:08 2014 -0400 summary: Issue #12387: Add missing upper(lower)case versions of default Windows key bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy. files: Lib/idlelib/config-keys.def | 28 ++++++++++++------------ 1 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -13,37 +13,37 @@ paste= beginning-of-line= center-insert= -close-all-windows= +close-all-windows= close-window= do-nothing= end-of-file= python-docs= python-context-help= -history-next= -history-previous= +history-next= +history-previous= interrupt-execution= view-restart= restart-shell= -open-class-browser= -open-module= +open-class-browser= +open-module= open-new-window= open-window-from-file= plain-newline-and-indent= print-window= -redo= +redo= remove-selection= -save-copy-of-window-as-file= -save-window-as-file= -save-window= -select-all= +save-copy-of-window-as-file= +save-window-as-file= +save-window= +select-all= toggle-auto-coloring= undo= find= -find-again= +find-again= find-in-files= find-selection= replace= -goto-line= +goto-line= smart-backspace= newline-and-indent= smart-indent= @@ -53,8 +53,8 @@ uncomment-region= tabify-region= untabify-region= -toggle-tabs= -change-indentwidth= +toggle-tabs= +change-indentwidth= del-word-left= del-word-right= -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 12 07:04:03 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 12 Jun 2014 07:04:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gptJg0238z7Lk5@mail.python.org> http://hg.python.org/cpython/rev/9aba5d75ce94 changeset: 91145:9aba5d75ce94 parent: 91142:38a325c84564 parent: 91144:25fd9aeeff91 user: Terry Jan Reedy date: Thu Jun 12 01:03:35 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/config-keys.def | 28 ++++++++++++------------ 1 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -13,37 +13,37 @@ paste= beginning-of-line= center-insert= -close-all-windows= +close-all-windows= close-window= do-nothing= end-of-file= python-docs= python-context-help= -history-next= -history-previous= +history-next= +history-previous= interrupt-execution= view-restart= restart-shell= -open-class-browser= -open-module= +open-class-browser= +open-module= open-new-window= open-window-from-file= plain-newline-and-indent= print-window= -redo= +redo= remove-selection= -save-copy-of-window-as-file= -save-window-as-file= -save-window= -select-all= +save-copy-of-window-as-file= +save-window-as-file= +save-window= +select-all= toggle-auto-coloring= undo= find= -find-again= +find-again= find-in-files= find-selection= replace= -goto-line= +goto-line= smart-backspace= newline-and-indent= smart-indent= @@ -53,8 +53,8 @@ uncomment-region= tabify-region= untabify-region= -toggle-tabs= -change-indentwidth= +toggle-tabs= +change-indentwidth= del-word-left= del-word-right= -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Thu Jun 12 10:54:51 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 12 Jun 2014 10:54:51 +0200 Subject: [Python-checkins] Daily reference leaks (38a325c84564): sum=9 Message-ID: results for 38a325c84564 on branch "default" -------------------------------------------- test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 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/reflogD9NY6h', '-x'] From python-checkins at python.org Thu Jun 12 18:43:11 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 12 Jun 2014 18:43:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogVHVs?= =?utf-8?q?ip_issue_173=3A_Enhance_repr=28Handle=29_and_repr=28Task=29?= Message-ID: <3gq9qM2r3Fz7Ljk@mail.python.org> http://hg.python.org/cpython/rev/e0395363f22f changeset: 91146:e0395363f22f branch: 3.4 parent: 91144:25fd9aeeff91 user: Victor Stinner date: Thu Jun 12 18:39:26 2014 +0200 summary: asyncio: Tulip issue 173: Enhance repr(Handle) and repr(Task) repr(Handle) is shorter for function: "foo" instead of "". It now also includes the source of the callback, filename and line number where it was defined, if available. repr(Task) now also includes the current position in the code, filename and line number, if available. If the coroutine (generator) is done, the line number is omitted and "done" is added. files: Lib/asyncio/events.py | 30 +++++- Lib/asyncio/tasks.py | 10 +- Lib/asyncio/test_utils.py | 7 + Lib/test/test_asyncio/test_events.py | 78 +++++++++++---- Lib/test/test_asyncio/test_tasks.py | 29 ++++- 5 files changed, 123 insertions(+), 31 deletions(-) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -8,9 +8,29 @@ 'get_child_watcher', 'set_child_watcher', ] +import functools +import inspect import subprocess import threading import socket +import sys + + +_PY34 = sys.version_info >= (3, 4) + +def _get_function_source(func): + if _PY34: + func = inspect.unwrap(func) + elif hasattr(func, '__wrapped__'): + func = func.__wrapped__ + if inspect.isfunction(func): + code = func.__code__ + return (code.co_filename, code.co_firstlineno) + if isinstance(func, functools.partial): + return _get_function_source(func.func) + if _PY34 and isinstance(func, functools.partialmethod): + return _get_function_source(func.func) + return None class Handle: @@ -26,7 +46,15 @@ self._cancelled = False def __repr__(self): - res = 'Handle({}, {})'.format(self._callback, self._args) + cb_repr = getattr(self._callback, '__qualname__', None) + if not cb_repr: + cb_repr = str(self._callback) + + source = _get_function_source(self._callback) + if source: + cb_repr += ' at %s:%s' % source + + res = 'Handle({}, {})'.format(cb_repr, self._args) if self._cancelled: res += '' return res diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -188,7 +188,15 @@ i = res.find('<') if i < 0: i = len(res) - res = res[:i] + '(<{}>)'.format(self._coro.__name__) + res[i:] + text = self._coro.__name__ + coro = self._coro + if inspect.isgenerator(coro): + filename = coro.gi_code.co_filename + if coro.gi_frame is not None: + text += ' at %s:%s' % (filename, coro.gi_frame.f_lineno) + else: + text += ' done at %s' % filename + res = res[:i] + '(<{}>)'.format(text) + res[i:] return res def get_stack(self, *, limit=None): 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 @@ -372,3 +372,10 @@ """ def __eq__(self, other): return bool(re.search(str(self), other, re.S)) + + +def get_function_source(func): + source = events._get_function_source(func) + if source is None: + raise ValueError("unable to get the source of %r" % (func,)) + return source 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 @@ -5,6 +5,7 @@ import io import os import platform +import re import signal import socket try: @@ -1737,52 +1738,46 @@ return asyncio.SelectorEventLoop(selectors.SelectSelector()) +def noop(): + pass + + class HandleTests(unittest.TestCase): + def setUp(self): + self.loop = None + def test_handle(self): def callback(*args): return args args = () - h = asyncio.Handle(callback, args, mock.Mock()) + h = asyncio.Handle(callback, args, self.loop) self.assertIs(h._callback, callback) self.assertIs(h._args, args) self.assertFalse(h._cancelled) - r = repr(h) - self.assertTrue(r.startswith( - 'Handle(' - '.callback')) - self.assertTrue(r.endswith('())')) - h.cancel() self.assertTrue(h._cancelled) - r = repr(h) - self.assertTrue(r.startswith( - 'Handle(' - '.callback')) - self.assertTrue(r.endswith('())'), r) - def test_handle_from_handle(self): def callback(*args): return args - m_loop = object() - h1 = asyncio.Handle(callback, (), loop=m_loop) + h1 = asyncio.Handle(callback, (), loop=self.loop) self.assertRaises( - AssertionError, asyncio.Handle, h1, (), m_loop) + AssertionError, asyncio.Handle, h1, (), self.loop) def test_callback_with_exception(self): def callback(): raise ValueError() - m_loop = mock.Mock() - m_loop.call_exception_handler = mock.Mock() + self.loop = mock.Mock() + self.loop.call_exception_handler = mock.Mock() - h = asyncio.Handle(callback, (), m_loop) + h = asyncio.Handle(callback, (), self.loop) h._run() - m_loop.call_exception_handler.assert_called_with({ + self.loop.call_exception_handler.assert_called_with({ 'message': test_utils.MockPattern('Exception in callback.*'), 'exception': mock.ANY, 'handle': h @@ -1790,9 +1785,50 @@ def test_handle_weakref(self): wd = weakref.WeakValueDictionary() - h = asyncio.Handle(lambda: None, (), object()) + h = asyncio.Handle(lambda: None, (), self.loop) wd['h'] = h # Would fail without __weakref__ slot. + def test_repr(self): + # simple function + h = asyncio.Handle(noop, (), self.loop) + src = test_utils.get_function_source(noop) + self.assertEqual(repr(h), + 'Handle(noop at %s:%s, ())' % src) + + # cancelled handle + h.cancel() + self.assertEqual(repr(h), + 'Handle(noop at %s:%s, ())' % src) + + # decorated function + cb = asyncio.coroutine(noop) + h = asyncio.Handle(cb, (), self.loop) + self.assertEqual(repr(h), + 'Handle(noop at %s:%s, ())' % src) + + # partial function + cb = functools.partial(noop) + h = asyncio.Handle(cb, (), self.loop) + filename, lineno = src + regex = (r'^Handle\(functools.partial\(' + r'\) at %s:%s, ' + r'\(\)\)$' % (re.escape(filename), lineno)) + self.assertRegex(repr(h), regex) + + # partial method + if sys.version_info >= (3, 4): + method = HandleTests.test_repr + cb = functools.partialmethod(method) + src = test_utils.get_function_source(method) + h = asyncio.Handle(cb, (), self.loop) + + filename, lineno = src + regex = (r'^Handle\(functools.partialmethod\(' + r', , \) at %s:%s, ' + r'\(\)\)$' % (re.escape(filename), lineno)) + self.assertRegex(repr(h), regex) + + class TimerTests(unittest.TestCase): 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 @@ -116,21 +116,30 @@ yield from [] return 'abc' + filename, lineno = test_utils.get_function_source(notmuch) + src = "%s:%s" % (filename, lineno) + t = asyncio.Task(notmuch(), loop=self.loop) t.add_done_callback(Dummy()) - self.assertEqual(repr(t), 'Task()') + self.assertEqual(repr(t), + 'Task()' % src) + t.cancel() # Does not take immediate effect! - self.assertEqual(repr(t), 'Task()') + self.assertEqual(repr(t), + 'Task()' % src) self.assertRaises(asyncio.CancelledError, self.loop.run_until_complete, t) - self.assertEqual(repr(t), 'Task()') + self.assertEqual(repr(t), + 'Task()' % filename) + t = asyncio.Task(notmuch(), loop=self.loop) self.loop.run_until_complete(t) - self.assertEqual(repr(t), "Task()") + self.assertEqual(repr(t), + "Task()" % filename) def test_task_repr_custom(self): @asyncio.coroutine - def coro(): + def notmuch(): pass class T(asyncio.Future): @@ -141,10 +150,14 @@ def __repr__(self): return super().__repr__() - gen = coro() + gen = notmuch() t = MyTask(gen, loop=self.loop) - self.assertEqual(repr(t), 'T[]()') - gen.close() + filename = gen.gi_code.co_filename + lineno = gen.gi_frame.f_lineno + # FIXME: check for the name "coro" instead of "notmuch" because + # @asyncio.coroutine drops the name of the wrapped function: + # http://bugs.python.org/issue21205 + self.assertEqual(repr(t), 'T[]()' % (filename, lineno)) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 12 18:43:12 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 12 Jun 2014 18:43:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Tulip_issue_173=3A_Enhance_?= =?utf-8?q?repr=28Handle=29_and_repr=28Task=29?= Message-ID: <3gq9qN5qlHz7LkY@mail.python.org> http://hg.python.org/cpython/rev/04cc9c32f3e3 changeset: 91147:04cc9c32f3e3 parent: 91145:9aba5d75ce94 parent: 91146:e0395363f22f user: Victor Stinner date: Thu Jun 12 18:39:42 2014 +0200 summary: (Merge 3.4) asyncio: Tulip issue 173: Enhance repr(Handle) and repr(Task) repr(Handle) is shorter for function: "foo" instead of "". It now also includes the source of the callback, filename and line number where it was defined, if available. repr(Task) now also includes the current position in the code, filename and line number, if available. If the coroutine (generator) is done, the line number is omitted and "done" is added. files: Lib/asyncio/events.py | 30 +++++- Lib/asyncio/tasks.py | 10 +- Lib/asyncio/test_utils.py | 7 + Lib/test/test_asyncio/test_events.py | 78 +++++++++++---- Lib/test/test_asyncio/test_tasks.py | 29 ++++- 5 files changed, 123 insertions(+), 31 deletions(-) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -8,9 +8,29 @@ 'get_child_watcher', 'set_child_watcher', ] +import functools +import inspect import subprocess import threading import socket +import sys + + +_PY34 = sys.version_info >= (3, 4) + +def _get_function_source(func): + if _PY34: + func = inspect.unwrap(func) + elif hasattr(func, '__wrapped__'): + func = func.__wrapped__ + if inspect.isfunction(func): + code = func.__code__ + return (code.co_filename, code.co_firstlineno) + if isinstance(func, functools.partial): + return _get_function_source(func.func) + if _PY34 and isinstance(func, functools.partialmethod): + return _get_function_source(func.func) + return None class Handle: @@ -26,7 +46,15 @@ self._cancelled = False def __repr__(self): - res = 'Handle({}, {})'.format(self._callback, self._args) + cb_repr = getattr(self._callback, '__qualname__', None) + if not cb_repr: + cb_repr = str(self._callback) + + source = _get_function_source(self._callback) + if source: + cb_repr += ' at %s:%s' % source + + res = 'Handle({}, {})'.format(cb_repr, self._args) if self._cancelled: res += '' return res diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -188,7 +188,15 @@ i = res.find('<') if i < 0: i = len(res) - res = res[:i] + '(<{}>)'.format(self._coro.__name__) + res[i:] + text = self._coro.__name__ + coro = self._coro + if inspect.isgenerator(coro): + filename = coro.gi_code.co_filename + if coro.gi_frame is not None: + text += ' at %s:%s' % (filename, coro.gi_frame.f_lineno) + else: + text += ' done at %s' % filename + res = res[:i] + '(<{}>)'.format(text) + res[i:] return res def get_stack(self, *, limit=None): 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 @@ -372,3 +372,10 @@ """ def __eq__(self, other): return bool(re.search(str(self), other, re.S)) + + +def get_function_source(func): + source = events._get_function_source(func) + if source is None: + raise ValueError("unable to get the source of %r" % (func,)) + return source 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 @@ -5,6 +5,7 @@ import io import os import platform +import re import signal import socket try: @@ -1737,52 +1738,46 @@ return asyncio.SelectorEventLoop(selectors.SelectSelector()) +def noop(): + pass + + class HandleTests(unittest.TestCase): + def setUp(self): + self.loop = None + def test_handle(self): def callback(*args): return args args = () - h = asyncio.Handle(callback, args, mock.Mock()) + h = asyncio.Handle(callback, args, self.loop) self.assertIs(h._callback, callback) self.assertIs(h._args, args) self.assertFalse(h._cancelled) - r = repr(h) - self.assertTrue(r.startswith( - 'Handle(' - '.callback')) - self.assertTrue(r.endswith('())')) - h.cancel() self.assertTrue(h._cancelled) - r = repr(h) - self.assertTrue(r.startswith( - 'Handle(' - '.callback')) - self.assertTrue(r.endswith('())'), r) - def test_handle_from_handle(self): def callback(*args): return args - m_loop = object() - h1 = asyncio.Handle(callback, (), loop=m_loop) + h1 = asyncio.Handle(callback, (), loop=self.loop) self.assertRaises( - AssertionError, asyncio.Handle, h1, (), m_loop) + AssertionError, asyncio.Handle, h1, (), self.loop) def test_callback_with_exception(self): def callback(): raise ValueError() - m_loop = mock.Mock() - m_loop.call_exception_handler = mock.Mock() + self.loop = mock.Mock() + self.loop.call_exception_handler = mock.Mock() - h = asyncio.Handle(callback, (), m_loop) + h = asyncio.Handle(callback, (), self.loop) h._run() - m_loop.call_exception_handler.assert_called_with({ + self.loop.call_exception_handler.assert_called_with({ 'message': test_utils.MockPattern('Exception in callback.*'), 'exception': mock.ANY, 'handle': h @@ -1790,9 +1785,50 @@ def test_handle_weakref(self): wd = weakref.WeakValueDictionary() - h = asyncio.Handle(lambda: None, (), object()) + h = asyncio.Handle(lambda: None, (), self.loop) wd['h'] = h # Would fail without __weakref__ slot. + def test_repr(self): + # simple function + h = asyncio.Handle(noop, (), self.loop) + src = test_utils.get_function_source(noop) + self.assertEqual(repr(h), + 'Handle(noop at %s:%s, ())' % src) + + # cancelled handle + h.cancel() + self.assertEqual(repr(h), + 'Handle(noop at %s:%s, ())' % src) + + # decorated function + cb = asyncio.coroutine(noop) + h = asyncio.Handle(cb, (), self.loop) + self.assertEqual(repr(h), + 'Handle(noop at %s:%s, ())' % src) + + # partial function + cb = functools.partial(noop) + h = asyncio.Handle(cb, (), self.loop) + filename, lineno = src + regex = (r'^Handle\(functools.partial\(' + r'\) at %s:%s, ' + r'\(\)\)$' % (re.escape(filename), lineno)) + self.assertRegex(repr(h), regex) + + # partial method + if sys.version_info >= (3, 4): + method = HandleTests.test_repr + cb = functools.partialmethod(method) + src = test_utils.get_function_source(method) + h = asyncio.Handle(cb, (), self.loop) + + filename, lineno = src + regex = (r'^Handle\(functools.partialmethod\(' + r', , \) at %s:%s, ' + r'\(\)\)$' % (re.escape(filename), lineno)) + self.assertRegex(repr(h), regex) + + class TimerTests(unittest.TestCase): 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 @@ -116,21 +116,30 @@ yield from [] return 'abc' + filename, lineno = test_utils.get_function_source(notmuch) + src = "%s:%s" % (filename, lineno) + t = asyncio.Task(notmuch(), loop=self.loop) t.add_done_callback(Dummy()) - self.assertEqual(repr(t), 'Task()') + self.assertEqual(repr(t), + 'Task()' % src) + t.cancel() # Does not take immediate effect! - self.assertEqual(repr(t), 'Task()') + self.assertEqual(repr(t), + 'Task()' % src) self.assertRaises(asyncio.CancelledError, self.loop.run_until_complete, t) - self.assertEqual(repr(t), 'Task()') + self.assertEqual(repr(t), + 'Task()' % filename) + t = asyncio.Task(notmuch(), loop=self.loop) self.loop.run_until_complete(t) - self.assertEqual(repr(t), "Task()") + self.assertEqual(repr(t), + "Task()" % filename) def test_task_repr_custom(self): @asyncio.coroutine - def coro(): + def notmuch(): pass class T(asyncio.Future): @@ -141,10 +150,14 @@ def __repr__(self): return super().__repr__() - gen = coro() + gen = notmuch() t = MyTask(gen, loop=self.loop) - self.assertEqual(repr(t), 'T[]()') - gen.close() + filename = gen.gi_code.co_filename + lineno = gen.gi_frame.f_lineno + # FIXME: check for the name "coro" instead of "notmuch" because + # @asyncio.coroutine drops the name of the wrapped function: + # http://bugs.python.org/issue21205 + self.assertEqual(repr(t), 'T[]()' % (filename, lineno)) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 00:38:33 2014 From: python-checkins at python.org (vinay.sajip) Date: Fri, 13 Jun 2014 00:38:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzA5?= =?utf-8?q?=3A_Improved_implementation_to_cover_the_frozen_module_case=2E?= Message-ID: <3gqKjP1zQFz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/bec6f18dd636 changeset: 91148:bec6f18dd636 branch: 3.4 parent: 91146:e0395363f22f user: Vinay Sajip date: Thu Jun 12 23:36:33 2014 +0100 summary: Issue #21709: Improved implementation to cover the frozen module case. files: Lib/logging/__init__.py | 62 +++++++++++++++------------- 1 files changed, 34 insertions(+), 28 deletions(-) diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -52,34 +52,6 @@ #--------------------------------------------------------------------------- # -# _srcfile is used when walking the stack to check when we've got the first -# caller stack frame. -# -if hasattr(sys, 'frozen'): - _srcfile = os.path.join('logging', '__init__.py') -else: - _srcfile = __file__ -_srcfile = os.path.normcase(_srcfile) - - -if hasattr(sys, '_getframe'): - currentframe = lambda: sys._getframe(3) -else: #pragma: no cover - def currentframe(): - """Return the frame object for the caller's stack frame.""" - try: - raise Exception - except Exception: - return sys.exc_info()[2].tb_frame.f_back - -# _srcfile is only used in conjunction with sys._getframe(). -# To provide compatibility with older versions of Python, set _srcfile -# to None if _getframe() is not available; this value will prevent -# findCaller() from being called. -#if not hasattr(sys, "_getframe"): -# _srcfile = None - -# #_startTime is used as the base when calculating the relative time of events # _startTime = time.time() @@ -172,6 +144,40 @@ finally: _releaseLock() +if hasattr(sys, '_getframe'): + currentframe = lambda: sys._getframe(3) +else: #pragma: no cover + def currentframe(): + """Return the frame object for the caller's stack frame.""" + try: + raise Exception + except Exception: + return sys.exc_info()[2].tb_frame.f_back + +# +# _srcfile is used when walking the stack to check when we've got the first +# caller stack frame, by skipping frames whose filename is that of this +# module's source. It therefore should contain the filename of this module's +# source file. +# +# Ordinarily we would use __file__ for this, but frozen modules don't always +# have __file__ set, for some reason (see Issue #21736). Thus, we get the +# filename from a handy code object from a function defined in this module. +# (There's no particular reason for picking addLevelName.) +# + +_srcfile = os.path.normcase(addLevelName.__code__.co_filename) + +# _srcfile is only used in conjunction with sys._getframe(). +# To provide compatibility with older versions of Python, set _srcfile +# to None if _getframe() is not available; this value will prevent +# findCaller() from being called. You can also do this if you want to avoid +# the overhead of fetching caller information, even when _getframe() is +# available. +#if not hasattr(sys, '_getframe'): +# _srcfile = None + + def _checkLevel(level): if isinstance(level, int): rv = level -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 00:38:34 2014 From: python-checkins at python.org (vinay.sajip) Date: Fri, 13 Jun 2014 00:38:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321709=3A_Merged_update_from_3=2E4=2E?= Message-ID: <3gqKjQ3jG3z7LqV@mail.python.org> http://hg.python.org/cpython/rev/bd44ad77013a changeset: 91149:bd44ad77013a parent: 91147:04cc9c32f3e3 parent: 91148:bec6f18dd636 user: Vinay Sajip date: Thu Jun 12 23:38:16 2014 +0100 summary: Issue #21709: Merged update from 3.4. files: Lib/logging/__init__.py | 62 +++++++++++++++------------- 1 files changed, 34 insertions(+), 28 deletions(-) diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -52,34 +52,6 @@ #--------------------------------------------------------------------------- # -# _srcfile is used when walking the stack to check when we've got the first -# caller stack frame. -# -if hasattr(sys, 'frozen'): - _srcfile = os.path.join('logging', '__init__.py') -else: - _srcfile = __file__ -_srcfile = os.path.normcase(_srcfile) - - -if hasattr(sys, '_getframe'): - currentframe = lambda: sys._getframe(3) -else: #pragma: no cover - def currentframe(): - """Return the frame object for the caller's stack frame.""" - try: - raise Exception - except Exception: - return sys.exc_info()[2].tb_frame.f_back - -# _srcfile is only used in conjunction with sys._getframe(). -# To provide compatibility with older versions of Python, set _srcfile -# to None if _getframe() is not available; this value will prevent -# findCaller() from being called. -#if not hasattr(sys, "_getframe"): -# _srcfile = None - -# #_startTime is used as the base when calculating the relative time of events # _startTime = time.time() @@ -172,6 +144,40 @@ finally: _releaseLock() +if hasattr(sys, '_getframe'): + currentframe = lambda: sys._getframe(3) +else: #pragma: no cover + def currentframe(): + """Return the frame object for the caller's stack frame.""" + try: + raise Exception + except Exception: + return sys.exc_info()[2].tb_frame.f_back + +# +# _srcfile is used when walking the stack to check when we've got the first +# caller stack frame, by skipping frames whose filename is that of this +# module's source. It therefore should contain the filename of this module's +# source file. +# +# Ordinarily we would use __file__ for this, but frozen modules don't always +# have __file__ set, for some reason (see Issue #21736). Thus, we get the +# filename from a handy code object from a function defined in this module. +# (There's no particular reason for picking addLevelName.) +# + +_srcfile = os.path.normcase(addLevelName.__code__.co_filename) + +# _srcfile is only used in conjunction with sys._getframe(). +# To provide compatibility with older versions of Python, set _srcfile +# to None if _getframe() is not available; this value will prevent +# findCaller() from being called. You can also do this if you want to avoid +# the overhead of fetching caller information, even when _getframe() is +# available. +#if not hasattr(sys, '_getframe'): +# _srcfile = None + + def _checkLevel(level): if isinstance(level, int): rv = level -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 01:41:44 2014 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 13 Jun 2014 01:41:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321711=3A_support_?= =?utf-8?q?for_=22site-python=22_directories_has_now_been_removed_from?= Message-ID: <3gqM6J3Frwz7LkF@mail.python.org> http://hg.python.org/cpython/rev/3852afce2ca3 changeset: 91150:3852afce2ca3 user: Antoine Pitrou date: Thu Jun 12 19:41:30 2014 -0400 summary: Issue #21711: support for "site-python" directories has now been removed from the site module (it was deprecated in 3.4). files: Doc/library/site.rst | 18 ++++++++---------- Lib/site.py | 15 ++++----------- Lib/test/test_site.py | 8 +++----- Misc/NEWS | 5 ++++- 4 files changed, 19 insertions(+), 27 deletions(-) diff --git a/Doc/library/site.rst b/Doc/library/site.rst --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -26,24 +26,23 @@ :option:`-S`. .. index:: - pair: site-python; directory pair: site-packages; directory It starts by constructing up to four directories from a head and a tail part. For the head part, it uses ``sys.prefix`` and ``sys.exec_prefix``; empty heads are skipped. For the tail part, it uses the empty string and then :file:`lib/site-packages` (on Windows) or -:file:`lib/python{X.Y}/site-packages` and then :file:`lib/site-python` (on -Unix and Macintosh). For each of the distinct head-tail combinations, it sees -if it refers to an existing directory, and if so, adds it to ``sys.path`` and -also inspects the newly added path for configuration files. +:file:`lib/python{X.Y}/site-packages` (on Unix and Macintosh). For each +of the distinct head-tail combinations, it sees if it refers to an existing +directory, and if so, adds it to ``sys.path`` and also inspects the newly +added path for configuration files. -.. deprecated-removed:: 3.4 3.5 - Support for the "site-python" directory will be removed in 3.5. +.. versionchanged:: 3.5 + Support for the "site-python" directory has been removed. If a file named "pyvenv.cfg" exists one directory above sys.executable, sys.prefix and sys.exec_prefix are set to that directory and -it is also checked for site-packages and site-python (sys.base_prefix and +it is also checked for site-packages (sys.base_prefix and sys.base_exec_prefix will always be the "real" prefixes of the Python installation). If "pyvenv.cfg" (a bootstrap configuration file) contains the key "include-system-site-packages" set to anything other than "false" @@ -195,8 +194,7 @@ .. function:: getsitepackages() - Return a list containing all global site-packages directories (and possibly - site-python). + Return a list containing all global site-packages directories. .. versionadded:: 3.2 diff --git a/Lib/site.py b/Lib/site.py --- a/Lib/site.py +++ b/Lib/site.py @@ -7,7 +7,7 @@ This will append site-specific paths to the module search path. On Unix (including Mac OSX), it starts with sys.prefix and sys.exec_prefix (if different) and appends -lib/python/site-packages as well as lib/site-python. +lib/python/site-packages. On other platforms (such as Windows), it tries each of the prefixes directly, as well as with lib/site-packages appended. The resulting directories, if they exist, are appended to sys.path, and @@ -15,7 +15,7 @@ If a file named "pyvenv.cfg" exists one directory above sys.executable, sys.prefix and sys.exec_prefix are set to that directory and -it is also checked for site-packages and site-python (sys.base_prefix and +it is also checked for site-packages (sys.base_prefix and sys.base_exec_prefix will always be the "real" prefixes of the Python installation). If "pyvenv.cfg" (a bootstrap configuration file) contains the key "include-system-site-packages" set to anything other than "false" @@ -285,8 +285,7 @@ return known_paths def getsitepackages(prefixes=None): - """Returns a list containing all global site-packages directories - (and possibly site-python). + """Returns a list containing all global site-packages directories. For each directory present in ``prefixes`` (or the global ``PREFIXES``), this function will find its `site-packages` subdirectory depending on the @@ -307,7 +306,6 @@ sitepackages.append(os.path.join(prefix, "lib", "python" + sys.version[:3], "site-packages")) - sitepackages.append(os.path.join(prefix, "lib", "site-python")) else: sitepackages.append(prefix) sitepackages.append(os.path.join(prefix, "lib", "site-packages")) @@ -323,14 +321,9 @@ return sitepackages def addsitepackages(known_paths, prefixes=None): - """Add site-packages (and possibly site-python) to sys.path""" + """Add site-packages to sys.path""" for sitedir in getsitepackages(prefixes): if os.path.isdir(sitedir): - if "site-python" in sitedir: - import warnings - warnings.warn('"site-python" directories will not be ' - 'supported in 3.5 anymore', - DeprecationWarning) addsitedir(sitedir, known_paths) return known_paths diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -235,20 +235,18 @@ # OS X framework builds site.PREFIXES = ['Python.framework'] dirs = site.getsitepackages() - self.assertEqual(len(dirs), 3) + self.assertEqual(len(dirs), 2) wanted = os.path.join('/Library', sysconfig.get_config_var("PYTHONFRAMEWORK"), sys.version[:3], 'site-packages') - self.assertEqual(dirs[2], wanted) + self.assertEqual(dirs[1], wanted) elif os.sep == '/': # OS X non-framwework builds, Linux, FreeBSD, etc - self.assertEqual(len(dirs), 2) + self.assertEqual(len(dirs), 1) wanted = os.path.join('xoxo', 'lib', 'python' + sys.version[:3], 'site-packages') self.assertEqual(dirs[0], wanted) - wanted = os.path.join('xoxo', 'lib', 'site-python') - self.assertEqual(dirs[1], wanted) else: # other platforms self.assertEqual(len(dirs), 2) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -92,7 +92,10 @@ Library ------- -- Issue 17552: new socket.sendfile() method allowing to send a file over a +- Issue #21711: support for "site-python" directories has now been removed + from the site module (it was deprecated in 3.4). + +- Issue #17552: new socket.sendfile() method allowing to send a file over a socket by using high-performance os.sendfile() on UNIX. Patch by Giampaolo Rodola'. -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Fri Jun 13 09:24:46 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 13 Jun 2014 09:24:46 +0200 Subject: [Python-checkins] Daily reference leaks (3852afce2ca3): sum=13 Message-ID: results for 3852afce2ca3 on branch "default" -------------------------------------------- test_asyncio leaked [4, 0, 0] memory blocks, sum=4 test_collections leaked [2, -2, 0] references, sum=0 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 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/reflog6juo91', '-x'] From python-checkins at python.org Fri Jun 13 14:56:09 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 13 Jun 2014 14:56:09 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2316136=3A_VMSError?= =?utf-8?q?_is_done=2C_bye_bye_VMS?= Message-ID: <3gqhkx16Tgz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/75b18ff66e41 changeset: 91151:75b18ff66e41 user: Victor Stinner date: Fri Jun 13 14:55:47 2014 +0200 summary: Issue #16136: VMSError is done, bye bye VMS files: Doc/library/exceptions.rst | 4 ---- 1 files changed, 0 insertions(+), 4 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -457,10 +457,6 @@ .. exception:: IOError -.. exception:: VMSError - - Only available on VMS. - .. exception:: WindowsError Only available on Windows. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 14:59:05 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 13 Jun 2014 14:59:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE2MTM2?= =?utf-8?q?=3A_VMSError_is_done=2C_bye_bye_VMS?= Message-ID: <3gqhpK1GGQz7LkJ@mail.python.org> http://hg.python.org/cpython/rev/48126f225f10 changeset: 91152:48126f225f10 branch: 3.4 parent: 91148:bec6f18dd636 user: Victor Stinner date: Fri Jun 13 14:58:48 2014 +0200 summary: Issue #16136: VMSError is done, bye bye VMS files: Doc/library/exceptions.rst | 4 ---- 1 files changed, 0 insertions(+), 4 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -457,10 +457,6 @@ .. exception:: IOError -.. exception:: VMSError - - Only available on VMS. - .. exception:: WindowsError Only available on Windows. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 14:59:06 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 13 Jun 2014 14:59:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_3=2E4_=28null_merge=29?= Message-ID: <3gqhpL5BFQz7LkJ@mail.python.org> http://hg.python.org/cpython/rev/3af408ef0e1a changeset: 91153:3af408ef0e1a parent: 91151:75b18ff66e41 parent: 91152:48126f225f10 user: Victor Stinner date: Fri Jun 13 14:58:55 2014 +0200 summary: Merge 3.4 (null merge) files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 16:39:48 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 13 Jun 2014 16:39:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?devguide=3A_Issue_=2321745=3A_Mention?= =?utf-8?q?_VS2010_SP1_as_a_solution_for_error_LNK1123=2E?= Message-ID: <3gql2X4gsVz7Ljk@mail.python.org> http://hg.python.org/devguide/rev/9794412fa62d changeset: 699:9794412fa62d user: Zachary Ware date: Fri Jun 13 09:35:02 2014 -0500 summary: Issue #21745: Mention VS2010 SP1 as a solution for error LNK1123. Patch by Ben Hoyt. files: setup.rst | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/setup.rst b/setup.rst --- a/setup.rst +++ b/setup.rst @@ -230,6 +230,12 @@ To use it for more than 28 days, one must register through a Windows Live account. +You'll also need to install the Visual Studio `Service Pack 1 (SP1) +`_. If you +don't install this service pack, you may receive errors like the following +during linking: ``LINK : fatal error LNK1123: failure during conversion to +COFF: file invalid or corrupt``. + Most Python versions prior to 3.3 use Microsoft Visual Studio 2008. You can download Microsoft Visual C++ 2008 Express Edition with SP1 from a new location yet to be determined. -- Repository URL: http://hg.python.org/devguide From python-checkins at python.org Fri Jun 13 16:39:57 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 13 Jun 2014 16:39:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzQ1?= =?utf-8?q?=3A_Mention_VS2010_SP1_as_a_solution_for_LNK1123_errors?= Message-ID: <3gql2j6tcmz7LkW@mail.python.org> http://hg.python.org/cpython/rev/10cca530d14c changeset: 91154:10cca530d14c branch: 3.4 parent: 91152:48126f225f10 user: Zachary Ware date: Fri Jun 13 09:38:50 2014 -0500 summary: Issue #21745: Mention VS2010 SP1 as a solution for LNK1123 errors files: PCbuild/readme.txt | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -17,6 +17,9 @@ Required for building Release configuration builds that make use of Profile Guided Optimization (PGO), on either platform. +Installing Service Pack 1 for Visual Studio 2010 is highly recommended +to avoid LNK1123 errors. + The official Python releases are built with PGO using Visual Studio 2010 Ultimate Edition. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 16:39:59 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 13 Jun 2014 16:39:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321745=3A_Merge_with_3=2E4?= Message-ID: <3gql2l1QXfz7Lkw@mail.python.org> http://hg.python.org/cpython/rev/b5b54073d495 changeset: 91155:b5b54073d495 parent: 91153:3af408ef0e1a parent: 91154:10cca530d14c user: Zachary Ware date: Fri Jun 13 09:39:24 2014 -0500 summary: Issue #21745: Merge with 3.4 files: PCbuild/readme.txt | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -17,6 +17,9 @@ Required for building Release configuration builds that make use of Profile Guided Optimization (PGO), on either platform. +Installing Service Pack 1 for Visual Studio 2010 is highly recommended +to avoid LNK1123 errors. + The official Python releases are built with PGO using Visual Studio 2010 Ultimate Edition. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 16:43:46 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 13 Jun 2014 16:43:46 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogUGVyIE1hcnRpbiBb?= =?utf-8?q?1=5D=2C_PGO_is_no_longer_used_for_official_releases=2E?= Message-ID: <3gql764cXhz7Ljg@mail.python.org> http://hg.python.org/cpython/rev/86a20231a080 changeset: 91156:86a20231a080 branch: 3.4 parent: 91154:10cca530d14c user: Zachary Ware date: Fri Jun 13 09:43:15 2014 -0500 summary: Per Martin [1], PGO is no longer used for official releases. [1] https://mail.python.org/pipermail/python-dev/2014-June/135018.html files: PCbuild/readme.txt | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -20,9 +20,6 @@ Installing Service Pack 1 for Visual Studio 2010 is highly recommended to avoid LNK1123 errors. -The official Python releases are built with PGO using Visual Studio 2010 -Ultimate Edition. - All you need to do to build is open the solution "pcbuild.sln" in Visual Studio, select the desired combination of configuration and platform, then build with "Build Solution" or the F7 keyboard shortcut. You can -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 16:43:47 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 13 Jun 2014 16:43:47 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gql776N15z7Lkb@mail.python.org> http://hg.python.org/cpython/rev/5876805aefa6 changeset: 91157:5876805aefa6 parent: 91155:b5b54073d495 parent: 91156:86a20231a080 user: Zachary Ware date: Fri Jun 13 09:43:32 2014 -0500 summary: Merge with 3.4 files: PCbuild/readme.txt | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -20,9 +20,6 @@ Installing Service Pack 1 for Visual Studio 2010 is highly recommended to avoid LNK1123 errors. -The official Python releases are built with PGO using Visual Studio 2010 -Ultimate Edition. - All you need to do to build is open the solution "pcbuild.sln" in Visual Studio, select the desired combination of configuration and platform, then build with "Build Solution" or the F7 keyboard shortcut. You can -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 20:24:23 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 13 Jun 2014 20:24:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIwMDQz?= =?utf-8?q?=3A_Add_direct_test_for_=5Fthread=2E?= Message-ID: <3gqr1g36mpz7LjP@mail.python.org> http://hg.python.org/cpython/rev/ef491d76ac70 changeset: 91158:ef491d76ac70 branch: 3.4 parent: 91156:86a20231a080 user: Terry Jan Reedy date: Fri Jun 13 14:23:43 2014 -0400 summary: Issue #20043: Add direct test for _thread. files: Lib/test/test_multiprocessing_main_handling.py | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_multiprocessing_main_handling.py b/Lib/test/test_multiprocessing_main_handling.py --- a/Lib/test/test_multiprocessing_main_handling.py +++ b/Lib/test/test_multiprocessing_main_handling.py @@ -1,4 +1,8 @@ # tests __main__ module handling in multiprocessing +from test import support +# Skip tests if _thread or _multiprocessing wasn't built. +support.import_module('_thread') +support.import_module('_multiprocessing') import importlib import importlib.machinery @@ -9,14 +13,11 @@ import os.path import py_compile -from test import support from test.script_helper import ( make_pkg, make_script, make_zip_pkg, make_zip_script, assert_python_ok, assert_python_failure, temp_dir, spawn_python, kill_python) -# Skip tests if _multiprocessing wasn't built. -_multiprocessing = support.import_module('_multiprocessing') # Look up which start methods are available to test import multiprocessing AVAILABLE_START_METHODS = set(multiprocessing.get_all_start_methods()) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 20:24:24 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 13 Jun 2014 20:24:24 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gqr1h5Cl2z7Ljk@mail.python.org> http://hg.python.org/cpython/rev/c678d5020eb0 changeset: 91159:c678d5020eb0 parent: 91157:5876805aefa6 parent: 91158:ef491d76ac70 user: Terry Jan Reedy date: Fri Jun 13 14:23:57 2014 -0400 summary: Merge with 3.4 files: Lib/test/test_multiprocessing_main_handling.py | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_multiprocessing_main_handling.py b/Lib/test/test_multiprocessing_main_handling.py --- a/Lib/test/test_multiprocessing_main_handling.py +++ b/Lib/test/test_multiprocessing_main_handling.py @@ -1,4 +1,8 @@ # tests __main__ module handling in multiprocessing +from test import support +# Skip tests if _thread or _multiprocessing wasn't built. +support.import_module('_thread') +support.import_module('_multiprocessing') import importlib import importlib.machinery @@ -9,14 +13,11 @@ import os.path import py_compile -from test import support from test.script_helper import ( make_pkg, make_script, make_zip_pkg, make_zip_script, assert_python_ok, assert_python_failure, temp_dir, spawn_python, kill_python) -# Skip tests if _multiprocessing wasn't built. -_multiprocessing = support.import_module('_multiprocessing') # Look up which start methods are available to test import multiprocessing AVAILABLE_START_METHODS = set(multiprocessing.get_all_start_methods()) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 20:48:37 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 13 Jun 2014 20:48:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE5NDkz?= =?utf-8?q?=3A_Refactor_ctypes_test_package=2E?= Message-ID: <3gqrYd593Nz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/6f63fff5c120 changeset: 91160:6f63fff5c120 branch: 3.4 parent: 91158:ef491d76ac70 user: Zachary Ware date: Fri Jun 13 13:44:39 2014 -0500 summary: Issue #19493: Refactor ctypes test package. Skipped tests are now marked as skipped, formerly commented-out or renamed-so-it-doesn't-look-like-a-test tests are uncommented, properly named, and unconditionally skipped, some tests that simply didn't run before are now able to run, and a few are split into multiple methods instead of skipping via 'return' in the middle of a method. Also, a couple of unused files are removed completely. files: Lib/ctypes/test/__init__.py | 10 +- Lib/ctypes/test/test_arrays.py | 26 +- Lib/ctypes/test/test_as_parameter.py | 6 +- Lib/ctypes/test/test_bitfields.py | 19 +- Lib/ctypes/test/test_buffers.py | 60 ++-- Lib/ctypes/test/test_bytes.py | 12 +- Lib/ctypes/test/test_byteswap.py | 3 +- Lib/ctypes/test/test_callbacks.py | 47 ++-- Lib/ctypes/test/test_cast.py | 15 +- Lib/ctypes/test/test_cfuncs.py | 9 +- Lib/ctypes/test/test_checkretval.py | 15 +- Lib/ctypes/test/test_errcheck.py | 19 - Lib/ctypes/test/test_find.py | 69 +++--- Lib/ctypes/test/test_functions.py | 70 +++--- Lib/ctypes/test/test_integers.py | 5 - Lib/ctypes/test/test_keeprefs.py | 3 +- Lib/ctypes/test/test_loading.py | 138 +++++++------ Lib/ctypes/test/test_macholib.py | 24 +- Lib/ctypes/test/test_memfunctions.py | 44 ++-- Lib/ctypes/test/test_numbers.py | 39 ++- Lib/ctypes/test/test_objects.py | 11 +- Lib/ctypes/test/test_parameters.py | 19 +- Lib/ctypes/test/test_prototypes.py | 84 +++---- Lib/ctypes/test/test_python_api.py | 33 +- Lib/ctypes/test/test_random_things.py | 27 +- Lib/ctypes/test/test_slicing.py | 67 +++--- Lib/ctypes/test/test_strings.py | 126 ++++++------ Lib/ctypes/test/test_structures.py | 23 +- Lib/ctypes/test/test_unicode.py | 81 +++---- Lib/ctypes/test/test_values.py | 114 ++++++---- Lib/ctypes/test/test_win32.py | 145 +++++++------ Lib/ctypes/test/test_wintypes.py | 8 +- Misc/NEWS | 3 + 33 files changed, 674 insertions(+), 700 deletions(-) diff --git a/Lib/ctypes/test/__init__.py b/Lib/ctypes/test/__init__.py --- a/Lib/ctypes/test/__init__.py +++ b/Lib/ctypes/test/__init__.py @@ -2,7 +2,15 @@ use_resources = [] -class ResourceDenied(Exception): +import ctypes +ctypes_symbols = dir(ctypes) + +def need_symbol(name): + return unittest.skipUnless(name in ctypes_symbols, + '{!r} is required'.format(name)) + + +class ResourceDenied(unittest.SkipTest): """Test skipped because it requested a disallowed resource. This is raised when a test calls requires() for a resource that diff --git a/Lib/ctypes/test/test_arrays.py b/Lib/ctypes/test/test_arrays.py --- a/Lib/ctypes/test/test_arrays.py +++ b/Lib/ctypes/test/test_arrays.py @@ -1,6 +1,8 @@ import unittest from ctypes import * +from ctypes.test import need_symbol + formats = "bBhHiIlLqQfd" formats = c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, \ @@ -98,20 +100,16 @@ self.assertEqual(sz[1:4:2], b"o") self.assertEqual(sz.value, b"foo") - try: - create_unicode_buffer - except NameError: - pass - else: - def test_from_addressW(self): - p = create_unicode_buffer("foo") - sz = (c_wchar * 3).from_address(addressof(p)) - self.assertEqual(sz[:], "foo") - self.assertEqual(sz[::], "foo") - self.assertEqual(sz[::-1], "oof") - self.assertEqual(sz[::3], "f") - self.assertEqual(sz[1:4:2], "o") - self.assertEqual(sz.value, "foo") + @need_symbol('create_unicode_buffer') + def test_from_addressW(self): + p = create_unicode_buffer("foo") + sz = (c_wchar * 3).from_address(addressof(p)) + self.assertEqual(sz[:], "foo") + self.assertEqual(sz[::], "foo") + self.assertEqual(sz[::-1], "oof") + self.assertEqual(sz[::3], "f") + self.assertEqual(sz[1:4:2], "o") + self.assertEqual(sz.value, "foo") def test_cache(self): # Array types are cached internally in the _ctypes extension, diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py --- a/Lib/ctypes/test/test_as_parameter.py +++ b/Lib/ctypes/test/test_as_parameter.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test dll = CDLL(_ctypes_test.__file__) @@ -17,11 +18,8 @@ def wrap(self, param): return param + @need_symbol('c_wchar') def test_wchar_parm(self): - try: - c_wchar - except NameError: - return f = dll._testfunc_i_bhilfd f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double] result = f(self.wrap(1), self.wrap("x"), self.wrap(3), self.wrap(4), self.wrap(5.0), self.wrap(6.0)) diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py --- a/Lib/ctypes/test/test_bitfields.py +++ b/Lib/ctypes/test/test_bitfields.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest import os @@ -127,20 +128,18 @@ result = self.fail_fields(("a", c_char, 1)) self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_char')) - try: - c_wchar - except NameError: - pass - else: - result = self.fail_fields(("a", c_wchar, 1)) - self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_wchar')) - class Dummy(Structure): _fields_ = [] result = self.fail_fields(("a", Dummy, 1)) self.assertEqual(result, (TypeError, 'bit fields not allowed for type Dummy')) + @need_symbol('c_wchar') + def test_c_wchar(self): + result = self.fail_fields(("a", c_wchar, 1)) + self.assertEqual(result, + (TypeError, 'bit fields not allowed for type c_wchar')) + def test_single_bitfield_size(self): for c_typ in int_types: result = self.fail_fields(("a", c_typ, -1)) @@ -240,7 +239,7 @@ _anonymous_ = ["_"] _fields_ = [("_", X)] - @unittest.skipUnless(hasattr(ctypes, "c_uint32"), "c_int32 is required") + @need_symbol('c_uint32') def test_uint32(self): class X(Structure): _fields_ = [("a", c_uint32, 32)] @@ -250,7 +249,7 @@ x.a = 0xFDCBA987 self.assertEqual(x.a, 0xFDCBA987) - @unittest.skipUnless(hasattr(ctypes, "c_uint64"), "c_int64 is required") + @need_symbol('c_uint64') def test_uint64(self): class X(Structure): _fields_ = [("a", c_uint64, 64)] diff --git a/Lib/ctypes/test/test_buffers.py b/Lib/ctypes/test/test_buffers.py --- a/Lib/ctypes/test/test_buffers.py +++ b/Lib/ctypes/test/test_buffers.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest class StringBufferTestCase(unittest.TestCase): @@ -24,39 +25,36 @@ self.assertEqual(len(bytearray(create_string_buffer(0))), 0) self.assertEqual(len(bytearray(create_string_buffer(1))), 1) - try: - c_wchar - except NameError: - pass - else: - def test_unicode_buffer(self): - b = create_unicode_buffer(32) - self.assertEqual(len(b), 32) - self.assertEqual(sizeof(b), 32 * sizeof(c_wchar)) - self.assertIs(type(b[0]), str) + @need_symbol('c_wchar') + def test_unicode_buffer(self): + b = create_unicode_buffer(32) + self.assertEqual(len(b), 32) + self.assertEqual(sizeof(b), 32 * sizeof(c_wchar)) + self.assertIs(type(b[0]), str) - b = create_unicode_buffer("abc") - self.assertEqual(len(b), 4) # trailing nul char - self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) - self.assertIs(type(b[0]), str) - self.assertEqual(b[0], "a") - self.assertEqual(b[:], "abc\0") - self.assertEqual(b[::], "abc\0") - self.assertEqual(b[::-1], "\0cba") - self.assertEqual(b[::2], "ac") - self.assertEqual(b[::5], "a") + b = create_unicode_buffer("abc") + self.assertEqual(len(b), 4) # trailing nul char + self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) + self.assertIs(type(b[0]), str) + self.assertEqual(b[0], "a") + self.assertEqual(b[:], "abc\0") + self.assertEqual(b[::], "abc\0") + self.assertEqual(b[::-1], "\0cba") + self.assertEqual(b[::2], "ac") + self.assertEqual(b[::5], "a") - def test_unicode_conversion(self): - b = create_unicode_buffer("abc") - self.assertEqual(len(b), 4) # trailing nul char - self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) - self.assertIs(type(b[0]), str) - self.assertEqual(b[0], "a") - self.assertEqual(b[:], "abc\0") - self.assertEqual(b[::], "abc\0") - self.assertEqual(b[::-1], "\0cba") - self.assertEqual(b[::2], "ac") - self.assertEqual(b[::5], "a") + @need_symbol('c_wchar') + def test_unicode_conversion(self): + b = create_unicode_buffer("abc") + self.assertEqual(len(b), 4) # trailing nul char + self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) + self.assertIs(type(b[0]), str) + self.assertEqual(b[0], "a") + self.assertEqual(b[:], "abc\0") + self.assertEqual(b[::], "abc\0") + self.assertEqual(b[::-1], "\0cba") + self.assertEqual(b[::2], "ac") + self.assertEqual(b[::5], "a") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_bytes.py b/Lib/ctypes/test/test_bytes.py --- a/Lib/ctypes/test/test_bytes.py +++ b/Lib/ctypes/test/test_bytes.py @@ -38,13 +38,13 @@ self.assertEqual(x.a, "abc") self.assertEqual(type(x.a), str) - if sys.platform == "win32": - def test_BSTR(self): - from _ctypes import _SimpleCData - class BSTR(_SimpleCData): - _type_ = "X" + @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + def test_BSTR(self): + from _ctypes import _SimpleCData + class BSTR(_SimpleCData): + _type_ = "X" - BSTR("abc") + BSTR("abc") if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_byteswap.py b/Lib/ctypes/test/test_byteswap.py --- a/Lib/ctypes/test/test_byteswap.py +++ b/Lib/ctypes/test/test_byteswap.py @@ -14,7 +14,8 @@ # For Structures and Unions, these types are created on demand. class Test(unittest.TestCase): - def X_test(self): + @unittest.skip('test disabled') + def test_X(self): print(sys.byteorder, file=sys.stderr) for i in range(32): bits = BITS() diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test class Callbacks(unittest.TestCase): @@ -88,9 +89,10 @@ # disabled: would now (correctly) raise a RuntimeWarning about # a memory leak. A callback function cannot return a non-integral # C type without causing a memory leak. -## def test_char_p(self): -## self.check_type(c_char_p, "abc") -## self.check_type(c_char_p, "def") + @unittest.skip('test disabled') + def test_char_p(self): + self.check_type(c_char_p, "abc") + self.check_type(c_char_p, "def") def test_pyobject(self): o = () @@ -142,13 +144,12 @@ CFUNCTYPE(None)(lambda x=Nasty(): None) -try: - WINFUNCTYPE -except NameError: - pass -else: - class StdcallCallbacks(Callbacks): + at need_symbol('WINFUNCTYPE') +class StdcallCallbacks(Callbacks): + try: functype = WINFUNCTYPE + except NameError: + pass ################################################################ @@ -178,7 +179,7 @@ from ctypes.util import find_library libc_path = find_library("c") if not libc_path: - return # cannot test + self.skipTest('could not find libc') libc = CDLL(libc_path) @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) @@ -190,23 +191,19 @@ libc.qsort(array, len(array), sizeof(c_int), cmp_func) self.assertEqual(array[:], [1, 5, 7, 33, 99]) - try: - WINFUNCTYPE - except NameError: - pass - else: - def test_issue_8959_b(self): - from ctypes.wintypes import BOOL, HWND, LPARAM + @need_symbol('WINFUNCTYPE') + def test_issue_8959_b(self): + from ctypes.wintypes import BOOL, HWND, LPARAM + global windowCount + windowCount = 0 + + @WINFUNCTYPE(BOOL, HWND, LPARAM) + def EnumWindowsCallbackFunc(hwnd, lParam): global windowCount - windowCount = 0 + windowCount += 1 + return True #Allow windows to keep enumerating - @WINFUNCTYPE(BOOL, HWND, LPARAM) - def EnumWindowsCallbackFunc(hwnd, lParam): - global windowCount - windowCount += 1 - return True #Allow windows to keep enumerating - - windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0) + windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0) def test_callback_register_int(self): # Issue #8275: buggy handling of callback args under Win64 diff --git a/Lib/ctypes/test/test_cast.py b/Lib/ctypes/test/test_cast.py --- a/Lib/ctypes/test/test_cast.py +++ b/Lib/ctypes/test/test_cast.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest import sys @@ -75,15 +76,11 @@ self.assertEqual(cast(cast(s, c_void_p), c_char_p).value, b"hiho") - try: - c_wchar_p - except NameError: - pass - else: - def test_wchar_p(self): - s = c_wchar_p("hiho") - self.assertEqual(cast(cast(s, c_void_p), c_wchar_p).value, - "hiho") + @need_symbol('c_wchar_p') + def test_wchar_p(self): + s = c_wchar_p("hiho") + self.assertEqual(cast(cast(s, c_void_p), c_wchar_p).value, + "hiho") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py --- a/Lib/ctypes/test/test_cfuncs.py +++ b/Lib/ctypes/test/test_cfuncs.py @@ -3,6 +3,7 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test @@ -193,7 +194,7 @@ try: WinDLL except NameError: - pass + def stdcall_dll(*_): pass else: class stdcall_dll(WinDLL): def __getattr__(self, name): @@ -203,9 +204,9 @@ setattr(self, name, func) return func - class stdcallCFunctions(CFunctions): - _dll = stdcall_dll(_ctypes_test.__file__) - pass + at need_symbol('WinDLL') +class stdcallCFunctions(CFunctions): + _dll = stdcall_dll(_ctypes_test.__file__) if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py --- a/Lib/ctypes/test/test_checkretval.py +++ b/Lib/ctypes/test/test_checkretval.py @@ -1,6 +1,7 @@ import unittest from ctypes import * +from ctypes.test import need_symbol class CHECKED(c_int): def _check_retval_(value): @@ -25,15 +26,11 @@ del dll._testfunc_p_p.restype self.assertEqual(42, dll._testfunc_p_p(42)) - try: - oledll - except NameError: - pass - else: - def test_oledll(self): - self.assertRaises(OSError, - oledll.oleaut32.CreateTypeLib2, - 0, None, None) + @need_symbol('oledll') + def test_oledll(self): + self.assertRaises(OSError, + oledll.oleaut32.CreateTypeLib2, + 0, None, None) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_errcheck.py b/Lib/ctypes/test/test_errcheck.py deleted file mode 100644 --- a/Lib/ctypes/test/test_errcheck.py +++ /dev/null @@ -1,19 +0,0 @@ -import sys -from ctypes import * - -##class HMODULE(Structure): -## _fields_ = [("value", c_void_p)] - -## def __repr__(self): -## return "" % self.value - -##windll.kernel32.GetModuleHandleA.restype = HMODULE - -##print windll.kernel32.GetModuleHandleA("python23.dll") -##print hex(sys.dllhandle) - -##def nonzero(handle): -## return (GetLastError(), handle) - -##windll.kernel32.GetModuleHandleA.errcheck = nonzero -##print windll.kernel32.GetModuleHandleA("spam") diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py --- a/Lib/ctypes/test/test_find.py +++ b/Lib/ctypes/test/test_find.py @@ -1,4 +1,5 @@ import unittest +import os import sys from ctypes import * from ctypes.util import find_library @@ -40,43 +41,43 @@ except OSError: pass - if lib_gl: - def test_gl(self): - if self.gl: - self.gl.glClearIndex + @unittest.skipUnless(lib_gl, 'lib_gl not available') + def test_gl(self): + if self.gl: + self.gl.glClearIndex - if lib_glu: - def test_glu(self): - if self.glu: - self.glu.gluBeginCurve + @unittest.skipUnless(lib_glu, 'lib_glu not available') + def test_glu(self): + if self.glu: + self.glu.gluBeginCurve - if lib_gle: - def test_gle(self): - if self.gle: - self.gle.gleGetJoinStyle + @unittest.skipUnless(lib_gle, 'lib_gle not available') + def test_gle(self): + if self.gle: + self.gle.gleGetJoinStyle -##if os.name == "posix" and sys.platform != "darwin": - -## # On platforms where the default shared library suffix is '.so', -## # at least some libraries can be loaded as attributes of the cdll -## # object, since ctypes now tries loading the lib again -## # with '.so' appended of the first try fails. -## # -## # Won't work for libc, unfortunately. OTOH, it isn't -## # needed for libc since this is already mapped into the current -## # process (?) -## # -## # On MAC OSX, it won't work either, because dlopen() needs a full path, -## # and the default suffix is either none or '.dylib'. - -## class LoadLibs(unittest.TestCase): -## def test_libm(self): -## import math -## libm = cdll.libm -## sqrt = libm.sqrt -## sqrt.argtypes = (c_double,) -## sqrt.restype = c_double -## self.assertEqual(sqrt(2), math.sqrt(2)) +# On platforms where the default shared library suffix is '.so', +# at least some libraries can be loaded as attributes of the cdll +# object, since ctypes now tries loading the lib again +# with '.so' appended of the first try fails. +# +# Won't work for libc, unfortunately. OTOH, it isn't +# needed for libc since this is already mapped into the current +# process (?) +# +# On MAC OSX, it won't work either, because dlopen() needs a full path, +# and the default suffix is either none or '.dylib'. + at unittest.skip('test disabled') + at unittest.skipUnless(os.name=="posix" and sys.platform != "darwin", + 'test not suitable for this platform') +class LoadLibs(unittest.TestCase): + def test_libm(self): + import math + libm = cdll.libm + sqrt = libm.sqrt + sqrt.argtypes = (c_double,) + sqrt.restype = c_double + self.assertEqual(sqrt(2), math.sqrt(2)) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -6,6 +6,7 @@ """ from ctypes import * +from ctypes.test import need_symbol import sys, unittest try: @@ -63,22 +64,16 @@ pass + @need_symbol('c_wchar') def test_wchar_parm(self): - try: - c_wchar - except NameError: - return f = dll._testfunc_i_bhilfd f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double] result = f(1, "x", 3, 4, 5.0, 6.0) self.assertEqual(result, 139) self.assertEqual(type(result), int) + @need_symbol('c_wchar') def test_wchar_result(self): - try: - c_wchar - except NameError: - return f = dll._testfunc_i_bhilfd f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double] f.restype = c_wchar @@ -155,11 +150,8 @@ self.assertEqual(result, -21) self.assertEqual(type(result), float) + @need_symbol('c_longlong') def test_longlongresult(self): - try: - c_longlong - except NameError: - return f = dll._testfunc_q_bhilfd f.restype = c_longlong f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double] @@ -296,6 +288,7 @@ result = f(-10, cb) self.assertEqual(result, -18) + @need_symbol('c_longlong') def test_longlong_callbacks(self): f = dll._testfunc_callback_q_qf @@ -348,16 +341,16 @@ s2h = dll.ret_2h_func(inp) self.assertEqual((s2h.x, s2h.y), (99*2, 88*3)) - if sys.platform == "win32": - def test_struct_return_2H_stdcall(self): - class S2H(Structure): - _fields_ = [("x", c_short), - ("y", c_short)] + @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + def test_struct_return_2H_stdcall(self): + class S2H(Structure): + _fields_ = [("x", c_short), + ("y", c_short)] - windll.s_ret_2h_func.restype = S2H - windll.s_ret_2h_func.argtypes = [S2H] - s2h = windll.s_ret_2h_func(S2H(99, 88)) - self.assertEqual((s2h.x, s2h.y), (99*2, 88*3)) + windll.s_ret_2h_func.restype = S2H + windll.s_ret_2h_func.argtypes = [S2H] + s2h = windll.s_ret_2h_func(S2H(99, 88)) + self.assertEqual((s2h.x, s2h.y), (99*2, 88*3)) def test_struct_return_8H(self): class S8I(Structure): @@ -376,23 +369,24 @@ self.assertEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) - if sys.platform == "win32": - def test_struct_return_8H_stdcall(self): - class S8I(Structure): - _fields_ = [("a", c_int), - ("b", c_int), - ("c", c_int), - ("d", c_int), - ("e", c_int), - ("f", c_int), - ("g", c_int), - ("h", c_int)] - windll.s_ret_8i_func.restype = S8I - windll.s_ret_8i_func.argtypes = [S8I] - inp = S8I(9, 8, 7, 6, 5, 4, 3, 2) - s8i = windll.s_ret_8i_func(inp) - self.assertEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), - (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) + @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + def test_struct_return_8H_stdcall(self): + class S8I(Structure): + _fields_ = [("a", c_int), + ("b", c_int), + ("c", c_int), + ("d", c_int), + ("e", c_int), + ("f", c_int), + ("g", c_int), + ("h", c_int)] + windll.s_ret_8i_func.restype = S8I + windll.s_ret_8i_func.argtypes = [S8I] + inp = S8I(9, 8, 7, 6, 5, 4, 3, 2) + s8i = windll.s_ret_8i_func(inp) + self.assertEqual( + (s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), + (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) def test_sf1651235(self): # see http://www.python.org/sf/1651235 diff --git a/Lib/ctypes/test/test_integers.py b/Lib/ctypes/test/test_integers.py deleted file mode 100644 --- a/Lib/ctypes/test/test_integers.py +++ /dev/null @@ -1,5 +0,0 @@ -# superseded by test_numbers.py -import unittest - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/ctypes/test/test_keeprefs.py b/Lib/ctypes/test/test_keeprefs.py --- a/Lib/ctypes/test/test_keeprefs.py +++ b/Lib/ctypes/test/test_keeprefs.py @@ -94,7 +94,8 @@ self.assertEqual(x._objects, {'1': i}) class DeletePointerTestCase(unittest.TestCase): - def X_test(self): + @unittest.skip('test disabled') + def test_X(self): class X(Structure): _fields_ = [("p", POINTER(c_char_p))] x = X() diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -21,18 +21,21 @@ unknowndll = "xxrandomnamexx" - if libc_name is not None: - def test_load(self): - CDLL(libc_name) - CDLL(os.path.basename(libc_name)) - self.assertRaises(OSError, CDLL, self.unknowndll) + @unittest.skipUnless(libc_name is not None, 'could not find libc') + def test_load(self): + CDLL(libc_name) + CDLL(os.path.basename(libc_name)) + self.assertRaises(OSError, CDLL, self.unknowndll) - if libc_name is not None and os.path.basename(libc_name) == "libc.so.6": - def test_load_version(self): - cdll.LoadLibrary("libc.so.6") - # linux uses version, libc 9 should not exist - self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9") - self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll) + @unittest.skipUnless(libc_name is not None, 'could not find libc') + @unittest.skipUnless(libc_name is not None and + os.path.basename(libc_name) == "libc.so.6", + 'wrong libc path for test') + def test_load_version(self): + cdll.LoadLibrary("libc.so.6") + # linux uses version, libc 9 should not exist + self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9") + self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll) def test_find(self): for name in ("c", "m"): @@ -41,66 +44,71 @@ cdll.LoadLibrary(lib) CDLL(lib) - if os.name in ("nt", "ce"): - def test_load_library(self): - self.assertIsNotNone(libc_name) - if is_resource_enabled("printing"): - print(find_library("kernel32")) - print(find_library("user32")) + @unittest.skipUnless(os.name in ("nt", "ce"), + 'test specific to Windows (NT/CE)') + def test_load_library(self): + self.assertIsNotNone(libc_name) + if is_resource_enabled("printing"): + print(find_library("kernel32")) + print(find_library("user32")) - if os.name == "nt": - windll.kernel32.GetModuleHandleW - windll["kernel32"].GetModuleHandleW - windll.LoadLibrary("kernel32").GetModuleHandleW - WinDLL("kernel32").GetModuleHandleW - elif os.name == "ce": - windll.coredll.GetModuleHandleW - windll["coredll"].GetModuleHandleW - windll.LoadLibrary("coredll").GetModuleHandleW - WinDLL("coredll").GetModuleHandleW + if os.name == "nt": + windll.kernel32.GetModuleHandleW + windll["kernel32"].GetModuleHandleW + windll.LoadLibrary("kernel32").GetModuleHandleW + WinDLL("kernel32").GetModuleHandleW + elif os.name == "ce": + windll.coredll.GetModuleHandleW + windll["coredll"].GetModuleHandleW + windll.LoadLibrary("coredll").GetModuleHandleW + WinDLL("coredll").GetModuleHandleW - def test_load_ordinal_functions(self): - import _ctypes_test - dll = WinDLL(_ctypes_test.__file__) - # We load the same function both via ordinal and name - func_ord = dll[2] - func_name = dll.GetString - # addressof gets the address where the function pointer is stored - a_ord = addressof(func_ord) - a_name = addressof(func_name) - f_ord_addr = c_void_p.from_address(a_ord).value - f_name_addr = c_void_p.from_address(a_name).value - self.assertEqual(hex(f_ord_addr), hex(f_name_addr)) + @unittest.skipUnless(os.name in ("nt", "ce"), + 'test specific to Windows (NT/CE)') + def test_load_ordinal_functions(self): + import _ctypes_test + dll = WinDLL(_ctypes_test.__file__) + # We load the same function both via ordinal and name + func_ord = dll[2] + func_name = dll.GetString + # addressof gets the address where the function pointer is stored + a_ord = addressof(func_ord) + a_name = addressof(func_name) + f_ord_addr = c_void_p.from_address(a_ord).value + f_name_addr = c_void_p.from_address(a_name).value + self.assertEqual(hex(f_ord_addr), hex(f_name_addr)) - self.assertRaises(AttributeError, dll.__getitem__, 1234) + self.assertRaises(AttributeError, dll.__getitem__, 1234) - if os.name == "nt": - def test_1703286_A(self): - from _ctypes import LoadLibrary, FreeLibrary - # On winXP 64-bit, advapi32 loads at an address that does - # NOT fit into a 32-bit integer. FreeLibrary must be able - # to accept this address. + @unittest.skipUnless(os.name == "nt", 'Windows-specific test') + def test_1703286_A(self): + from _ctypes import LoadLibrary, FreeLibrary + # On winXP 64-bit, advapi32 loads at an address that does + # NOT fit into a 32-bit integer. FreeLibrary must be able + # to accept this address. - # These are tests for http://www.python.org/sf/1703286 - handle = LoadLibrary("advapi32") - FreeLibrary(handle) + # These are tests for http://www.python.org/sf/1703286 + handle = LoadLibrary("advapi32") + FreeLibrary(handle) - def test_1703286_B(self): - # Since on winXP 64-bit advapi32 loads like described - # above, the (arbitrarily selected) CloseEventLog function - # also has a high address. 'call_function' should accept - # addresses so large. - from _ctypes import call_function - advapi32 = windll.advapi32 - # Calling CloseEventLog with a NULL argument should fail, - # but the call should not segfault or so. - self.assertEqual(0, advapi32.CloseEventLog(None)) - windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p - windll.kernel32.GetProcAddress.restype = c_void_p - proc = windll.kernel32.GetProcAddress(advapi32._handle, b"CloseEventLog") - self.assertTrue(proc) - # This is the real test: call the function via 'call_function' - self.assertEqual(0, call_function(proc, (None,))) + @unittest.skipUnless(os.name == "nt", 'Windows-specific test') + def test_1703286_B(self): + # Since on winXP 64-bit advapi32 loads like described + # above, the (arbitrarily selected) CloseEventLog function + # also has a high address. 'call_function' should accept + # addresses so large. + from _ctypes import call_function + advapi32 = windll.advapi32 + # Calling CloseEventLog with a NULL argument should fail, + # but the call should not segfault or so. + self.assertEqual(0, advapi32.CloseEventLog(None)) + windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p + windll.kernel32.GetProcAddress.restype = c_void_p + proc = windll.kernel32.GetProcAddress(advapi32._handle, + b"CloseEventLog") + self.assertTrue(proc) + # This is the real test: call the function via 'call_function' + self.assertEqual(0, call_function(proc, (None,))) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_macholib.py b/Lib/ctypes/test/test_macholib.py --- a/Lib/ctypes/test/test_macholib.py +++ b/Lib/ctypes/test/test_macholib.py @@ -43,21 +43,21 @@ raise ValueError("%s not found" % (name,)) class MachOTest(unittest.TestCase): - if sys.platform == "darwin": - def test_find(self): + @unittest.skipUnless(sys.platform == "darwin", 'OSX-specific test') + def test_find(self): - self.assertEqual(find_lib('pthread'), - '/usr/lib/libSystem.B.dylib') + self.assertEqual(find_lib('pthread'), + '/usr/lib/libSystem.B.dylib') - result = find_lib('z') - # Issue #21093: dyld default search path includes $HOME/lib and - # /usr/local/lib before /usr/lib, which caused test failures if - # a local copy of libz exists in one of them. Now ignore the head - # of the path. - self.assertRegex(result, r".*/lib/libz\..*.*\.dylib") + result = find_lib('z') + # Issue #21093: dyld default search path includes $HOME/lib and + # /usr/local/lib before /usr/lib, which caused test failures if + # a local copy of libz exists in one of them. Now ignore the head + # of the path. + self.assertRegex(result, r".*/lib/libz\..*.*\.dylib") - self.assertEqual(find_lib('IOKit'), - '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit') + self.assertEqual(find_lib('IOKit'), + '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit') if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_memfunctions.py b/Lib/ctypes/test/test_memfunctions.py --- a/Lib/ctypes/test/test_memfunctions.py +++ b/Lib/ctypes/test/test_memfunctions.py @@ -2,17 +2,19 @@ from test import support import unittest from ctypes import * +from ctypes.test import need_symbol class MemFunctionsTest(unittest.TestCase): -## def test_overflow(self): -## # string_at and wstring_at must use the Python calling -## # convention (which acquires the GIL and checks the Python -## # error flag). Provoke an error and catch it; see also issue -## # #3554: -## self.assertRaises((OverflowError, MemoryError, SystemError), -## lambda: wstring_at(u"foo", sys.maxint - 1)) -## self.assertRaises((OverflowError, MemoryError, SystemError), -## lambda: string_at("foo", sys.maxint - 1)) + @unittest.skip('test disabled') + def test_overflow(self): + # string_at and wstring_at must use the Python calling + # convention (which acquires the GIL and checks the Python + # error flag). Provoke an error and catch it; see also issue + # #3554: + self.assertRaises((OverflowError, MemoryError, SystemError), + lambda: wstring_at(u"foo", sys.maxint - 1)) + self.assertRaises((OverflowError, MemoryError, SystemError), + lambda: string_at("foo", sys.maxint - 1)) def test_memmove(self): # large buffers apparently increase the chance that the memory @@ -61,21 +63,17 @@ self.assertEqual(string_at(b"foo bar", 7), b"foo bar") self.assertEqual(string_at(b"foo bar", 3), b"foo") - try: - create_unicode_buffer - except NameError: - pass - else: - def test_wstring_at(self): - p = create_unicode_buffer("Hello, World") - a = create_unicode_buffer(1000000) - result = memmove(a, p, len(p) * sizeof(c_wchar)) - self.assertEqual(a.value, "Hello, World") + @need_symbol('create_unicode_buffer') + def test_wstring_at(self): + p = create_unicode_buffer("Hello, World") + a = create_unicode_buffer(1000000) + result = memmove(a, p, len(p) * sizeof(c_wchar)) + self.assertEqual(a.value, "Hello, World") - self.assertEqual(wstring_at(a), "Hello, World") - self.assertEqual(wstring_at(a, 5), "Hello") - self.assertEqual(wstring_at(a, 16), "Hello, World\0\0\0\0") - self.assertEqual(wstring_at(a, 0), "") + self.assertEqual(wstring_at(a), "Hello, World") + self.assertEqual(wstring_at(a, 5), "Hello") + self.assertEqual(wstring_at(a, 16), "Hello, World\0\0\0\0") + self.assertEqual(wstring_at(a, 0), "") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_numbers.py b/Lib/ctypes/test/test_numbers.py --- a/Lib/ctypes/test/test_numbers.py +++ b/Lib/ctypes/test/test_numbers.py @@ -82,12 +82,13 @@ self.assertRaises(TypeError, t, "") self.assertRaises(TypeError, t, None) -## def test_valid_ranges(self): -## # invalid values of the correct type -## # raise ValueError (not OverflowError) -## for t, (l, h) in zip(unsigned_types, unsigned_ranges): -## self.assertRaises(ValueError, t, l-1) -## self.assertRaises(ValueError, t, h+1) + @unittest.skip('test disabled') + def test_valid_ranges(self): + # invalid values of the correct type + # raise ValueError (not OverflowError) + for t, (l, h) in zip(unsigned_types, unsigned_ranges): + self.assertRaises(ValueError, t, l-1) + self.assertRaises(ValueError, t, h+1) def test_from_param(self): # the from_param class method attribute always @@ -200,16 +201,17 @@ self.assertEqual(v.value, b'?') # array does not support c_bool / 't' - # def test_bool_from_address(self): - # from ctypes import c_bool - # from array import array - # a = array(c_bool._type_, [True]) - # v = t.from_address(a.buffer_info()[0]) - # self.assertEqual(v.value, a[0]) - # self.assertEqual(type(v) is t) - # a[0] = False - # self.assertEqual(v.value, a[0]) - # self.assertEqual(type(v) is t) + @unittest.skip('test disabled') + def test_bool_from_address(self): + from ctypes import c_bool + from array import array + a = array(c_bool._type_, [True]) + v = t.from_address(a.buffer_info()[0]) + self.assertEqual(v.value, a[0]) + self.assertEqual(type(v) is t) + a[0] = False + self.assertEqual(v.value, a[0]) + self.assertEqual(type(v) is t) def test_init(self): # c_int() can be initialized from Python's int, and c_int. @@ -227,8 +229,9 @@ if (hasattr(t, "__ctype_le__")): self.assertRaises(OverflowError, t.__ctype_le__, big_int) -## def test_perf(self): -## check_perf() + @unittest.skip('test disabled') + def test_perf(self): + check_perf() from ctypes import _SimpleCData class c_int_S(_SimpleCData): diff --git a/Lib/ctypes/test/test_objects.py b/Lib/ctypes/test/test_objects.py --- a/Lib/ctypes/test/test_objects.py +++ b/Lib/ctypes/test/test_objects.py @@ -59,12 +59,9 @@ import ctypes.test.test_objects class TestCase(unittest.TestCase): - if sys.hexversion > 0x02040000: - # Python 2.3 has no ELLIPSIS flag, so we don't test with this - # version: - def test(self): - doctest.testmod(ctypes.test.test_objects) + def test(self): + failures, tests = doctest.testmod(ctypes.test.test_objects) + self.assertFalse(failures, 'doctests failed, see output above') if __name__ == '__main__': - if sys.hexversion > 0x02040000: - doctest.testmod(ctypes.test.test_objects) + doctest.testmod(ctypes.test.test_objects) diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -1,4 +1,5 @@ import unittest, sys +from ctypes.test import need_symbol class SimpleTypesTestCase(unittest.TestCase): @@ -35,10 +36,9 @@ self.assertEqual(CVOIDP.from_param("abc"), "abcabc") self.assertEqual(CCHARP.from_param("abc"), "abcabcabcabc") - try: - from ctypes import c_wchar_p - except ImportError: - return + @need_symbol('c_wchar_p') + def test_subclasses_c_wchar_p(self): + from ctypes import c_wchar_p class CWCHARP(c_wchar_p): def from_param(cls, value): @@ -66,13 +66,9 @@ a = c_char_p(b"123") self.assertIs(c_char_p.from_param(a), a) + @need_symbol('c_wchar_p') def test_cw_strings(self): - from ctypes import byref - try: - from ctypes import c_wchar_p - except ImportError: -## print "(No c_wchar_p)" - return + from ctypes import byref, c_wchar_p c_wchar_p.from_param("123") @@ -139,9 +135,6 @@ self.assertRaises(TypeError, LPINT.from_param, c_long*3) self.assertRaises(TypeError, LPINT.from_param, c_uint*3) -## def test_performance(self): -## check_perf() - def test_noctypes_argtype(self): import _ctypes_test from ctypes import CDLL, c_void_p, ArgumentError diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py --- a/Lib/ctypes/test/test_prototypes.py +++ b/Lib/ctypes/test/test_prototypes.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest # IMPORTANT INFO: @@ -135,13 +136,14 @@ func(pointer(c_int())) func((c_int * 3)()) - try: - func.restype = c_wchar_p - except NameError: - pass - else: - self.assertEqual(None, func(c_wchar_p(None))) - self.assertEqual("123", func(c_wchar_p("123"))) + @need_symbol('c_wchar_p') + def test_c_void_p_arg_with_c_wchar_p(self): + func = testdll._testfunc_p_p + func.restype = c_wchar_p + func.argtypes = c_void_p, + + self.assertEqual(None, func(c_wchar_p(None))) + self.assertEqual("123", func(c_wchar_p("123"))) def test_instance(self): func = testdll._testfunc_p_p @@ -156,51 +158,47 @@ func.argtypes = None self.assertEqual(None, func(X())) -try: - c_wchar -except NameError: - pass -else: - class WCharPointersTestCase(unittest.TestCase): + at need_symbol('c_wchar') +class WCharPointersTestCase(unittest.TestCase): - def setUp(self): - func = testdll._testfunc_p_p - func.restype = c_int - func.argtypes = None + def setUp(self): + func = testdll._testfunc_p_p + func.restype = c_int + func.argtypes = None - def test_POINTER_c_wchar_arg(self): - func = testdll._testfunc_p_p - func.restype = c_wchar_p - func.argtypes = POINTER(c_wchar), + def test_POINTER_c_wchar_arg(self): + func = testdll._testfunc_p_p + func.restype = c_wchar_p + func.argtypes = POINTER(c_wchar), - self.assertEqual(None, func(None)) - self.assertEqual("123", func("123")) - self.assertEqual(None, func(c_wchar_p(None))) - self.assertEqual("123", func(c_wchar_p("123"))) + self.assertEqual(None, func(None)) + self.assertEqual("123", func("123")) + self.assertEqual(None, func(c_wchar_p(None))) + self.assertEqual("123", func(c_wchar_p("123"))) - self.assertEqual("123", func(c_wbuffer("123"))) - ca = c_wchar("a") - self.assertEqual("a", func(pointer(ca))[0]) - self.assertEqual("a", func(byref(ca))[0]) + self.assertEqual("123", func(c_wbuffer("123"))) + ca = c_wchar("a") + self.assertEqual("a", func(pointer(ca))[0]) + self.assertEqual("a", func(byref(ca))[0]) - def test_c_wchar_p_arg(self): - func = testdll._testfunc_p_p - func.restype = c_wchar_p - func.argtypes = c_wchar_p, + def test_c_wchar_p_arg(self): + func = testdll._testfunc_p_p + func.restype = c_wchar_p + func.argtypes = c_wchar_p, - c_wchar_p.from_param("123") + c_wchar_p.from_param("123") - self.assertEqual(None, func(None)) - self.assertEqual("123", func("123")) - self.assertEqual(None, func(c_wchar_p(None))) - self.assertEqual("123", func(c_wchar_p("123"))) + self.assertEqual(None, func(None)) + self.assertEqual("123", func("123")) + self.assertEqual(None, func(c_wchar_p(None))) + self.assertEqual("123", func(c_wchar_p("123"))) - # XXX Currently, these raise TypeErrors, although they shouldn't: - self.assertEqual("123", func(c_wbuffer("123"))) - ca = c_wchar("a") - self.assertEqual("a", func(pointer(ca))[0]) - self.assertEqual("a", func(byref(ca))[0]) + # XXX Currently, these raise TypeErrors, although they shouldn't: + self.assertEqual("123", func(c_wbuffer("123"))) + ca = c_wchar("a") + self.assertEqual("a", func(pointer(ca))[0]) + self.assertEqual("a", func(byref(ca))[0]) class ArrayTest(unittest.TestCase): def test(self): diff --git a/Lib/ctypes/test/test_python_api.py b/Lib/ctypes/test/test_python_api.py --- a/Lib/ctypes/test/test_python_api.py +++ b/Lib/ctypes/test/test_python_api.py @@ -1,7 +1,7 @@ from ctypes import * import unittest, sys from test import support -from ctypes.test import is_resource_enabled +from ctypes.test import requires ################################################################ # This section should be moved into ctypes\__init__.py, when it's ready. @@ -39,24 +39,25 @@ del pyob self.assertEqual(grc(s), refcnt) - if is_resource_enabled("refcount"): - # This test is unreliable, because it is possible that code in - # unittest changes the refcount of the '42' integer. So, it - # is disabled by default. - def test_PyLong_Long(self): - ref42 = grc(42) - pythonapi.PyLong_FromLong.restype = py_object - self.assertEqual(pythonapi.PyLong_FromLong(42), 42) + # This test is unreliable, because it is possible that code in + # unittest changes the refcount of the '42' integer. So, it + # is disabled by default. + @requires("refcount") + @support.refcount_test + def test_PyLong_Long(self): + ref42 = grc(42) + pythonapi.PyLong_FromLong.restype = py_object + self.assertEqual(pythonapi.PyLong_FromLong(42), 42) - self.assertEqual(grc(42), ref42) + self.assertEqual(grc(42), ref42) - pythonapi.PyLong_AsLong.argtypes = (py_object,) - pythonapi.PyLong_AsLong.restype = c_long + pythonapi.PyLong_AsLong.argtypes = (py_object,) + pythonapi.PyLong_AsLong.restype = c_long - res = pythonapi.PyLong_AsLong(42) - self.assertEqual(grc(res), ref42 + 1) - del res - self.assertEqual(grc(42), ref42) + res = pythonapi.PyLong_AsLong(42) + self.assertEqual(grc(res), ref42 + 1) + del res + self.assertEqual(grc(42), ref42) @support.refcount_test def test_PyObj_FromPtr(self): diff --git a/Lib/ctypes/test/test_random_things.py b/Lib/ctypes/test/test_random_things.py --- a/Lib/ctypes/test/test_random_things.py +++ b/Lib/ctypes/test/test_random_things.py @@ -5,23 +5,22 @@ 42 / arg raise ValueError(arg) -if sys.platform == "win32": + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class call_function_TestCase(unittest.TestCase): + # _ctypes.call_function is deprecated and private, but used by + # Gary Bishp's readline module. If we have it, we must test it as well. - class call_function_TestCase(unittest.TestCase): - # _ctypes.call_function is deprecated and private, but used by - # Gary Bishp's readline module. If we have it, we must test it as well. + def test(self): + from _ctypes import call_function + windll.kernel32.LoadLibraryA.restype = c_void_p + windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p + windll.kernel32.GetProcAddress.restype = c_void_p - def test(self): - from _ctypes import call_function - windll.kernel32.LoadLibraryA.restype = c_void_p - windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p - windll.kernel32.GetProcAddress.restype = c_void_p + hdll = windll.kernel32.LoadLibraryA(b"kernel32") + funcaddr = windll.kernel32.GetProcAddress(hdll, b"GetModuleHandleA") - hdll = windll.kernel32.LoadLibraryA(b"kernel32") - funcaddr = windll.kernel32.GetProcAddress(hdll, b"GetModuleHandleA") - - self.assertEqual(call_function(funcaddr, (None,)), - windll.kernel32.GetModuleHandleA(None)) + self.assertEqual(call_function(funcaddr, (None,)), + windll.kernel32.GetModuleHandleA(None)) class CallbackTracbackTestCase(unittest.TestCase): # When an exception is raised in a ctypes callback function, the C diff --git a/Lib/ctypes/test/test_slicing.py b/Lib/ctypes/test/test_slicing.py --- a/Lib/ctypes/test/test_slicing.py +++ b/Lib/ctypes/test/test_slicing.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test @@ -125,44 +126,40 @@ self.assertEqual(p[2:5:-3], s[2:5:-3]) - try: - c_wchar - except NameError: - pass - else: - def test_wchar_ptr(self): - s = "abcdefghijklmnopqrstuvwxyz\0" + @need_symbol('c_wchar') + def test_wchar_ptr(self): + s = "abcdefghijklmnopqrstuvwxyz\0" - dll = CDLL(_ctypes_test.__file__) - dll.my_wcsdup.restype = POINTER(c_wchar) - dll.my_wcsdup.argtypes = POINTER(c_wchar), - dll.my_free.restype = None - res = dll.my_wcsdup(s) - self.assertEqual(res[:len(s)], s) - self.assertEqual(res[:len(s):], s) - self.assertEqual(res[len(s)-1:-1:-1], s[::-1]) - self.assertEqual(res[len(s)-1:5:-7], s[:5:-7]) + dll = CDLL(_ctypes_test.__file__) + dll.my_wcsdup.restype = POINTER(c_wchar) + dll.my_wcsdup.argtypes = POINTER(c_wchar), + dll.my_free.restype = None + res = dll.my_wcsdup(s) + self.assertEqual(res[:len(s)], s) + self.assertEqual(res[:len(s):], s) + self.assertEqual(res[len(s)-1:-1:-1], s[::-1]) + self.assertEqual(res[len(s)-1:5:-7], s[:5:-7]) - import operator - self.assertRaises(TypeError, operator.setitem, - res, slice(0, 5), "abcde") - dll.my_free(res) + import operator + self.assertRaises(TypeError, operator.setitem, + res, slice(0, 5), "abcde") + dll.my_free(res) - if sizeof(c_wchar) == sizeof(c_short): - dll.my_wcsdup.restype = POINTER(c_short) - elif sizeof(c_wchar) == sizeof(c_int): - dll.my_wcsdup.restype = POINTER(c_int) - elif sizeof(c_wchar) == sizeof(c_long): - dll.my_wcsdup.restype = POINTER(c_long) - else: - return - res = dll.my_wcsdup(s) - tmpl = list(range(ord("a"), ord("z")+1)) - self.assertEqual(res[:len(s)-1], tmpl) - self.assertEqual(res[:len(s)-1:], tmpl) - self.assertEqual(res[len(s)-2:-1:-1], tmpl[::-1]) - self.assertEqual(res[len(s)-2:5:-7], tmpl[:5:-7]) - dll.my_free(res) + if sizeof(c_wchar) == sizeof(c_short): + dll.my_wcsdup.restype = POINTER(c_short) + elif sizeof(c_wchar) == sizeof(c_int): + dll.my_wcsdup.restype = POINTER(c_int) + elif sizeof(c_wchar) == sizeof(c_long): + dll.my_wcsdup.restype = POINTER(c_long) + else: + self.skipTest('Pointers to c_wchar are not supported') + res = dll.my_wcsdup(s) + tmpl = list(range(ord("a"), ord("z")+1)) + self.assertEqual(res[:len(s)-1], tmpl) + self.assertEqual(res[:len(s)-1:], tmpl) + self.assertEqual(res[len(s)-2:-1:-1], tmpl[::-1]) + self.assertEqual(res[len(s)-2:5:-7], tmpl[:5:-7]) + dll.my_free(res) ################################################################ diff --git a/Lib/ctypes/test/test_strings.py b/Lib/ctypes/test/test_strings.py --- a/Lib/ctypes/test/test_strings.py +++ b/Lib/ctypes/test/test_strings.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol class StringArrayTestCase(unittest.TestCase): def test(self): @@ -53,36 +54,33 @@ ## print BUF.from_param(c_char_p("python")) ## print BUF.from_param(BUF(*"pyth")) -try: - c_wchar -except NameError: - pass -else: - class WStringArrayTestCase(unittest.TestCase): - def test(self): - BUF = c_wchar * 4 + at need_symbol('c_wchar') +class WStringArrayTestCase(unittest.TestCase): + def test(self): + BUF = c_wchar * 4 - buf = BUF("a", "b", "c") - self.assertEqual(buf.value, "abc") + buf = BUF("a", "b", "c") + self.assertEqual(buf.value, "abc") - buf.value = "ABCD" - self.assertEqual(buf.value, "ABCD") + buf.value = "ABCD" + self.assertEqual(buf.value, "ABCD") - buf.value = "x" - self.assertEqual(buf.value, "x") + buf.value = "x" + self.assertEqual(buf.value, "x") - buf[1] = "Z" - self.assertEqual(buf.value, "xZCD") + buf[1] = "Z" + self.assertEqual(buf.value, "xZCD") - @unittest.skipIf(sizeof(c_wchar) < 4, - "sizeof(wchar_t) is smaller than 4 bytes") - def test_nonbmp(self): - u = chr(0x10ffff) - w = c_wchar(u) - self.assertEqual(w.value, u) + @unittest.skipIf(sizeof(c_wchar) < 4, + "sizeof(wchar_t) is smaller than 4 bytes") + def test_nonbmp(self): + u = chr(0x10ffff) + w = c_wchar(u) + self.assertEqual(w.value, u) class StringTestCase(unittest.TestCase): - def XX_test_basic_strings(self): + @unittest.skip('test disabled') + def test_basic_strings(self): cs = c_string("abcdef") # Cannot call len on a c_string any longer @@ -108,7 +106,8 @@ self.assertRaises(TypeError, c_string, "123") - def XX_test_sized_strings(self): + @unittest.skip('test disabled') + def test_sized_strings(self): # New in releases later than 0.4.0: self.assertRaises(TypeError, c_string, None) @@ -125,7 +124,8 @@ self.assertEqual(c_string(2).raw[-1], "\000") self.assertEqual(len(c_string(2).raw), 2) - def XX_test_initialized_strings(self): + @unittest.skip('test disabled') + def test_initialized_strings(self): self.assertEqual(c_string("ab", 4).raw[:2], "ab") self.assertEqual(c_string("ab", 4).raw[:2:], "ab") @@ -134,7 +134,8 @@ self.assertEqual(c_string("ab", 4).raw[-1], "\000") self.assertEqual(c_string("ab", 2).raw, "a\000") - def XX_test_toolong(self): + @unittest.skip('test disabled') + def test_toolong(self): cs = c_string("abcdef") # Much too long string: self.assertRaises(ValueError, setattr, cs, "value", "123456789012345") @@ -142,54 +143,53 @@ # One char too long values: self.assertRaises(ValueError, setattr, cs, "value", "1234567") -## def test_perf(self): -## check_perf() + @unittest.skip('test disabled') + def test_perf(self): + check_perf() -try: - c_wchar -except NameError: - pass -else: - class WStringTestCase(unittest.TestCase): - def test_wchar(self): - c_wchar("x") - repr(byref(c_wchar("x"))) - c_wchar("x") + at need_symbol('c_wchar') +class WStringTestCase(unittest.TestCase): + def test_wchar(self): + c_wchar("x") + repr(byref(c_wchar("x"))) + c_wchar("x") - def X_test_basic_wstrings(self): - cs = c_wstring("abcdef") + @unittest.skip('test disabled') + def test_basic_wstrings(self): + cs = c_wstring("abcdef") - # XXX This behaviour is about to change: - # len returns the size of the internal buffer in bytes. - # This includes the terminating NUL character. - self.assertEqual(sizeof(cs), 14) + # XXX This behaviour is about to change: + # len returns the size of the internal buffer in bytes. + # This includes the terminating NUL character. + self.assertEqual(sizeof(cs), 14) - # The value property is the string up to the first terminating NUL. - self.assertEqual(cs.value, "abcdef") - self.assertEqual(c_wstring("abc\000def").value, "abc") + # The value property is the string up to the first terminating NUL. + self.assertEqual(cs.value, "abcdef") + self.assertEqual(c_wstring("abc\000def").value, "abc") - self.assertEqual(c_wstring("abc\000def").value, "abc") + self.assertEqual(c_wstring("abc\000def").value, "abc") - # The raw property is the total buffer contents: - self.assertEqual(cs.raw, "abcdef\000") - self.assertEqual(c_wstring("abc\000def").raw, "abc\000def\000") + # The raw property is the total buffer contents: + self.assertEqual(cs.raw, "abcdef\000") + self.assertEqual(c_wstring("abc\000def").raw, "abc\000def\000") - # We can change the value: - cs.value = "ab" - self.assertEqual(cs.value, "ab") - self.assertEqual(cs.raw, "ab\000\000\000\000\000") + # We can change the value: + cs.value = "ab" + self.assertEqual(cs.value, "ab") + self.assertEqual(cs.raw, "ab\000\000\000\000\000") - self.assertRaises(TypeError, c_wstring, "123") - self.assertRaises(ValueError, c_wstring, 0) + self.assertRaises(TypeError, c_wstring, "123") + self.assertRaises(ValueError, c_wstring, 0) - def X_test_toolong(self): - cs = c_wstring("abcdef") - # Much too long string: - self.assertRaises(ValueError, setattr, cs, "value", "123456789012345") + @unittest.skip('test disabled') + def test_toolong(self): + cs = c_wstring("abcdef") + # Much too long string: + self.assertRaises(ValueError, setattr, cs, "value", "123456789012345") - # One char too long values: - self.assertRaises(ValueError, setattr, cs, "value", "1234567") + # One char too long values: + self.assertRaises(ValueError, setattr, cs, "value", "1234567") def run_test(rep, msg, func, arg): diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol from struct import calcsize import _testcapi @@ -291,12 +292,8 @@ self.assertEqual(p.phone.number, b"5678") self.assertEqual(p.age, 5) + @need_symbol('c_wchar') def test_structures_with_wchar(self): - try: - c_wchar - except NameError: - return # no unicode - class PersonW(Structure): _fields_ = [("name", c_wchar * 12), ("age", c_int)] @@ -354,14 +351,14 @@ except Exception as detail: return detail.__class__, str(detail) - -## def test_subclass_creation(self): -## meta = type(Structure) -## # same as 'class X(Structure): pass' -## # fails, since we need either a _fields_ or a _abstract_ attribute -## cls, msg = self.get_except(meta, "X", (Structure,), {}) -## self.assertEqual((cls, msg), -## (AttributeError, "class must define a '_fields_' attribute")) + @unittest.skip('test disabled') + def test_subclass_creation(self): + meta = type(Structure) + # same as 'class X(Structure): pass' + # fails, since we need either a _fields_ or a _abstract_ attribute + cls, msg = self.get_except(meta, "X", (Structure,), {}) + self.assertEqual((cls, msg), + (AttributeError, "class must define a '_fields_' attribute")) def test_abstract_class(self): class X(Structure): diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py --- a/Lib/ctypes/test/test_unicode.py +++ b/Lib/ctypes/test/test_unicode.py @@ -1,58 +1,55 @@ import unittest import ctypes +from ctypes.test import need_symbol -try: - ctypes.c_wchar -except AttributeError: - pass -else: - import _ctypes_test +import _ctypes_test - class UnicodeTestCase(unittest.TestCase): - def test_wcslen(self): - dll = ctypes.CDLL(_ctypes_test.__file__) - wcslen = dll.my_wcslen - wcslen.argtypes = [ctypes.c_wchar_p] + at need_symbol('c_wchar') +class UnicodeTestCase(unittest.TestCase): + def test_wcslen(self): + dll = ctypes.CDLL(_ctypes_test.__file__) + wcslen = dll.my_wcslen + wcslen.argtypes = [ctypes.c_wchar_p] - self.assertEqual(wcslen("abc"), 3) - self.assertEqual(wcslen("ab\u2070"), 3) - self.assertRaises(ctypes.ArgumentError, wcslen, b"ab\xe4") + self.assertEqual(wcslen("abc"), 3) + self.assertEqual(wcslen("ab\u2070"), 3) + self.assertRaises(ctypes.ArgumentError, wcslen, b"ab\xe4") - def test_buffers(self): - buf = ctypes.create_unicode_buffer("abc") - self.assertEqual(len(buf), 3+1) + def test_buffers(self): + buf = ctypes.create_unicode_buffer("abc") + self.assertEqual(len(buf), 3+1) - buf = ctypes.create_unicode_buffer("ab\xe4\xf6\xfc") - self.assertEqual(buf[:], "ab\xe4\xf6\xfc\0") - self.assertEqual(buf[::], "ab\xe4\xf6\xfc\0") - self.assertEqual(buf[::-1], '\x00\xfc\xf6\xe4ba') - self.assertEqual(buf[::2], 'a\xe4\xfc') - self.assertEqual(buf[6:5:-1], "") + buf = ctypes.create_unicode_buffer("ab\xe4\xf6\xfc") + self.assertEqual(buf[:], "ab\xe4\xf6\xfc\0") + self.assertEqual(buf[::], "ab\xe4\xf6\xfc\0") + self.assertEqual(buf[::-1], '\x00\xfc\xf6\xe4ba') + self.assertEqual(buf[::2], 'a\xe4\xfc') + self.assertEqual(buf[6:5:-1], "") - func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p +func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p - class StringTestCase(UnicodeTestCase): - def setUp(self): - func.argtypes = [ctypes.c_char_p] - func.restype = ctypes.c_char_p +class StringTestCase(UnicodeTestCase): + def setUp(self): + func.argtypes = [ctypes.c_char_p] + func.restype = ctypes.c_char_p - def tearDown(self): - func.argtypes = None - func.restype = ctypes.c_int + def tearDown(self): + func.argtypes = None + func.restype = ctypes.c_int - def test_func(self): - self.assertEqual(func(b"abc\xe4"), b"abc\xe4") + def test_func(self): + self.assertEqual(func(b"abc\xe4"), b"abc\xe4") - def test_buffers(self): - buf = ctypes.create_string_buffer(b"abc") - self.assertEqual(len(buf), 3+1) + def test_buffers(self): + buf = ctypes.create_string_buffer(b"abc") + self.assertEqual(len(buf), 3+1) - buf = ctypes.create_string_buffer(b"ab\xe4\xf6\xfc") - self.assertEqual(buf[:], b"ab\xe4\xf6\xfc\0") - self.assertEqual(buf[::], b"ab\xe4\xf6\xfc\0") - self.assertEqual(buf[::-1], b'\x00\xfc\xf6\xe4ba') - self.assertEqual(buf[::2], b'a\xe4\xfc') - self.assertEqual(buf[6:5:-1], b"") + buf = ctypes.create_string_buffer(b"ab\xe4\xf6\xfc") + self.assertEqual(buf[:], b"ab\xe4\xf6\xfc\0") + self.assertEqual(buf[::], b"ab\xe4\xf6\xfc\0") + self.assertEqual(buf[::-1], b'\x00\xfc\xf6\xe4ba') + self.assertEqual(buf[::2], b'a\xe4\xfc') + self.assertEqual(buf[6:5:-1], b"") if __name__ == '__main__': diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py --- a/Lib/ctypes/test/test_values.py +++ b/Lib/ctypes/test/test_values.py @@ -3,6 +3,7 @@ """ import unittest +import sys from ctypes import * import _ctypes_test @@ -27,62 +28,77 @@ ctdll = CDLL(_ctypes_test.__file__) self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") - class Win_ValuesTestCase(unittest.TestCase): - """This test only works when python itself is a dll/shared library""" + at unittest.skipUnless(sys.platform == 'win32', 'Windows-specific test') +class Win_ValuesTestCase(unittest.TestCase): + """This test only works when python itself is a dll/shared library""" - def test_optimizeflag(self): - # This test accesses the Py_OptimizeFlag intger, which is - # exported by the Python dll. + def test_optimizeflag(self): + # This test accesses the Py_OptimizeFlag intger, which is + # exported by the Python dll. - # It's value is set depending on the -O and -OO flags: - # if not given, it is 0 and __debug__ is 1. - # If -O is given, the flag is 1, for -OO it is 2. - # docstrings are also removed in the latter case. - opt = c_int.in_dll(pydll, "Py_OptimizeFlag").value - if __debug__: - self.assertEqual(opt, 0) - elif ValuesTestCase.__doc__ is not None: - self.assertEqual(opt, 1) - else: - self.assertEqual(opt, 2) + # It's value is set depending on the -O and -OO flags: + # if not given, it is 0 and __debug__ is 1. + # If -O is given, the flag is 1, for -OO it is 2. + # docstrings are also removed in the latter case. + opt = c_int.in_dll(pythonapi, "Py_OptimizeFlag").value + if __debug__: + self.assertEqual(opt, 0) + elif ValuesTestCase.__doc__ is not None: + self.assertEqual(opt, 1) + else: + self.assertEqual(opt, 2) - def test_frozentable(self): - # Python exports a PyImport_FrozenModules symbol. This is a - # pointer to an array of struct _frozen entries. The end of the - # array is marked by an entry containing a NULL name and zero - # size. + def test_frozentable(self): + # Python exports a PyImport_FrozenModules symbol. This is a + # pointer to an array of struct _frozen entries. The end of the + # array is marked by an entry containing a NULL name and zero + # size. - # In standard Python, this table contains a __hello__ - # module, and a __phello__ package containing a spam - # module. - class struct_frozen(Structure): - _fields_ = [("name", c_char_p), - ("code", POINTER(c_ubyte)), - ("size", c_int)] - FrozenTable = POINTER(struct_frozen) + # In standard Python, this table contains a __hello__ + # module, and a __phello__ package containing a spam + # module. + class struct_frozen(Structure): + _fields_ = [("name", c_char_p), + ("code", POINTER(c_ubyte)), + ("size", c_int)] + FrozenTable = POINTER(struct_frozen) - ft = FrozenTable.in_dll(pydll, "PyImport_FrozenModules") - # ft is a pointer to the struct_frozen entries: - items = [] - for entry in ft: - # This is dangerous. We *can* iterate over a pointer, but - # the loop will not terminate (maybe with an access - # violation;-) because the pointer instance has no size. - if entry.name is None: - break - items.append((entry.name, entry.size)) - import sys - if sys.version_info[:2] >= (2, 3): - expected = [("__hello__", 104), ("__phello__", -104), ("__phello__.spam", 104)] - else: - expected = [("__hello__", 100), ("__phello__", -100), ("__phello__.spam", 100)] - self.assertEqual(items, expected) + ft = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules") + # ft is a pointer to the struct_frozen entries: + items = [] + # _frozen_importlib changes size whenever importlib._bootstrap + # changes, so it gets a special case. We should make sure it's + # found, but don't worry about its size too much. + _fzn_implib_seen = False + for entry in ft: + # This is dangerous. We *can* iterate over a pointer, but + # the loop will not terminate (maybe with an access + # violation;-) because the pointer instance has no size. + if entry.name is None: + break - from ctypes import _pointer_type_cache - del _pointer_type_cache[struct_frozen] + if entry.name == b'_frozen_importlib': + _fzn_implib_seen = True + self.assertTrue(entry.size, + "_frozen_importlib was reported as having no size") + continue + items.append((entry.name, entry.size)) - def test_undefined(self): - self.assertRaises(ValueError, c_int.in_dll, pydll, "Undefined_Symbol") + expected = [(b"__hello__", 161), + (b"__phello__", -161), + (b"__phello__.spam", 161), + ] + self.assertEqual(items, expected) + + self.assertTrue(_fzn_implib_seen, + "_frozen_importlib wasn't found in PyImport_FrozenModules") + + from ctypes import _pointer_type_cache + del _pointer_type_cache[struct_frozen] + + def test_undefined(self): + self.assertRaises(ValueError, c_int.in_dll, pythonapi, + "Undefined_Symbol") if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py --- a/Lib/ctypes/test/test_win32.py +++ b/Lib/ctypes/test/test_win32.py @@ -1,99 +1,102 @@ # Windows specific tests from ctypes import * -from ctypes.test import is_resource_enabled +from ctypes.test import requires import unittest, sys from test import support import _ctypes_test -if sys.platform == "win32" and sizeof(c_void_p) == sizeof(c_int): - # Only windows 32-bit has different calling conventions. +# Only windows 32-bit has different calling conventions. + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + at unittest.skipUnless(sizeof(c_void_p) == sizeof(c_int), + "sizeof c_void_p and c_int differ") +class WindowsTestCase(unittest.TestCase): + def test_callconv_1(self): + # Testing stdcall function - class WindowsTestCase(unittest.TestCase): - def test_callconv_1(self): - # Testing stdcall function + IsWindow = windll.user32.IsWindow + # ValueError: Procedure probably called with not enough arguments + # (4 bytes missing) + self.assertRaises(ValueError, IsWindow) - IsWindow = windll.user32.IsWindow - # ValueError: Procedure probably called with not enough arguments (4 bytes missing) - self.assertRaises(ValueError, IsWindow) + # This one should succeed... + self.assertEqual(0, IsWindow(0)) - # This one should succeed... - self.assertEqual(0, IsWindow(0)) + # ValueError: Procedure probably called with too many arguments + # (8 bytes in excess) + self.assertRaises(ValueError, IsWindow, 0, 0, 0) - # ValueError: Procedure probably called with too many arguments (8 bytes in excess) - self.assertRaises(ValueError, IsWindow, 0, 0, 0) + def test_callconv_2(self): + # Calling stdcall function as cdecl - def test_callconv_2(self): - # Calling stdcall function as cdecl + IsWindow = cdll.user32.IsWindow - IsWindow = cdll.user32.IsWindow + # ValueError: Procedure called with not enough arguments + # (4 bytes missing) or wrong calling convention + self.assertRaises(ValueError, IsWindow, None) - # ValueError: Procedure called with not enough arguments (4 bytes missing) - # or wrong calling convention - self.assertRaises(ValueError, IsWindow, None) + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class FunctionCallTestCase(unittest.TestCase): + @requires("SEH") + def test_SEH(self): + # Call functions with invalid arguments, and make sure + # that access violations are trapped and raise an + # exception. + self.assertRaises(OSError, windll.kernel32.GetModuleHandleA, 32) -if sys.platform == "win32": - class FunctionCallTestCase(unittest.TestCase): + def test_noargs(self): + # This is a special case on win32 x64 + windll.user32.GetDesktopWindow() - if is_resource_enabled("SEH"): - def test_SEH(self): - # Call functions with invalid arguments, and make sure - # that access violations are trapped and raise an - # exception. - self.assertRaises(OSError, windll.kernel32.GetModuleHandleA, 32) + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class TestWintypes(unittest.TestCase): + def test_HWND(self): + from ctypes import wintypes + self.assertEqual(sizeof(wintypes.HWND), sizeof(c_void_p)) - def test_noargs(self): - # This is a special case on win32 x64 - windll.user32.GetDesktopWindow() + def test_PARAM(self): + from ctypes import wintypes + self.assertEqual(sizeof(wintypes.WPARAM), + sizeof(c_void_p)) + self.assertEqual(sizeof(wintypes.LPARAM), + sizeof(c_void_p)) - class TestWintypes(unittest.TestCase): - def test_HWND(self): - from ctypes import wintypes - self.assertEqual(sizeof(wintypes.HWND), sizeof(c_void_p)) + def test_COMError(self): + from _ctypes import COMError + if support.HAVE_DOCSTRINGS: + self.assertEqual(COMError.__doc__, + "Raised when a COM method call failed.") - def test_PARAM(self): - from ctypes import wintypes - self.assertEqual(sizeof(wintypes.WPARAM), - sizeof(c_void_p)) - self.assertEqual(sizeof(wintypes.LPARAM), - sizeof(c_void_p)) + ex = COMError(-1, "text", ("details",)) + self.assertEqual(ex.hresult, -1) + self.assertEqual(ex.text, "text") + self.assertEqual(ex.details, ("details",)) - def test_COMError(self): - from _ctypes import COMError - if support.HAVE_DOCSTRINGS: - self.assertEqual(COMError.__doc__, - "Raised when a COM method call failed.") + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class TestWinError(unittest.TestCase): + def test_winerror(self): + # see Issue 16169 + import errno + ERROR_INVALID_PARAMETER = 87 + msg = FormatError(ERROR_INVALID_PARAMETER).strip() + args = (errno.EINVAL, msg, None, ERROR_INVALID_PARAMETER) - ex = COMError(-1, "text", ("details",)) - self.assertEqual(ex.hresult, -1) - self.assertEqual(ex.text, "text") - self.assertEqual(ex.details, ("details",)) + e = WinError(ERROR_INVALID_PARAMETER) + self.assertEqual(e.args, args) + self.assertEqual(e.errno, errno.EINVAL) + self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER) - class TestWinError(unittest.TestCase): - def test_winerror(self): - # see Issue 16169 - import errno - ERROR_INVALID_PARAMETER = 87 - msg = FormatError(ERROR_INVALID_PARAMETER).strip() - args = (errno.EINVAL, msg, None, ERROR_INVALID_PARAMETER) - - e = WinError(ERROR_INVALID_PARAMETER) - self.assertEqual(e.args, args) - self.assertEqual(e.errno, errno.EINVAL) - self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER) - - windll.kernel32.SetLastError(ERROR_INVALID_PARAMETER) - try: - raise WinError() - except OSError as exc: - e = exc - self.assertEqual(e.args, args) - self.assertEqual(e.errno, errno.EINVAL) - self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER) + windll.kernel32.SetLastError(ERROR_INVALID_PARAMETER) + try: + raise WinError() + except OSError as exc: + e = exc + self.assertEqual(e.args, args) + self.assertEqual(e.errno, errno.EINVAL) + self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER) class Structures(unittest.TestCase): - def test_struct_by_value(self): class POINT(Structure): _fields_ = [("x", c_long), diff --git a/Lib/ctypes/test/test_wintypes.py b/Lib/ctypes/test/test_wintypes.py --- a/Lib/ctypes/test/test_wintypes.py +++ b/Lib/ctypes/test/test_wintypes.py @@ -1,14 +1,12 @@ import sys import unittest -if not sys.platform.startswith('win'): - raise unittest.SkipTest('Windows-only test') +from ctypes import * -from ctypes import * -from ctypes import wintypes - + at unittest.skipUnless(sys.platform.startswith('win'), 'Windows-only test') class WinTypesTest(unittest.TestCase): def test_variant_bool(self): + from ctypes import wintypes # reads 16-bits from memory, anything non-zero is True for true_value in (1, 32767, 32768, 65535, 65537): true = POINTER(c_int16)(c_int16(true_value)) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -95,6 +95,9 @@ Tests ----- +- Issue #19493: Refactored the ctypes test package to skip tests explicitly + rather than silently. + - Issue #18492: All resources are now allowed when tests are not run by regrtest.py. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 20:48:39 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 13 Jun 2014 20:48:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2319493=3A_Merge_with_3=2E4?= Message-ID: <3gqrYg4ftrz7Lk5@mail.python.org> http://hg.python.org/cpython/rev/86d14cf2a6a8 changeset: 91161:86d14cf2a6a8 parent: 91159:c678d5020eb0 parent: 91160:6f63fff5c120 user: Zachary Ware date: Fri Jun 13 13:48:03 2014 -0500 summary: Issue #19493: Merge with 3.4 files: Lib/ctypes/test/__init__.py | 10 +- Lib/ctypes/test/test_arrays.py | 26 +- Lib/ctypes/test/test_as_parameter.py | 6 +- Lib/ctypes/test/test_bitfields.py | 19 +- Lib/ctypes/test/test_buffers.py | 60 ++-- Lib/ctypes/test/test_bytes.py | 12 +- Lib/ctypes/test/test_byteswap.py | 3 +- Lib/ctypes/test/test_callbacks.py | 47 ++-- Lib/ctypes/test/test_cast.py | 15 +- Lib/ctypes/test/test_cfuncs.py | 9 +- Lib/ctypes/test/test_checkretval.py | 15 +- Lib/ctypes/test/test_errcheck.py | 19 - Lib/ctypes/test/test_find.py | 69 +++--- Lib/ctypes/test/test_functions.py | 70 +++--- Lib/ctypes/test/test_integers.py | 5 - Lib/ctypes/test/test_keeprefs.py | 3 +- Lib/ctypes/test/test_loading.py | 138 +++++++------ Lib/ctypes/test/test_macholib.py | 24 +- Lib/ctypes/test/test_memfunctions.py | 44 ++-- Lib/ctypes/test/test_numbers.py | 39 ++- Lib/ctypes/test/test_objects.py | 11 +- Lib/ctypes/test/test_parameters.py | 19 +- Lib/ctypes/test/test_prototypes.py | 84 +++---- Lib/ctypes/test/test_python_api.py | 33 +- Lib/ctypes/test/test_random_things.py | 27 +- Lib/ctypes/test/test_slicing.py | 67 +++--- Lib/ctypes/test/test_strings.py | 126 ++++++------ Lib/ctypes/test/test_structures.py | 23 +- Lib/ctypes/test/test_unicode.py | 81 +++---- Lib/ctypes/test/test_values.py | 114 ++++++---- Lib/ctypes/test/test_win32.py | 145 +++++++------ Lib/ctypes/test/test_wintypes.py | 8 +- Misc/NEWS | 3 + 33 files changed, 674 insertions(+), 700 deletions(-) diff --git a/Lib/ctypes/test/__init__.py b/Lib/ctypes/test/__init__.py --- a/Lib/ctypes/test/__init__.py +++ b/Lib/ctypes/test/__init__.py @@ -2,7 +2,15 @@ use_resources = [] -class ResourceDenied(Exception): +import ctypes +ctypes_symbols = dir(ctypes) + +def need_symbol(name): + return unittest.skipUnless(name in ctypes_symbols, + '{!r} is required'.format(name)) + + +class ResourceDenied(unittest.SkipTest): """Test skipped because it requested a disallowed resource. This is raised when a test calls requires() for a resource that diff --git a/Lib/ctypes/test/test_arrays.py b/Lib/ctypes/test/test_arrays.py --- a/Lib/ctypes/test/test_arrays.py +++ b/Lib/ctypes/test/test_arrays.py @@ -1,6 +1,8 @@ import unittest from ctypes import * +from ctypes.test import need_symbol + formats = "bBhHiIlLqQfd" formats = c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, \ @@ -98,20 +100,16 @@ self.assertEqual(sz[1:4:2], b"o") self.assertEqual(sz.value, b"foo") - try: - create_unicode_buffer - except NameError: - pass - else: - def test_from_addressW(self): - p = create_unicode_buffer("foo") - sz = (c_wchar * 3).from_address(addressof(p)) - self.assertEqual(sz[:], "foo") - self.assertEqual(sz[::], "foo") - self.assertEqual(sz[::-1], "oof") - self.assertEqual(sz[::3], "f") - self.assertEqual(sz[1:4:2], "o") - self.assertEqual(sz.value, "foo") + @need_symbol('create_unicode_buffer') + def test_from_addressW(self): + p = create_unicode_buffer("foo") + sz = (c_wchar * 3).from_address(addressof(p)) + self.assertEqual(sz[:], "foo") + self.assertEqual(sz[::], "foo") + self.assertEqual(sz[::-1], "oof") + self.assertEqual(sz[::3], "f") + self.assertEqual(sz[1:4:2], "o") + self.assertEqual(sz.value, "foo") def test_cache(self): # Array types are cached internally in the _ctypes extension, diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py --- a/Lib/ctypes/test/test_as_parameter.py +++ b/Lib/ctypes/test/test_as_parameter.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test dll = CDLL(_ctypes_test.__file__) @@ -17,11 +18,8 @@ def wrap(self, param): return param + @need_symbol('c_wchar') def test_wchar_parm(self): - try: - c_wchar - except NameError: - return f = dll._testfunc_i_bhilfd f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double] result = f(self.wrap(1), self.wrap("x"), self.wrap(3), self.wrap(4), self.wrap(5.0), self.wrap(6.0)) diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py --- a/Lib/ctypes/test/test_bitfields.py +++ b/Lib/ctypes/test/test_bitfields.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest import os @@ -127,20 +128,18 @@ result = self.fail_fields(("a", c_char, 1)) self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_char')) - try: - c_wchar - except NameError: - pass - else: - result = self.fail_fields(("a", c_wchar, 1)) - self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_wchar')) - class Dummy(Structure): _fields_ = [] result = self.fail_fields(("a", Dummy, 1)) self.assertEqual(result, (TypeError, 'bit fields not allowed for type Dummy')) + @need_symbol('c_wchar') + def test_c_wchar(self): + result = self.fail_fields(("a", c_wchar, 1)) + self.assertEqual(result, + (TypeError, 'bit fields not allowed for type c_wchar')) + def test_single_bitfield_size(self): for c_typ in int_types: result = self.fail_fields(("a", c_typ, -1)) @@ -240,7 +239,7 @@ _anonymous_ = ["_"] _fields_ = [("_", X)] - @unittest.skipUnless(hasattr(ctypes, "c_uint32"), "c_int32 is required") + @need_symbol('c_uint32') def test_uint32(self): class X(Structure): _fields_ = [("a", c_uint32, 32)] @@ -250,7 +249,7 @@ x.a = 0xFDCBA987 self.assertEqual(x.a, 0xFDCBA987) - @unittest.skipUnless(hasattr(ctypes, "c_uint64"), "c_int64 is required") + @need_symbol('c_uint64') def test_uint64(self): class X(Structure): _fields_ = [("a", c_uint64, 64)] diff --git a/Lib/ctypes/test/test_buffers.py b/Lib/ctypes/test/test_buffers.py --- a/Lib/ctypes/test/test_buffers.py +++ b/Lib/ctypes/test/test_buffers.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest class StringBufferTestCase(unittest.TestCase): @@ -24,39 +25,36 @@ self.assertEqual(len(bytearray(create_string_buffer(0))), 0) self.assertEqual(len(bytearray(create_string_buffer(1))), 1) - try: - c_wchar - except NameError: - pass - else: - def test_unicode_buffer(self): - b = create_unicode_buffer(32) - self.assertEqual(len(b), 32) - self.assertEqual(sizeof(b), 32 * sizeof(c_wchar)) - self.assertIs(type(b[0]), str) + @need_symbol('c_wchar') + def test_unicode_buffer(self): + b = create_unicode_buffer(32) + self.assertEqual(len(b), 32) + self.assertEqual(sizeof(b), 32 * sizeof(c_wchar)) + self.assertIs(type(b[0]), str) - b = create_unicode_buffer("abc") - self.assertEqual(len(b), 4) # trailing nul char - self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) - self.assertIs(type(b[0]), str) - self.assertEqual(b[0], "a") - self.assertEqual(b[:], "abc\0") - self.assertEqual(b[::], "abc\0") - self.assertEqual(b[::-1], "\0cba") - self.assertEqual(b[::2], "ac") - self.assertEqual(b[::5], "a") + b = create_unicode_buffer("abc") + self.assertEqual(len(b), 4) # trailing nul char + self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) + self.assertIs(type(b[0]), str) + self.assertEqual(b[0], "a") + self.assertEqual(b[:], "abc\0") + self.assertEqual(b[::], "abc\0") + self.assertEqual(b[::-1], "\0cba") + self.assertEqual(b[::2], "ac") + self.assertEqual(b[::5], "a") - def test_unicode_conversion(self): - b = create_unicode_buffer("abc") - self.assertEqual(len(b), 4) # trailing nul char - self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) - self.assertIs(type(b[0]), str) - self.assertEqual(b[0], "a") - self.assertEqual(b[:], "abc\0") - self.assertEqual(b[::], "abc\0") - self.assertEqual(b[::-1], "\0cba") - self.assertEqual(b[::2], "ac") - self.assertEqual(b[::5], "a") + @need_symbol('c_wchar') + def test_unicode_conversion(self): + b = create_unicode_buffer("abc") + self.assertEqual(len(b), 4) # trailing nul char + self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) + self.assertIs(type(b[0]), str) + self.assertEqual(b[0], "a") + self.assertEqual(b[:], "abc\0") + self.assertEqual(b[::], "abc\0") + self.assertEqual(b[::-1], "\0cba") + self.assertEqual(b[::2], "ac") + self.assertEqual(b[::5], "a") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_bytes.py b/Lib/ctypes/test/test_bytes.py --- a/Lib/ctypes/test/test_bytes.py +++ b/Lib/ctypes/test/test_bytes.py @@ -38,13 +38,13 @@ self.assertEqual(x.a, "abc") self.assertEqual(type(x.a), str) - if sys.platform == "win32": - def test_BSTR(self): - from _ctypes import _SimpleCData - class BSTR(_SimpleCData): - _type_ = "X" + @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + def test_BSTR(self): + from _ctypes import _SimpleCData + class BSTR(_SimpleCData): + _type_ = "X" - BSTR("abc") + BSTR("abc") if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_byteswap.py b/Lib/ctypes/test/test_byteswap.py --- a/Lib/ctypes/test/test_byteswap.py +++ b/Lib/ctypes/test/test_byteswap.py @@ -14,7 +14,8 @@ # For Structures and Unions, these types are created on demand. class Test(unittest.TestCase): - def X_test(self): + @unittest.skip('test disabled') + def test_X(self): print(sys.byteorder, file=sys.stderr) for i in range(32): bits = BITS() diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test class Callbacks(unittest.TestCase): @@ -88,9 +89,10 @@ # disabled: would now (correctly) raise a RuntimeWarning about # a memory leak. A callback function cannot return a non-integral # C type without causing a memory leak. -## def test_char_p(self): -## self.check_type(c_char_p, "abc") -## self.check_type(c_char_p, "def") + @unittest.skip('test disabled') + def test_char_p(self): + self.check_type(c_char_p, "abc") + self.check_type(c_char_p, "def") def test_pyobject(self): o = () @@ -142,13 +144,12 @@ CFUNCTYPE(None)(lambda x=Nasty(): None) -try: - WINFUNCTYPE -except NameError: - pass -else: - class StdcallCallbacks(Callbacks): + at need_symbol('WINFUNCTYPE') +class StdcallCallbacks(Callbacks): + try: functype = WINFUNCTYPE + except NameError: + pass ################################################################ @@ -178,7 +179,7 @@ from ctypes.util import find_library libc_path = find_library("c") if not libc_path: - return # cannot test + self.skipTest('could not find libc') libc = CDLL(libc_path) @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) @@ -190,23 +191,19 @@ libc.qsort(array, len(array), sizeof(c_int), cmp_func) self.assertEqual(array[:], [1, 5, 7, 33, 99]) - try: - WINFUNCTYPE - except NameError: - pass - else: - def test_issue_8959_b(self): - from ctypes.wintypes import BOOL, HWND, LPARAM + @need_symbol('WINFUNCTYPE') + def test_issue_8959_b(self): + from ctypes.wintypes import BOOL, HWND, LPARAM + global windowCount + windowCount = 0 + + @WINFUNCTYPE(BOOL, HWND, LPARAM) + def EnumWindowsCallbackFunc(hwnd, lParam): global windowCount - windowCount = 0 + windowCount += 1 + return True #Allow windows to keep enumerating - @WINFUNCTYPE(BOOL, HWND, LPARAM) - def EnumWindowsCallbackFunc(hwnd, lParam): - global windowCount - windowCount += 1 - return True #Allow windows to keep enumerating - - windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0) + windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0) def test_callback_register_int(self): # Issue #8275: buggy handling of callback args under Win64 diff --git a/Lib/ctypes/test/test_cast.py b/Lib/ctypes/test/test_cast.py --- a/Lib/ctypes/test/test_cast.py +++ b/Lib/ctypes/test/test_cast.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest import sys @@ -75,15 +76,11 @@ self.assertEqual(cast(cast(s, c_void_p), c_char_p).value, b"hiho") - try: - c_wchar_p - except NameError: - pass - else: - def test_wchar_p(self): - s = c_wchar_p("hiho") - self.assertEqual(cast(cast(s, c_void_p), c_wchar_p).value, - "hiho") + @need_symbol('c_wchar_p') + def test_wchar_p(self): + s = c_wchar_p("hiho") + self.assertEqual(cast(cast(s, c_void_p), c_wchar_p).value, + "hiho") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py --- a/Lib/ctypes/test/test_cfuncs.py +++ b/Lib/ctypes/test/test_cfuncs.py @@ -3,6 +3,7 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test @@ -193,7 +194,7 @@ try: WinDLL except NameError: - pass + def stdcall_dll(*_): pass else: class stdcall_dll(WinDLL): def __getattr__(self, name): @@ -203,9 +204,9 @@ setattr(self, name, func) return func - class stdcallCFunctions(CFunctions): - _dll = stdcall_dll(_ctypes_test.__file__) - pass + at need_symbol('WinDLL') +class stdcallCFunctions(CFunctions): + _dll = stdcall_dll(_ctypes_test.__file__) if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py --- a/Lib/ctypes/test/test_checkretval.py +++ b/Lib/ctypes/test/test_checkretval.py @@ -1,6 +1,7 @@ import unittest from ctypes import * +from ctypes.test import need_symbol class CHECKED(c_int): def _check_retval_(value): @@ -25,15 +26,11 @@ del dll._testfunc_p_p.restype self.assertEqual(42, dll._testfunc_p_p(42)) - try: - oledll - except NameError: - pass - else: - def test_oledll(self): - self.assertRaises(OSError, - oledll.oleaut32.CreateTypeLib2, - 0, None, None) + @need_symbol('oledll') + def test_oledll(self): + self.assertRaises(OSError, + oledll.oleaut32.CreateTypeLib2, + 0, None, None) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_errcheck.py b/Lib/ctypes/test/test_errcheck.py deleted file mode 100644 --- a/Lib/ctypes/test/test_errcheck.py +++ /dev/null @@ -1,19 +0,0 @@ -import sys -from ctypes import * - -##class HMODULE(Structure): -## _fields_ = [("value", c_void_p)] - -## def __repr__(self): -## return "" % self.value - -##windll.kernel32.GetModuleHandleA.restype = HMODULE - -##print windll.kernel32.GetModuleHandleA("python23.dll") -##print hex(sys.dllhandle) - -##def nonzero(handle): -## return (GetLastError(), handle) - -##windll.kernel32.GetModuleHandleA.errcheck = nonzero -##print windll.kernel32.GetModuleHandleA("spam") diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py --- a/Lib/ctypes/test/test_find.py +++ b/Lib/ctypes/test/test_find.py @@ -1,4 +1,5 @@ import unittest +import os import sys from ctypes import * from ctypes.util import find_library @@ -40,43 +41,43 @@ except OSError: pass - if lib_gl: - def test_gl(self): - if self.gl: - self.gl.glClearIndex + @unittest.skipUnless(lib_gl, 'lib_gl not available') + def test_gl(self): + if self.gl: + self.gl.glClearIndex - if lib_glu: - def test_glu(self): - if self.glu: - self.glu.gluBeginCurve + @unittest.skipUnless(lib_glu, 'lib_glu not available') + def test_glu(self): + if self.glu: + self.glu.gluBeginCurve - if lib_gle: - def test_gle(self): - if self.gle: - self.gle.gleGetJoinStyle + @unittest.skipUnless(lib_gle, 'lib_gle not available') + def test_gle(self): + if self.gle: + self.gle.gleGetJoinStyle -##if os.name == "posix" and sys.platform != "darwin": - -## # On platforms where the default shared library suffix is '.so', -## # at least some libraries can be loaded as attributes of the cdll -## # object, since ctypes now tries loading the lib again -## # with '.so' appended of the first try fails. -## # -## # Won't work for libc, unfortunately. OTOH, it isn't -## # needed for libc since this is already mapped into the current -## # process (?) -## # -## # On MAC OSX, it won't work either, because dlopen() needs a full path, -## # and the default suffix is either none or '.dylib'. - -## class LoadLibs(unittest.TestCase): -## def test_libm(self): -## import math -## libm = cdll.libm -## sqrt = libm.sqrt -## sqrt.argtypes = (c_double,) -## sqrt.restype = c_double -## self.assertEqual(sqrt(2), math.sqrt(2)) +# On platforms where the default shared library suffix is '.so', +# at least some libraries can be loaded as attributes of the cdll +# object, since ctypes now tries loading the lib again +# with '.so' appended of the first try fails. +# +# Won't work for libc, unfortunately. OTOH, it isn't +# needed for libc since this is already mapped into the current +# process (?) +# +# On MAC OSX, it won't work either, because dlopen() needs a full path, +# and the default suffix is either none or '.dylib'. + at unittest.skip('test disabled') + at unittest.skipUnless(os.name=="posix" and sys.platform != "darwin", + 'test not suitable for this platform') +class LoadLibs(unittest.TestCase): + def test_libm(self): + import math + libm = cdll.libm + sqrt = libm.sqrt + sqrt.argtypes = (c_double,) + sqrt.restype = c_double + self.assertEqual(sqrt(2), math.sqrt(2)) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -6,6 +6,7 @@ """ from ctypes import * +from ctypes.test import need_symbol import sys, unittest try: @@ -63,22 +64,16 @@ pass + @need_symbol('c_wchar') def test_wchar_parm(self): - try: - c_wchar - except NameError: - return f = dll._testfunc_i_bhilfd f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double] result = f(1, "x", 3, 4, 5.0, 6.0) self.assertEqual(result, 139) self.assertEqual(type(result), int) + @need_symbol('c_wchar') def test_wchar_result(self): - try: - c_wchar - except NameError: - return f = dll._testfunc_i_bhilfd f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double] f.restype = c_wchar @@ -155,11 +150,8 @@ self.assertEqual(result, -21) self.assertEqual(type(result), float) + @need_symbol('c_longlong') def test_longlongresult(self): - try: - c_longlong - except NameError: - return f = dll._testfunc_q_bhilfd f.restype = c_longlong f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double] @@ -296,6 +288,7 @@ result = f(-10, cb) self.assertEqual(result, -18) + @need_symbol('c_longlong') def test_longlong_callbacks(self): f = dll._testfunc_callback_q_qf @@ -348,16 +341,16 @@ s2h = dll.ret_2h_func(inp) self.assertEqual((s2h.x, s2h.y), (99*2, 88*3)) - if sys.platform == "win32": - def test_struct_return_2H_stdcall(self): - class S2H(Structure): - _fields_ = [("x", c_short), - ("y", c_short)] + @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + def test_struct_return_2H_stdcall(self): + class S2H(Structure): + _fields_ = [("x", c_short), + ("y", c_short)] - windll.s_ret_2h_func.restype = S2H - windll.s_ret_2h_func.argtypes = [S2H] - s2h = windll.s_ret_2h_func(S2H(99, 88)) - self.assertEqual((s2h.x, s2h.y), (99*2, 88*3)) + windll.s_ret_2h_func.restype = S2H + windll.s_ret_2h_func.argtypes = [S2H] + s2h = windll.s_ret_2h_func(S2H(99, 88)) + self.assertEqual((s2h.x, s2h.y), (99*2, 88*3)) def test_struct_return_8H(self): class S8I(Structure): @@ -376,23 +369,24 @@ self.assertEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) - if sys.platform == "win32": - def test_struct_return_8H_stdcall(self): - class S8I(Structure): - _fields_ = [("a", c_int), - ("b", c_int), - ("c", c_int), - ("d", c_int), - ("e", c_int), - ("f", c_int), - ("g", c_int), - ("h", c_int)] - windll.s_ret_8i_func.restype = S8I - windll.s_ret_8i_func.argtypes = [S8I] - inp = S8I(9, 8, 7, 6, 5, 4, 3, 2) - s8i = windll.s_ret_8i_func(inp) - self.assertEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), - (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) + @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + def test_struct_return_8H_stdcall(self): + class S8I(Structure): + _fields_ = [("a", c_int), + ("b", c_int), + ("c", c_int), + ("d", c_int), + ("e", c_int), + ("f", c_int), + ("g", c_int), + ("h", c_int)] + windll.s_ret_8i_func.restype = S8I + windll.s_ret_8i_func.argtypes = [S8I] + inp = S8I(9, 8, 7, 6, 5, 4, 3, 2) + s8i = windll.s_ret_8i_func(inp) + self.assertEqual( + (s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), + (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) def test_sf1651235(self): # see http://www.python.org/sf/1651235 diff --git a/Lib/ctypes/test/test_integers.py b/Lib/ctypes/test/test_integers.py deleted file mode 100644 --- a/Lib/ctypes/test/test_integers.py +++ /dev/null @@ -1,5 +0,0 @@ -# superseded by test_numbers.py -import unittest - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/ctypes/test/test_keeprefs.py b/Lib/ctypes/test/test_keeprefs.py --- a/Lib/ctypes/test/test_keeprefs.py +++ b/Lib/ctypes/test/test_keeprefs.py @@ -94,7 +94,8 @@ self.assertEqual(x._objects, {'1': i}) class DeletePointerTestCase(unittest.TestCase): - def X_test(self): + @unittest.skip('test disabled') + def test_X(self): class X(Structure): _fields_ = [("p", POINTER(c_char_p))] x = X() diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -21,18 +21,21 @@ unknowndll = "xxrandomnamexx" - if libc_name is not None: - def test_load(self): - CDLL(libc_name) - CDLL(os.path.basename(libc_name)) - self.assertRaises(OSError, CDLL, self.unknowndll) + @unittest.skipUnless(libc_name is not None, 'could not find libc') + def test_load(self): + CDLL(libc_name) + CDLL(os.path.basename(libc_name)) + self.assertRaises(OSError, CDLL, self.unknowndll) - if libc_name is not None and os.path.basename(libc_name) == "libc.so.6": - def test_load_version(self): - cdll.LoadLibrary("libc.so.6") - # linux uses version, libc 9 should not exist - self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9") - self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll) + @unittest.skipUnless(libc_name is not None, 'could not find libc') + @unittest.skipUnless(libc_name is not None and + os.path.basename(libc_name) == "libc.so.6", + 'wrong libc path for test') + def test_load_version(self): + cdll.LoadLibrary("libc.so.6") + # linux uses version, libc 9 should not exist + self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9") + self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll) def test_find(self): for name in ("c", "m"): @@ -41,66 +44,71 @@ cdll.LoadLibrary(lib) CDLL(lib) - if os.name in ("nt", "ce"): - def test_load_library(self): - self.assertIsNotNone(libc_name) - if is_resource_enabled("printing"): - print(find_library("kernel32")) - print(find_library("user32")) + @unittest.skipUnless(os.name in ("nt", "ce"), + 'test specific to Windows (NT/CE)') + def test_load_library(self): + self.assertIsNotNone(libc_name) + if is_resource_enabled("printing"): + print(find_library("kernel32")) + print(find_library("user32")) - if os.name == "nt": - windll.kernel32.GetModuleHandleW - windll["kernel32"].GetModuleHandleW - windll.LoadLibrary("kernel32").GetModuleHandleW - WinDLL("kernel32").GetModuleHandleW - elif os.name == "ce": - windll.coredll.GetModuleHandleW - windll["coredll"].GetModuleHandleW - windll.LoadLibrary("coredll").GetModuleHandleW - WinDLL("coredll").GetModuleHandleW + if os.name == "nt": + windll.kernel32.GetModuleHandleW + windll["kernel32"].GetModuleHandleW + windll.LoadLibrary("kernel32").GetModuleHandleW + WinDLL("kernel32").GetModuleHandleW + elif os.name == "ce": + windll.coredll.GetModuleHandleW + windll["coredll"].GetModuleHandleW + windll.LoadLibrary("coredll").GetModuleHandleW + WinDLL("coredll").GetModuleHandleW - def test_load_ordinal_functions(self): - import _ctypes_test - dll = WinDLL(_ctypes_test.__file__) - # We load the same function both via ordinal and name - func_ord = dll[2] - func_name = dll.GetString - # addressof gets the address where the function pointer is stored - a_ord = addressof(func_ord) - a_name = addressof(func_name) - f_ord_addr = c_void_p.from_address(a_ord).value - f_name_addr = c_void_p.from_address(a_name).value - self.assertEqual(hex(f_ord_addr), hex(f_name_addr)) + @unittest.skipUnless(os.name in ("nt", "ce"), + 'test specific to Windows (NT/CE)') + def test_load_ordinal_functions(self): + import _ctypes_test + dll = WinDLL(_ctypes_test.__file__) + # We load the same function both via ordinal and name + func_ord = dll[2] + func_name = dll.GetString + # addressof gets the address where the function pointer is stored + a_ord = addressof(func_ord) + a_name = addressof(func_name) + f_ord_addr = c_void_p.from_address(a_ord).value + f_name_addr = c_void_p.from_address(a_name).value + self.assertEqual(hex(f_ord_addr), hex(f_name_addr)) - self.assertRaises(AttributeError, dll.__getitem__, 1234) + self.assertRaises(AttributeError, dll.__getitem__, 1234) - if os.name == "nt": - def test_1703286_A(self): - from _ctypes import LoadLibrary, FreeLibrary - # On winXP 64-bit, advapi32 loads at an address that does - # NOT fit into a 32-bit integer. FreeLibrary must be able - # to accept this address. + @unittest.skipUnless(os.name == "nt", 'Windows-specific test') + def test_1703286_A(self): + from _ctypes import LoadLibrary, FreeLibrary + # On winXP 64-bit, advapi32 loads at an address that does + # NOT fit into a 32-bit integer. FreeLibrary must be able + # to accept this address. - # These are tests for http://www.python.org/sf/1703286 - handle = LoadLibrary("advapi32") - FreeLibrary(handle) + # These are tests for http://www.python.org/sf/1703286 + handle = LoadLibrary("advapi32") + FreeLibrary(handle) - def test_1703286_B(self): - # Since on winXP 64-bit advapi32 loads like described - # above, the (arbitrarily selected) CloseEventLog function - # also has a high address. 'call_function' should accept - # addresses so large. - from _ctypes import call_function - advapi32 = windll.advapi32 - # Calling CloseEventLog with a NULL argument should fail, - # but the call should not segfault or so. - self.assertEqual(0, advapi32.CloseEventLog(None)) - windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p - windll.kernel32.GetProcAddress.restype = c_void_p - proc = windll.kernel32.GetProcAddress(advapi32._handle, b"CloseEventLog") - self.assertTrue(proc) - # This is the real test: call the function via 'call_function' - self.assertEqual(0, call_function(proc, (None,))) + @unittest.skipUnless(os.name == "nt", 'Windows-specific test') + def test_1703286_B(self): + # Since on winXP 64-bit advapi32 loads like described + # above, the (arbitrarily selected) CloseEventLog function + # also has a high address. 'call_function' should accept + # addresses so large. + from _ctypes import call_function + advapi32 = windll.advapi32 + # Calling CloseEventLog with a NULL argument should fail, + # but the call should not segfault or so. + self.assertEqual(0, advapi32.CloseEventLog(None)) + windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p + windll.kernel32.GetProcAddress.restype = c_void_p + proc = windll.kernel32.GetProcAddress(advapi32._handle, + b"CloseEventLog") + self.assertTrue(proc) + # This is the real test: call the function via 'call_function' + self.assertEqual(0, call_function(proc, (None,))) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_macholib.py b/Lib/ctypes/test/test_macholib.py --- a/Lib/ctypes/test/test_macholib.py +++ b/Lib/ctypes/test/test_macholib.py @@ -43,21 +43,21 @@ raise ValueError("%s not found" % (name,)) class MachOTest(unittest.TestCase): - if sys.platform == "darwin": - def test_find(self): + @unittest.skipUnless(sys.platform == "darwin", 'OSX-specific test') + def test_find(self): - self.assertEqual(find_lib('pthread'), - '/usr/lib/libSystem.B.dylib') + self.assertEqual(find_lib('pthread'), + '/usr/lib/libSystem.B.dylib') - result = find_lib('z') - # Issue #21093: dyld default search path includes $HOME/lib and - # /usr/local/lib before /usr/lib, which caused test failures if - # a local copy of libz exists in one of them. Now ignore the head - # of the path. - self.assertRegex(result, r".*/lib/libz\..*.*\.dylib") + result = find_lib('z') + # Issue #21093: dyld default search path includes $HOME/lib and + # /usr/local/lib before /usr/lib, which caused test failures if + # a local copy of libz exists in one of them. Now ignore the head + # of the path. + self.assertRegex(result, r".*/lib/libz\..*.*\.dylib") - self.assertEqual(find_lib('IOKit'), - '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit') + self.assertEqual(find_lib('IOKit'), + '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit') if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_memfunctions.py b/Lib/ctypes/test/test_memfunctions.py --- a/Lib/ctypes/test/test_memfunctions.py +++ b/Lib/ctypes/test/test_memfunctions.py @@ -2,17 +2,19 @@ from test import support import unittest from ctypes import * +from ctypes.test import need_symbol class MemFunctionsTest(unittest.TestCase): -## def test_overflow(self): -## # string_at and wstring_at must use the Python calling -## # convention (which acquires the GIL and checks the Python -## # error flag). Provoke an error and catch it; see also issue -## # #3554: -## self.assertRaises((OverflowError, MemoryError, SystemError), -## lambda: wstring_at(u"foo", sys.maxint - 1)) -## self.assertRaises((OverflowError, MemoryError, SystemError), -## lambda: string_at("foo", sys.maxint - 1)) + @unittest.skip('test disabled') + def test_overflow(self): + # string_at and wstring_at must use the Python calling + # convention (which acquires the GIL and checks the Python + # error flag). Provoke an error and catch it; see also issue + # #3554: + self.assertRaises((OverflowError, MemoryError, SystemError), + lambda: wstring_at(u"foo", sys.maxint - 1)) + self.assertRaises((OverflowError, MemoryError, SystemError), + lambda: string_at("foo", sys.maxint - 1)) def test_memmove(self): # large buffers apparently increase the chance that the memory @@ -61,21 +63,17 @@ self.assertEqual(string_at(b"foo bar", 7), b"foo bar") self.assertEqual(string_at(b"foo bar", 3), b"foo") - try: - create_unicode_buffer - except NameError: - pass - else: - def test_wstring_at(self): - p = create_unicode_buffer("Hello, World") - a = create_unicode_buffer(1000000) - result = memmove(a, p, len(p) * sizeof(c_wchar)) - self.assertEqual(a.value, "Hello, World") + @need_symbol('create_unicode_buffer') + def test_wstring_at(self): + p = create_unicode_buffer("Hello, World") + a = create_unicode_buffer(1000000) + result = memmove(a, p, len(p) * sizeof(c_wchar)) + self.assertEqual(a.value, "Hello, World") - self.assertEqual(wstring_at(a), "Hello, World") - self.assertEqual(wstring_at(a, 5), "Hello") - self.assertEqual(wstring_at(a, 16), "Hello, World\0\0\0\0") - self.assertEqual(wstring_at(a, 0), "") + self.assertEqual(wstring_at(a), "Hello, World") + self.assertEqual(wstring_at(a, 5), "Hello") + self.assertEqual(wstring_at(a, 16), "Hello, World\0\0\0\0") + self.assertEqual(wstring_at(a, 0), "") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_numbers.py b/Lib/ctypes/test/test_numbers.py --- a/Lib/ctypes/test/test_numbers.py +++ b/Lib/ctypes/test/test_numbers.py @@ -82,12 +82,13 @@ self.assertRaises(TypeError, t, "") self.assertRaises(TypeError, t, None) -## def test_valid_ranges(self): -## # invalid values of the correct type -## # raise ValueError (not OverflowError) -## for t, (l, h) in zip(unsigned_types, unsigned_ranges): -## self.assertRaises(ValueError, t, l-1) -## self.assertRaises(ValueError, t, h+1) + @unittest.skip('test disabled') + def test_valid_ranges(self): + # invalid values of the correct type + # raise ValueError (not OverflowError) + for t, (l, h) in zip(unsigned_types, unsigned_ranges): + self.assertRaises(ValueError, t, l-1) + self.assertRaises(ValueError, t, h+1) def test_from_param(self): # the from_param class method attribute always @@ -200,16 +201,17 @@ self.assertEqual(v.value, b'?') # array does not support c_bool / 't' - # def test_bool_from_address(self): - # from ctypes import c_bool - # from array import array - # a = array(c_bool._type_, [True]) - # v = t.from_address(a.buffer_info()[0]) - # self.assertEqual(v.value, a[0]) - # self.assertEqual(type(v) is t) - # a[0] = False - # self.assertEqual(v.value, a[0]) - # self.assertEqual(type(v) is t) + @unittest.skip('test disabled') + def test_bool_from_address(self): + from ctypes import c_bool + from array import array + a = array(c_bool._type_, [True]) + v = t.from_address(a.buffer_info()[0]) + self.assertEqual(v.value, a[0]) + self.assertEqual(type(v) is t) + a[0] = False + self.assertEqual(v.value, a[0]) + self.assertEqual(type(v) is t) def test_init(self): # c_int() can be initialized from Python's int, and c_int. @@ -227,8 +229,9 @@ if (hasattr(t, "__ctype_le__")): self.assertRaises(OverflowError, t.__ctype_le__, big_int) -## def test_perf(self): -## check_perf() + @unittest.skip('test disabled') + def test_perf(self): + check_perf() from ctypes import _SimpleCData class c_int_S(_SimpleCData): diff --git a/Lib/ctypes/test/test_objects.py b/Lib/ctypes/test/test_objects.py --- a/Lib/ctypes/test/test_objects.py +++ b/Lib/ctypes/test/test_objects.py @@ -59,12 +59,9 @@ import ctypes.test.test_objects class TestCase(unittest.TestCase): - if sys.hexversion > 0x02040000: - # Python 2.3 has no ELLIPSIS flag, so we don't test with this - # version: - def test(self): - doctest.testmod(ctypes.test.test_objects) + def test(self): + failures, tests = doctest.testmod(ctypes.test.test_objects) + self.assertFalse(failures, 'doctests failed, see output above') if __name__ == '__main__': - if sys.hexversion > 0x02040000: - doctest.testmod(ctypes.test.test_objects) + doctest.testmod(ctypes.test.test_objects) diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -1,4 +1,5 @@ import unittest, sys +from ctypes.test import need_symbol class SimpleTypesTestCase(unittest.TestCase): @@ -35,10 +36,9 @@ self.assertEqual(CVOIDP.from_param("abc"), "abcabc") self.assertEqual(CCHARP.from_param("abc"), "abcabcabcabc") - try: - from ctypes import c_wchar_p - except ImportError: - return + @need_symbol('c_wchar_p') + def test_subclasses_c_wchar_p(self): + from ctypes import c_wchar_p class CWCHARP(c_wchar_p): def from_param(cls, value): @@ -66,13 +66,9 @@ a = c_char_p(b"123") self.assertIs(c_char_p.from_param(a), a) + @need_symbol('c_wchar_p') def test_cw_strings(self): - from ctypes import byref - try: - from ctypes import c_wchar_p - except ImportError: -## print "(No c_wchar_p)" - return + from ctypes import byref, c_wchar_p c_wchar_p.from_param("123") @@ -139,9 +135,6 @@ self.assertRaises(TypeError, LPINT.from_param, c_long*3) self.assertRaises(TypeError, LPINT.from_param, c_uint*3) -## def test_performance(self): -## check_perf() - def test_noctypes_argtype(self): import _ctypes_test from ctypes import CDLL, c_void_p, ArgumentError diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py --- a/Lib/ctypes/test/test_prototypes.py +++ b/Lib/ctypes/test/test_prototypes.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest # IMPORTANT INFO: @@ -135,13 +136,14 @@ func(pointer(c_int())) func((c_int * 3)()) - try: - func.restype = c_wchar_p - except NameError: - pass - else: - self.assertEqual(None, func(c_wchar_p(None))) - self.assertEqual("123", func(c_wchar_p("123"))) + @need_symbol('c_wchar_p') + def test_c_void_p_arg_with_c_wchar_p(self): + func = testdll._testfunc_p_p + func.restype = c_wchar_p + func.argtypes = c_void_p, + + self.assertEqual(None, func(c_wchar_p(None))) + self.assertEqual("123", func(c_wchar_p("123"))) def test_instance(self): func = testdll._testfunc_p_p @@ -156,51 +158,47 @@ func.argtypes = None self.assertEqual(None, func(X())) -try: - c_wchar -except NameError: - pass -else: - class WCharPointersTestCase(unittest.TestCase): + at need_symbol('c_wchar') +class WCharPointersTestCase(unittest.TestCase): - def setUp(self): - func = testdll._testfunc_p_p - func.restype = c_int - func.argtypes = None + def setUp(self): + func = testdll._testfunc_p_p + func.restype = c_int + func.argtypes = None - def test_POINTER_c_wchar_arg(self): - func = testdll._testfunc_p_p - func.restype = c_wchar_p - func.argtypes = POINTER(c_wchar), + def test_POINTER_c_wchar_arg(self): + func = testdll._testfunc_p_p + func.restype = c_wchar_p + func.argtypes = POINTER(c_wchar), - self.assertEqual(None, func(None)) - self.assertEqual("123", func("123")) - self.assertEqual(None, func(c_wchar_p(None))) - self.assertEqual("123", func(c_wchar_p("123"))) + self.assertEqual(None, func(None)) + self.assertEqual("123", func("123")) + self.assertEqual(None, func(c_wchar_p(None))) + self.assertEqual("123", func(c_wchar_p("123"))) - self.assertEqual("123", func(c_wbuffer("123"))) - ca = c_wchar("a") - self.assertEqual("a", func(pointer(ca))[0]) - self.assertEqual("a", func(byref(ca))[0]) + self.assertEqual("123", func(c_wbuffer("123"))) + ca = c_wchar("a") + self.assertEqual("a", func(pointer(ca))[0]) + self.assertEqual("a", func(byref(ca))[0]) - def test_c_wchar_p_arg(self): - func = testdll._testfunc_p_p - func.restype = c_wchar_p - func.argtypes = c_wchar_p, + def test_c_wchar_p_arg(self): + func = testdll._testfunc_p_p + func.restype = c_wchar_p + func.argtypes = c_wchar_p, - c_wchar_p.from_param("123") + c_wchar_p.from_param("123") - self.assertEqual(None, func(None)) - self.assertEqual("123", func("123")) - self.assertEqual(None, func(c_wchar_p(None))) - self.assertEqual("123", func(c_wchar_p("123"))) + self.assertEqual(None, func(None)) + self.assertEqual("123", func("123")) + self.assertEqual(None, func(c_wchar_p(None))) + self.assertEqual("123", func(c_wchar_p("123"))) - # XXX Currently, these raise TypeErrors, although they shouldn't: - self.assertEqual("123", func(c_wbuffer("123"))) - ca = c_wchar("a") - self.assertEqual("a", func(pointer(ca))[0]) - self.assertEqual("a", func(byref(ca))[0]) + # XXX Currently, these raise TypeErrors, although they shouldn't: + self.assertEqual("123", func(c_wbuffer("123"))) + ca = c_wchar("a") + self.assertEqual("a", func(pointer(ca))[0]) + self.assertEqual("a", func(byref(ca))[0]) class ArrayTest(unittest.TestCase): def test(self): diff --git a/Lib/ctypes/test/test_python_api.py b/Lib/ctypes/test/test_python_api.py --- a/Lib/ctypes/test/test_python_api.py +++ b/Lib/ctypes/test/test_python_api.py @@ -1,7 +1,7 @@ from ctypes import * import unittest, sys from test import support -from ctypes.test import is_resource_enabled +from ctypes.test import requires ################################################################ # This section should be moved into ctypes\__init__.py, when it's ready. @@ -39,24 +39,25 @@ del pyob self.assertEqual(grc(s), refcnt) - if is_resource_enabled("refcount"): - # This test is unreliable, because it is possible that code in - # unittest changes the refcount of the '42' integer. So, it - # is disabled by default. - def test_PyLong_Long(self): - ref42 = grc(42) - pythonapi.PyLong_FromLong.restype = py_object - self.assertEqual(pythonapi.PyLong_FromLong(42), 42) + # This test is unreliable, because it is possible that code in + # unittest changes the refcount of the '42' integer. So, it + # is disabled by default. + @requires("refcount") + @support.refcount_test + def test_PyLong_Long(self): + ref42 = grc(42) + pythonapi.PyLong_FromLong.restype = py_object + self.assertEqual(pythonapi.PyLong_FromLong(42), 42) - self.assertEqual(grc(42), ref42) + self.assertEqual(grc(42), ref42) - pythonapi.PyLong_AsLong.argtypes = (py_object,) - pythonapi.PyLong_AsLong.restype = c_long + pythonapi.PyLong_AsLong.argtypes = (py_object,) + pythonapi.PyLong_AsLong.restype = c_long - res = pythonapi.PyLong_AsLong(42) - self.assertEqual(grc(res), ref42 + 1) - del res - self.assertEqual(grc(42), ref42) + res = pythonapi.PyLong_AsLong(42) + self.assertEqual(grc(res), ref42 + 1) + del res + self.assertEqual(grc(42), ref42) @support.refcount_test def test_PyObj_FromPtr(self): diff --git a/Lib/ctypes/test/test_random_things.py b/Lib/ctypes/test/test_random_things.py --- a/Lib/ctypes/test/test_random_things.py +++ b/Lib/ctypes/test/test_random_things.py @@ -5,23 +5,22 @@ 42 / arg raise ValueError(arg) -if sys.platform == "win32": + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class call_function_TestCase(unittest.TestCase): + # _ctypes.call_function is deprecated and private, but used by + # Gary Bishp's readline module. If we have it, we must test it as well. - class call_function_TestCase(unittest.TestCase): - # _ctypes.call_function is deprecated and private, but used by - # Gary Bishp's readline module. If we have it, we must test it as well. + def test(self): + from _ctypes import call_function + windll.kernel32.LoadLibraryA.restype = c_void_p + windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p + windll.kernel32.GetProcAddress.restype = c_void_p - def test(self): - from _ctypes import call_function - windll.kernel32.LoadLibraryA.restype = c_void_p - windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p - windll.kernel32.GetProcAddress.restype = c_void_p + hdll = windll.kernel32.LoadLibraryA(b"kernel32") + funcaddr = windll.kernel32.GetProcAddress(hdll, b"GetModuleHandleA") - hdll = windll.kernel32.LoadLibraryA(b"kernel32") - funcaddr = windll.kernel32.GetProcAddress(hdll, b"GetModuleHandleA") - - self.assertEqual(call_function(funcaddr, (None,)), - windll.kernel32.GetModuleHandleA(None)) + self.assertEqual(call_function(funcaddr, (None,)), + windll.kernel32.GetModuleHandleA(None)) class CallbackTracbackTestCase(unittest.TestCase): # When an exception is raised in a ctypes callback function, the C diff --git a/Lib/ctypes/test/test_slicing.py b/Lib/ctypes/test/test_slicing.py --- a/Lib/ctypes/test/test_slicing.py +++ b/Lib/ctypes/test/test_slicing.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test @@ -125,44 +126,40 @@ self.assertEqual(p[2:5:-3], s[2:5:-3]) - try: - c_wchar - except NameError: - pass - else: - def test_wchar_ptr(self): - s = "abcdefghijklmnopqrstuvwxyz\0" + @need_symbol('c_wchar') + def test_wchar_ptr(self): + s = "abcdefghijklmnopqrstuvwxyz\0" - dll = CDLL(_ctypes_test.__file__) - dll.my_wcsdup.restype = POINTER(c_wchar) - dll.my_wcsdup.argtypes = POINTER(c_wchar), - dll.my_free.restype = None - res = dll.my_wcsdup(s) - self.assertEqual(res[:len(s)], s) - self.assertEqual(res[:len(s):], s) - self.assertEqual(res[len(s)-1:-1:-1], s[::-1]) - self.assertEqual(res[len(s)-1:5:-7], s[:5:-7]) + dll = CDLL(_ctypes_test.__file__) + dll.my_wcsdup.restype = POINTER(c_wchar) + dll.my_wcsdup.argtypes = POINTER(c_wchar), + dll.my_free.restype = None + res = dll.my_wcsdup(s) + self.assertEqual(res[:len(s)], s) + self.assertEqual(res[:len(s):], s) + self.assertEqual(res[len(s)-1:-1:-1], s[::-1]) + self.assertEqual(res[len(s)-1:5:-7], s[:5:-7]) - import operator - self.assertRaises(TypeError, operator.setitem, - res, slice(0, 5), "abcde") - dll.my_free(res) + import operator + self.assertRaises(TypeError, operator.setitem, + res, slice(0, 5), "abcde") + dll.my_free(res) - if sizeof(c_wchar) == sizeof(c_short): - dll.my_wcsdup.restype = POINTER(c_short) - elif sizeof(c_wchar) == sizeof(c_int): - dll.my_wcsdup.restype = POINTER(c_int) - elif sizeof(c_wchar) == sizeof(c_long): - dll.my_wcsdup.restype = POINTER(c_long) - else: - return - res = dll.my_wcsdup(s) - tmpl = list(range(ord("a"), ord("z")+1)) - self.assertEqual(res[:len(s)-1], tmpl) - self.assertEqual(res[:len(s)-1:], tmpl) - self.assertEqual(res[len(s)-2:-1:-1], tmpl[::-1]) - self.assertEqual(res[len(s)-2:5:-7], tmpl[:5:-7]) - dll.my_free(res) + if sizeof(c_wchar) == sizeof(c_short): + dll.my_wcsdup.restype = POINTER(c_short) + elif sizeof(c_wchar) == sizeof(c_int): + dll.my_wcsdup.restype = POINTER(c_int) + elif sizeof(c_wchar) == sizeof(c_long): + dll.my_wcsdup.restype = POINTER(c_long) + else: + self.skipTest('Pointers to c_wchar are not supported') + res = dll.my_wcsdup(s) + tmpl = list(range(ord("a"), ord("z")+1)) + self.assertEqual(res[:len(s)-1], tmpl) + self.assertEqual(res[:len(s)-1:], tmpl) + self.assertEqual(res[len(s)-2:-1:-1], tmpl[::-1]) + self.assertEqual(res[len(s)-2:5:-7], tmpl[:5:-7]) + dll.my_free(res) ################################################################ diff --git a/Lib/ctypes/test/test_strings.py b/Lib/ctypes/test/test_strings.py --- a/Lib/ctypes/test/test_strings.py +++ b/Lib/ctypes/test/test_strings.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol class StringArrayTestCase(unittest.TestCase): def test(self): @@ -53,36 +54,33 @@ ## print BUF.from_param(c_char_p("python")) ## print BUF.from_param(BUF(*"pyth")) -try: - c_wchar -except NameError: - pass -else: - class WStringArrayTestCase(unittest.TestCase): - def test(self): - BUF = c_wchar * 4 + at need_symbol('c_wchar') +class WStringArrayTestCase(unittest.TestCase): + def test(self): + BUF = c_wchar * 4 - buf = BUF("a", "b", "c") - self.assertEqual(buf.value, "abc") + buf = BUF("a", "b", "c") + self.assertEqual(buf.value, "abc") - buf.value = "ABCD" - self.assertEqual(buf.value, "ABCD") + buf.value = "ABCD" + self.assertEqual(buf.value, "ABCD") - buf.value = "x" - self.assertEqual(buf.value, "x") + buf.value = "x" + self.assertEqual(buf.value, "x") - buf[1] = "Z" - self.assertEqual(buf.value, "xZCD") + buf[1] = "Z" + self.assertEqual(buf.value, "xZCD") - @unittest.skipIf(sizeof(c_wchar) < 4, - "sizeof(wchar_t) is smaller than 4 bytes") - def test_nonbmp(self): - u = chr(0x10ffff) - w = c_wchar(u) - self.assertEqual(w.value, u) + @unittest.skipIf(sizeof(c_wchar) < 4, + "sizeof(wchar_t) is smaller than 4 bytes") + def test_nonbmp(self): + u = chr(0x10ffff) + w = c_wchar(u) + self.assertEqual(w.value, u) class StringTestCase(unittest.TestCase): - def XX_test_basic_strings(self): + @unittest.skip('test disabled') + def test_basic_strings(self): cs = c_string("abcdef") # Cannot call len on a c_string any longer @@ -108,7 +106,8 @@ self.assertRaises(TypeError, c_string, "123") - def XX_test_sized_strings(self): + @unittest.skip('test disabled') + def test_sized_strings(self): # New in releases later than 0.4.0: self.assertRaises(TypeError, c_string, None) @@ -125,7 +124,8 @@ self.assertEqual(c_string(2).raw[-1], "\000") self.assertEqual(len(c_string(2).raw), 2) - def XX_test_initialized_strings(self): + @unittest.skip('test disabled') + def test_initialized_strings(self): self.assertEqual(c_string("ab", 4).raw[:2], "ab") self.assertEqual(c_string("ab", 4).raw[:2:], "ab") @@ -134,7 +134,8 @@ self.assertEqual(c_string("ab", 4).raw[-1], "\000") self.assertEqual(c_string("ab", 2).raw, "a\000") - def XX_test_toolong(self): + @unittest.skip('test disabled') + def test_toolong(self): cs = c_string("abcdef") # Much too long string: self.assertRaises(ValueError, setattr, cs, "value", "123456789012345") @@ -142,54 +143,53 @@ # One char too long values: self.assertRaises(ValueError, setattr, cs, "value", "1234567") -## def test_perf(self): -## check_perf() + @unittest.skip('test disabled') + def test_perf(self): + check_perf() -try: - c_wchar -except NameError: - pass -else: - class WStringTestCase(unittest.TestCase): - def test_wchar(self): - c_wchar("x") - repr(byref(c_wchar("x"))) - c_wchar("x") + at need_symbol('c_wchar') +class WStringTestCase(unittest.TestCase): + def test_wchar(self): + c_wchar("x") + repr(byref(c_wchar("x"))) + c_wchar("x") - def X_test_basic_wstrings(self): - cs = c_wstring("abcdef") + @unittest.skip('test disabled') + def test_basic_wstrings(self): + cs = c_wstring("abcdef") - # XXX This behaviour is about to change: - # len returns the size of the internal buffer in bytes. - # This includes the terminating NUL character. - self.assertEqual(sizeof(cs), 14) + # XXX This behaviour is about to change: + # len returns the size of the internal buffer in bytes. + # This includes the terminating NUL character. + self.assertEqual(sizeof(cs), 14) - # The value property is the string up to the first terminating NUL. - self.assertEqual(cs.value, "abcdef") - self.assertEqual(c_wstring("abc\000def").value, "abc") + # The value property is the string up to the first terminating NUL. + self.assertEqual(cs.value, "abcdef") + self.assertEqual(c_wstring("abc\000def").value, "abc") - self.assertEqual(c_wstring("abc\000def").value, "abc") + self.assertEqual(c_wstring("abc\000def").value, "abc") - # The raw property is the total buffer contents: - self.assertEqual(cs.raw, "abcdef\000") - self.assertEqual(c_wstring("abc\000def").raw, "abc\000def\000") + # The raw property is the total buffer contents: + self.assertEqual(cs.raw, "abcdef\000") + self.assertEqual(c_wstring("abc\000def").raw, "abc\000def\000") - # We can change the value: - cs.value = "ab" - self.assertEqual(cs.value, "ab") - self.assertEqual(cs.raw, "ab\000\000\000\000\000") + # We can change the value: + cs.value = "ab" + self.assertEqual(cs.value, "ab") + self.assertEqual(cs.raw, "ab\000\000\000\000\000") - self.assertRaises(TypeError, c_wstring, "123") - self.assertRaises(ValueError, c_wstring, 0) + self.assertRaises(TypeError, c_wstring, "123") + self.assertRaises(ValueError, c_wstring, 0) - def X_test_toolong(self): - cs = c_wstring("abcdef") - # Much too long string: - self.assertRaises(ValueError, setattr, cs, "value", "123456789012345") + @unittest.skip('test disabled') + def test_toolong(self): + cs = c_wstring("abcdef") + # Much too long string: + self.assertRaises(ValueError, setattr, cs, "value", "123456789012345") - # One char too long values: - self.assertRaises(ValueError, setattr, cs, "value", "1234567") + # One char too long values: + self.assertRaises(ValueError, setattr, cs, "value", "1234567") def run_test(rep, msg, func, arg): diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol from struct import calcsize import _testcapi @@ -291,12 +292,8 @@ self.assertEqual(p.phone.number, b"5678") self.assertEqual(p.age, 5) + @need_symbol('c_wchar') def test_structures_with_wchar(self): - try: - c_wchar - except NameError: - return # no unicode - class PersonW(Structure): _fields_ = [("name", c_wchar * 12), ("age", c_int)] @@ -354,14 +351,14 @@ except Exception as detail: return detail.__class__, str(detail) - -## def test_subclass_creation(self): -## meta = type(Structure) -## # same as 'class X(Structure): pass' -## # fails, since we need either a _fields_ or a _abstract_ attribute -## cls, msg = self.get_except(meta, "X", (Structure,), {}) -## self.assertEqual((cls, msg), -## (AttributeError, "class must define a '_fields_' attribute")) + @unittest.skip('test disabled') + def test_subclass_creation(self): + meta = type(Structure) + # same as 'class X(Structure): pass' + # fails, since we need either a _fields_ or a _abstract_ attribute + cls, msg = self.get_except(meta, "X", (Structure,), {}) + self.assertEqual((cls, msg), + (AttributeError, "class must define a '_fields_' attribute")) def test_abstract_class(self): class X(Structure): diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py --- a/Lib/ctypes/test/test_unicode.py +++ b/Lib/ctypes/test/test_unicode.py @@ -1,58 +1,55 @@ import unittest import ctypes +from ctypes.test import need_symbol -try: - ctypes.c_wchar -except AttributeError: - pass -else: - import _ctypes_test +import _ctypes_test - class UnicodeTestCase(unittest.TestCase): - def test_wcslen(self): - dll = ctypes.CDLL(_ctypes_test.__file__) - wcslen = dll.my_wcslen - wcslen.argtypes = [ctypes.c_wchar_p] + at need_symbol('c_wchar') +class UnicodeTestCase(unittest.TestCase): + def test_wcslen(self): + dll = ctypes.CDLL(_ctypes_test.__file__) + wcslen = dll.my_wcslen + wcslen.argtypes = [ctypes.c_wchar_p] - self.assertEqual(wcslen("abc"), 3) - self.assertEqual(wcslen("ab\u2070"), 3) - self.assertRaises(ctypes.ArgumentError, wcslen, b"ab\xe4") + self.assertEqual(wcslen("abc"), 3) + self.assertEqual(wcslen("ab\u2070"), 3) + self.assertRaises(ctypes.ArgumentError, wcslen, b"ab\xe4") - def test_buffers(self): - buf = ctypes.create_unicode_buffer("abc") - self.assertEqual(len(buf), 3+1) + def test_buffers(self): + buf = ctypes.create_unicode_buffer("abc") + self.assertEqual(len(buf), 3+1) - buf = ctypes.create_unicode_buffer("ab\xe4\xf6\xfc") - self.assertEqual(buf[:], "ab\xe4\xf6\xfc\0") - self.assertEqual(buf[::], "ab\xe4\xf6\xfc\0") - self.assertEqual(buf[::-1], '\x00\xfc\xf6\xe4ba') - self.assertEqual(buf[::2], 'a\xe4\xfc') - self.assertEqual(buf[6:5:-1], "") + buf = ctypes.create_unicode_buffer("ab\xe4\xf6\xfc") + self.assertEqual(buf[:], "ab\xe4\xf6\xfc\0") + self.assertEqual(buf[::], "ab\xe4\xf6\xfc\0") + self.assertEqual(buf[::-1], '\x00\xfc\xf6\xe4ba') + self.assertEqual(buf[::2], 'a\xe4\xfc') + self.assertEqual(buf[6:5:-1], "") - func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p +func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p - class StringTestCase(UnicodeTestCase): - def setUp(self): - func.argtypes = [ctypes.c_char_p] - func.restype = ctypes.c_char_p +class StringTestCase(UnicodeTestCase): + def setUp(self): + func.argtypes = [ctypes.c_char_p] + func.restype = ctypes.c_char_p - def tearDown(self): - func.argtypes = None - func.restype = ctypes.c_int + def tearDown(self): + func.argtypes = None + func.restype = ctypes.c_int - def test_func(self): - self.assertEqual(func(b"abc\xe4"), b"abc\xe4") + def test_func(self): + self.assertEqual(func(b"abc\xe4"), b"abc\xe4") - def test_buffers(self): - buf = ctypes.create_string_buffer(b"abc") - self.assertEqual(len(buf), 3+1) + def test_buffers(self): + buf = ctypes.create_string_buffer(b"abc") + self.assertEqual(len(buf), 3+1) - buf = ctypes.create_string_buffer(b"ab\xe4\xf6\xfc") - self.assertEqual(buf[:], b"ab\xe4\xf6\xfc\0") - self.assertEqual(buf[::], b"ab\xe4\xf6\xfc\0") - self.assertEqual(buf[::-1], b'\x00\xfc\xf6\xe4ba') - self.assertEqual(buf[::2], b'a\xe4\xfc') - self.assertEqual(buf[6:5:-1], b"") + buf = ctypes.create_string_buffer(b"ab\xe4\xf6\xfc") + self.assertEqual(buf[:], b"ab\xe4\xf6\xfc\0") + self.assertEqual(buf[::], b"ab\xe4\xf6\xfc\0") + self.assertEqual(buf[::-1], b'\x00\xfc\xf6\xe4ba') + self.assertEqual(buf[::2], b'a\xe4\xfc') + self.assertEqual(buf[6:5:-1], b"") if __name__ == '__main__': diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py --- a/Lib/ctypes/test/test_values.py +++ b/Lib/ctypes/test/test_values.py @@ -3,6 +3,7 @@ """ import unittest +import sys from ctypes import * import _ctypes_test @@ -27,62 +28,77 @@ ctdll = CDLL(_ctypes_test.__file__) self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") - class Win_ValuesTestCase(unittest.TestCase): - """This test only works when python itself is a dll/shared library""" + at unittest.skipUnless(sys.platform == 'win32', 'Windows-specific test') +class Win_ValuesTestCase(unittest.TestCase): + """This test only works when python itself is a dll/shared library""" - def test_optimizeflag(self): - # This test accesses the Py_OptimizeFlag intger, which is - # exported by the Python dll. + def test_optimizeflag(self): + # This test accesses the Py_OptimizeFlag intger, which is + # exported by the Python dll. - # It's value is set depending on the -O and -OO flags: - # if not given, it is 0 and __debug__ is 1. - # If -O is given, the flag is 1, for -OO it is 2. - # docstrings are also removed in the latter case. - opt = c_int.in_dll(pydll, "Py_OptimizeFlag").value - if __debug__: - self.assertEqual(opt, 0) - elif ValuesTestCase.__doc__ is not None: - self.assertEqual(opt, 1) - else: - self.assertEqual(opt, 2) + # It's value is set depending on the -O and -OO flags: + # if not given, it is 0 and __debug__ is 1. + # If -O is given, the flag is 1, for -OO it is 2. + # docstrings are also removed in the latter case. + opt = c_int.in_dll(pythonapi, "Py_OptimizeFlag").value + if __debug__: + self.assertEqual(opt, 0) + elif ValuesTestCase.__doc__ is not None: + self.assertEqual(opt, 1) + else: + self.assertEqual(opt, 2) - def test_frozentable(self): - # Python exports a PyImport_FrozenModules symbol. This is a - # pointer to an array of struct _frozen entries. The end of the - # array is marked by an entry containing a NULL name and zero - # size. + def test_frozentable(self): + # Python exports a PyImport_FrozenModules symbol. This is a + # pointer to an array of struct _frozen entries. The end of the + # array is marked by an entry containing a NULL name and zero + # size. - # In standard Python, this table contains a __hello__ - # module, and a __phello__ package containing a spam - # module. - class struct_frozen(Structure): - _fields_ = [("name", c_char_p), - ("code", POINTER(c_ubyte)), - ("size", c_int)] - FrozenTable = POINTER(struct_frozen) + # In standard Python, this table contains a __hello__ + # module, and a __phello__ package containing a spam + # module. + class struct_frozen(Structure): + _fields_ = [("name", c_char_p), + ("code", POINTER(c_ubyte)), + ("size", c_int)] + FrozenTable = POINTER(struct_frozen) - ft = FrozenTable.in_dll(pydll, "PyImport_FrozenModules") - # ft is a pointer to the struct_frozen entries: - items = [] - for entry in ft: - # This is dangerous. We *can* iterate over a pointer, but - # the loop will not terminate (maybe with an access - # violation;-) because the pointer instance has no size. - if entry.name is None: - break - items.append((entry.name, entry.size)) - import sys - if sys.version_info[:2] >= (2, 3): - expected = [("__hello__", 104), ("__phello__", -104), ("__phello__.spam", 104)] - else: - expected = [("__hello__", 100), ("__phello__", -100), ("__phello__.spam", 100)] - self.assertEqual(items, expected) + ft = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules") + # ft is a pointer to the struct_frozen entries: + items = [] + # _frozen_importlib changes size whenever importlib._bootstrap + # changes, so it gets a special case. We should make sure it's + # found, but don't worry about its size too much. + _fzn_implib_seen = False + for entry in ft: + # This is dangerous. We *can* iterate over a pointer, but + # the loop will not terminate (maybe with an access + # violation;-) because the pointer instance has no size. + if entry.name is None: + break - from ctypes import _pointer_type_cache - del _pointer_type_cache[struct_frozen] + if entry.name == b'_frozen_importlib': + _fzn_implib_seen = True + self.assertTrue(entry.size, + "_frozen_importlib was reported as having no size") + continue + items.append((entry.name, entry.size)) - def test_undefined(self): - self.assertRaises(ValueError, c_int.in_dll, pydll, "Undefined_Symbol") + expected = [(b"__hello__", 161), + (b"__phello__", -161), + (b"__phello__.spam", 161), + ] + self.assertEqual(items, expected) + + self.assertTrue(_fzn_implib_seen, + "_frozen_importlib wasn't found in PyImport_FrozenModules") + + from ctypes import _pointer_type_cache + del _pointer_type_cache[struct_frozen] + + def test_undefined(self): + self.assertRaises(ValueError, c_int.in_dll, pythonapi, + "Undefined_Symbol") if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py --- a/Lib/ctypes/test/test_win32.py +++ b/Lib/ctypes/test/test_win32.py @@ -1,99 +1,102 @@ # Windows specific tests from ctypes import * -from ctypes.test import is_resource_enabled +from ctypes.test import requires import unittest, sys from test import support import _ctypes_test -if sys.platform == "win32" and sizeof(c_void_p) == sizeof(c_int): - # Only windows 32-bit has different calling conventions. +# Only windows 32-bit has different calling conventions. + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + at unittest.skipUnless(sizeof(c_void_p) == sizeof(c_int), + "sizeof c_void_p and c_int differ") +class WindowsTestCase(unittest.TestCase): + def test_callconv_1(self): + # Testing stdcall function - class WindowsTestCase(unittest.TestCase): - def test_callconv_1(self): - # Testing stdcall function + IsWindow = windll.user32.IsWindow + # ValueError: Procedure probably called with not enough arguments + # (4 bytes missing) + self.assertRaises(ValueError, IsWindow) - IsWindow = windll.user32.IsWindow - # ValueError: Procedure probably called with not enough arguments (4 bytes missing) - self.assertRaises(ValueError, IsWindow) + # This one should succeed... + self.assertEqual(0, IsWindow(0)) - # This one should succeed... - self.assertEqual(0, IsWindow(0)) + # ValueError: Procedure probably called with too many arguments + # (8 bytes in excess) + self.assertRaises(ValueError, IsWindow, 0, 0, 0) - # ValueError: Procedure probably called with too many arguments (8 bytes in excess) - self.assertRaises(ValueError, IsWindow, 0, 0, 0) + def test_callconv_2(self): + # Calling stdcall function as cdecl - def test_callconv_2(self): - # Calling stdcall function as cdecl + IsWindow = cdll.user32.IsWindow - IsWindow = cdll.user32.IsWindow + # ValueError: Procedure called with not enough arguments + # (4 bytes missing) or wrong calling convention + self.assertRaises(ValueError, IsWindow, None) - # ValueError: Procedure called with not enough arguments (4 bytes missing) - # or wrong calling convention - self.assertRaises(ValueError, IsWindow, None) + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class FunctionCallTestCase(unittest.TestCase): + @requires("SEH") + def test_SEH(self): + # Call functions with invalid arguments, and make sure + # that access violations are trapped and raise an + # exception. + self.assertRaises(OSError, windll.kernel32.GetModuleHandleA, 32) -if sys.platform == "win32": - class FunctionCallTestCase(unittest.TestCase): + def test_noargs(self): + # This is a special case on win32 x64 + windll.user32.GetDesktopWindow() - if is_resource_enabled("SEH"): - def test_SEH(self): - # Call functions with invalid arguments, and make sure - # that access violations are trapped and raise an - # exception. - self.assertRaises(OSError, windll.kernel32.GetModuleHandleA, 32) + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class TestWintypes(unittest.TestCase): + def test_HWND(self): + from ctypes import wintypes + self.assertEqual(sizeof(wintypes.HWND), sizeof(c_void_p)) - def test_noargs(self): - # This is a special case on win32 x64 - windll.user32.GetDesktopWindow() + def test_PARAM(self): + from ctypes import wintypes + self.assertEqual(sizeof(wintypes.WPARAM), + sizeof(c_void_p)) + self.assertEqual(sizeof(wintypes.LPARAM), + sizeof(c_void_p)) - class TestWintypes(unittest.TestCase): - def test_HWND(self): - from ctypes import wintypes - self.assertEqual(sizeof(wintypes.HWND), sizeof(c_void_p)) + def test_COMError(self): + from _ctypes import COMError + if support.HAVE_DOCSTRINGS: + self.assertEqual(COMError.__doc__, + "Raised when a COM method call failed.") - def test_PARAM(self): - from ctypes import wintypes - self.assertEqual(sizeof(wintypes.WPARAM), - sizeof(c_void_p)) - self.assertEqual(sizeof(wintypes.LPARAM), - sizeof(c_void_p)) + ex = COMError(-1, "text", ("details",)) + self.assertEqual(ex.hresult, -1) + self.assertEqual(ex.text, "text") + self.assertEqual(ex.details, ("details",)) - def test_COMError(self): - from _ctypes import COMError - if support.HAVE_DOCSTRINGS: - self.assertEqual(COMError.__doc__, - "Raised when a COM method call failed.") + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class TestWinError(unittest.TestCase): + def test_winerror(self): + # see Issue 16169 + import errno + ERROR_INVALID_PARAMETER = 87 + msg = FormatError(ERROR_INVALID_PARAMETER).strip() + args = (errno.EINVAL, msg, None, ERROR_INVALID_PARAMETER) - ex = COMError(-1, "text", ("details",)) - self.assertEqual(ex.hresult, -1) - self.assertEqual(ex.text, "text") - self.assertEqual(ex.details, ("details",)) + e = WinError(ERROR_INVALID_PARAMETER) + self.assertEqual(e.args, args) + self.assertEqual(e.errno, errno.EINVAL) + self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER) - class TestWinError(unittest.TestCase): - def test_winerror(self): - # see Issue 16169 - import errno - ERROR_INVALID_PARAMETER = 87 - msg = FormatError(ERROR_INVALID_PARAMETER).strip() - args = (errno.EINVAL, msg, None, ERROR_INVALID_PARAMETER) - - e = WinError(ERROR_INVALID_PARAMETER) - self.assertEqual(e.args, args) - self.assertEqual(e.errno, errno.EINVAL) - self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER) - - windll.kernel32.SetLastError(ERROR_INVALID_PARAMETER) - try: - raise WinError() - except OSError as exc: - e = exc - self.assertEqual(e.args, args) - self.assertEqual(e.errno, errno.EINVAL) - self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER) + windll.kernel32.SetLastError(ERROR_INVALID_PARAMETER) + try: + raise WinError() + except OSError as exc: + e = exc + self.assertEqual(e.args, args) + self.assertEqual(e.errno, errno.EINVAL) + self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER) class Structures(unittest.TestCase): - def test_struct_by_value(self): class POINT(Structure): _fields_ = [("x", c_long), diff --git a/Lib/ctypes/test/test_wintypes.py b/Lib/ctypes/test/test_wintypes.py --- a/Lib/ctypes/test/test_wintypes.py +++ b/Lib/ctypes/test/test_wintypes.py @@ -1,14 +1,12 @@ import sys import unittest -if not sys.platform.startswith('win'): - raise unittest.SkipTest('Windows-only test') +from ctypes import * -from ctypes import * -from ctypes import wintypes - + at unittest.skipUnless(sys.platform.startswith('win'), 'Windows-only test') class WinTypesTest(unittest.TestCase): def test_variant_bool(self): + from ctypes import wintypes # reads 16-bits from memory, anything non-zero is True for true_value in (1, 32767, 32768, 65535, 65537): true = POINTER(c_int16)(c_int16(true_value)) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -573,6 +573,9 @@ Tests ----- +- Issue #19493: Refactored the ctypes test package to skip tests explicitly + rather than silently. + - Issue #18492: All resources are now allowed when tests are not run by regrtest.py. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 20:58:36 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 13 Jun 2014 20:58:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNzI2?= =?utf-8?q?=3A_Remove_unnecessary_and_contextually_wrong_line=2E?= Message-ID: <3gqrn86CCMz7Ljb@mail.python.org> http://hg.python.org/cpython/rev/cd08e366d619 changeset: 91162:cd08e366d619 branch: 2.7 parent: 91143:55fed3eae14b user: Terry Jan Reedy date: Fri Jun 13 14:57:44 2014 -0400 summary: Issue #21726: Remove unnecessary and contextually wrong line. files: Doc/distutils/examples.rst | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Doc/distutils/examples.rst b/Doc/distutils/examples.rst --- a/Doc/distutils/examples.rst +++ b/Doc/distutils/examples.rst @@ -193,9 +193,6 @@ packages=['foobar', 'foobar.subfoo'], ) -(Again, the empty string in :option:`package_dir` stands for the current -directory.) - .. _single-ext: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 20:58:38 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 13 Jun 2014 20:58:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzI2?= =?utf-8?q?=3A_Remove_unnecessary_and_contextually_wrong_line=2E?= Message-ID: <3gqrnB0hrpz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/efa32fcd7a0b changeset: 91163:efa32fcd7a0b branch: 3.4 parent: 91160:6f63fff5c120 user: Terry Jan Reedy date: Fri Jun 13 14:57:51 2014 -0400 summary: Issue #21726: Remove unnecessary and contextually wrong line. files: Doc/distutils/examples.rst | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Doc/distutils/examples.rst b/Doc/distutils/examples.rst --- a/Doc/distutils/examples.rst +++ b/Doc/distutils/examples.rst @@ -193,9 +193,6 @@ packages=['foobar', 'foobar.subfoo'], ) -(Again, the empty string in :option:`package_dir` stands for the current -directory.) - .. _single-ext: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 20:58:39 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 13 Jun 2014 20:58:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gqrnC2Xkdz7Ljs@mail.python.org> http://hg.python.org/cpython/rev/c99fc7e965b5 changeset: 91164:c99fc7e965b5 parent: 91161:86d14cf2a6a8 parent: 91163:efa32fcd7a0b user: Terry Jan Reedy date: Fri Jun 13 14:58:09 2014 -0400 summary: Merge with 3.4 files: Doc/distutils/examples.rst | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Doc/distutils/examples.rst b/Doc/distutils/examples.rst --- a/Doc/distutils/examples.rst +++ b/Doc/distutils/examples.rst @@ -193,9 +193,6 @@ packages=['foobar', 'foobar.subfoo'], ) -(Again, the empty string in :option:`package_dir` stands for the current -directory.) - .. _single-ext: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 21:21:25 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 13 Jun 2014 21:21:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzMw?= =?utf-8?q?=3A_Add_no-thread_skip_in_test=5Fsocket=2E_Patch_by_Berker_Peks?= =?utf-8?b?YWcu?= Message-ID: <3gqsHT19pqz7LjV@mail.python.org> http://hg.python.org/cpython/rev/f44275c66fcf changeset: 91165:f44275c66fcf branch: 3.4 parent: 91163:efa32fcd7a0b user: Terry Jan Reedy date: Fri Jun 13 15:20:45 2014 -0400 summary: Issue #21730: Add no-thread skip in test_socket. Patch by Berker Peksag. files: Lib/test/test_socket.py | 1 + 1 files changed, 1 insertions(+), 0 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 @@ -1456,6 +1456,7 @@ @unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.') + at unittest.skipUnless(thread, 'Threading required for this test.') class CANTest(ThreadedCANSocketTest): def __init__(self, methodName='runTest'): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 21:21:26 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 13 Jun 2014 21:21:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gqsHV2ySjz7Ljr@mail.python.org> http://hg.python.org/cpython/rev/273e3d52c0c9 changeset: 91166:273e3d52c0c9 parent: 91164:c99fc7e965b5 parent: 91165:f44275c66fcf user: Terry Jan Reedy date: Fri Jun 13 15:21:01 2014 -0400 summary: Merge with 3.4 files: Lib/test/test_socket.py | 1 + 1 files changed, 1 insertions(+), 0 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 @@ -1461,6 +1461,7 @@ @unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.') + at unittest.skipUnless(thread, 'Threading required for this test.') class CANTest(ThreadedCANSocketTest): def __init__(self, methodName='runTest'): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 21:40:43 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 13 Jun 2014 21:40:43 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5NDkz?= =?utf-8?q?=3A_Backport_6f63fff5c120?= Message-ID: <3gqsjl0P26z7Ljc@mail.python.org> http://hg.python.org/cpython/rev/08a2b36f6287 changeset: 91167:08a2b36f6287 branch: 2.7 parent: 91162:cd08e366d619 user: Zachary Ware date: Fri Jun 13 14:40:16 2014 -0500 summary: Issue #19493: Backport 6f63fff5c120 files: Lib/ctypes/test/__init__.py | 10 +- Lib/ctypes/test/test_arrays.py | 26 +- Lib/ctypes/test/test_as_parameter.py | 6 +- Lib/ctypes/test/test_bitfields.py | 19 +- Lib/ctypes/test/test_buffers.py | 60 +- Lib/ctypes/test/test_byteswap.py | 3 +- Lib/ctypes/test/test_callbacks.py | 47 +- Lib/ctypes/test/test_cast.py | 15 +- Lib/ctypes/test/test_cfuncs.py | 9 +- Lib/ctypes/test/test_checkretval.py | 15 +- Lib/ctypes/test/test_errcheck.py | 19 - Lib/ctypes/test/test_find.py | 69 ++-- Lib/ctypes/test/test_functions.py | 70 ++-- Lib/ctypes/test/test_integers.py | 5 - Lib/ctypes/test/test_keeprefs.py | 3 +- Lib/ctypes/test/test_loading.py | 138 ++++---- Lib/ctypes/test/test_macholib.py | 24 +- Lib/ctypes/test/test_memfunctions.py | 44 +- Lib/ctypes/test/test_numbers.py | 39 +- Lib/ctypes/test/test_objects.py | 11 +- Lib/ctypes/test/test_parameters.py | 19 +- Lib/ctypes/test/test_prototypes.py | 84 ++-- Lib/ctypes/test/test_python_api.py | 32 +- Lib/ctypes/test/test_random_things.py | 27 +- Lib/ctypes/test/test_slicing.py | 71 ++-- Lib/ctypes/test/test_strings.py | 114 +++--- Lib/ctypes/test/test_structures.py | 23 +- Lib/ctypes/test/test_unicode.py | 219 +++++++------ Lib/ctypes/test/test_values.py | 100 +++--- Lib/ctypes/test/test_win32.py | 106 +++--- Lib/ctypes/test/test_wintypes.py | 8 +- Misc/NEWS | 3 + 32 files changed, 704 insertions(+), 734 deletions(-) diff --git a/Lib/ctypes/test/__init__.py b/Lib/ctypes/test/__init__.py --- a/Lib/ctypes/test/__init__.py +++ b/Lib/ctypes/test/__init__.py @@ -2,7 +2,15 @@ use_resources = [] -class ResourceDenied(Exception): +import ctypes +ctypes_symbols = dir(ctypes) + +def need_symbol(name): + return unittest.skipUnless(name in ctypes_symbols, + '{!r} is required'.format(name)) + + +class ResourceDenied(unittest.SkipTest): """Test skipped because it requested a disallowed resource. This is raised when a test calls requires() for a resource that diff --git a/Lib/ctypes/test/test_arrays.py b/Lib/ctypes/test/test_arrays.py --- a/Lib/ctypes/test/test_arrays.py +++ b/Lib/ctypes/test/test_arrays.py @@ -1,6 +1,8 @@ import unittest from ctypes import * +from ctypes.test import need_symbol + formats = "bBhHiIlLqQfd" formats = c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, \ @@ -101,20 +103,16 @@ self.assertEqual(sz[1:4:2], "o") self.assertEqual(sz.value, "foo") - try: - create_unicode_buffer - except NameError: - pass - else: - def test_from_addressW(self): - p = create_unicode_buffer("foo") - sz = (c_wchar * 3).from_address(addressof(p)) - self.assertEqual(sz[:], "foo") - self.assertEqual(sz[::], "foo") - self.assertEqual(sz[::-1], "oof") - self.assertEqual(sz[::3], "f") - self.assertEqual(sz[1:4:2], "o") - self.assertEqual(sz.value, "foo") + @need_symbol('create_unicode_buffer') + def test_from_addressW(self): + p = create_unicode_buffer("foo") + sz = (c_wchar * 3).from_address(addressof(p)) + self.assertEqual(sz[:], "foo") + self.assertEqual(sz[::], "foo") + self.assertEqual(sz[::-1], "oof") + self.assertEqual(sz[::3], "f") + self.assertEqual(sz[1:4:2], "o") + self.assertEqual(sz.value, "foo") def test_cache(self): # Array types are cached internally in the _ctypes extension, diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py --- a/Lib/ctypes/test/test_as_parameter.py +++ b/Lib/ctypes/test/test_as_parameter.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test dll = CDLL(_ctypes_test.__file__) @@ -17,11 +18,8 @@ def wrap(self, param): return param + @need_symbol('c_wchar') def test_wchar_parm(self): - try: - c_wchar - except NameError: - return f = dll._testfunc_i_bhilfd f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double] result = f(self.wrap(1), self.wrap(u"x"), self.wrap(3), self.wrap(4), self.wrap(5.0), self.wrap(6.0)) diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py --- a/Lib/ctypes/test/test_bitfields.py +++ b/Lib/ctypes/test/test_bitfields.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest import os @@ -127,20 +128,18 @@ result = self.fail_fields(("a", c_char, 1)) self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_char')) - try: - c_wchar - except NameError: - pass - else: - result = self.fail_fields(("a", c_wchar, 1)) - self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_wchar')) - class Dummy(Structure): _fields_ = [] result = self.fail_fields(("a", Dummy, 1)) self.assertEqual(result, (TypeError, 'bit fields not allowed for type Dummy')) + @need_symbol('c_wchar') + def test_c_wchar(self): + result = self.fail_fields(("a", c_wchar, 1)) + self.assertEqual(result, + (TypeError, 'bit fields not allowed for type c_wchar')) + def test_single_bitfield_size(self): for c_typ in int_types: result = self.fail_fields(("a", c_typ, -1)) @@ -240,7 +239,7 @@ _anonymous_ = ["_"] _fields_ = [("_", X)] - @unittest.skipUnless(hasattr(ctypes, "c_uint32"), "c_int32 is required") + @need_symbol('c_uint32') def test_uint32(self): class X(Structure): _fields_ = [("a", c_uint32, 32)] @@ -250,7 +249,7 @@ x.a = 0xFDCBA987 self.assertEqual(x.a, 0xFDCBA987) - @unittest.skipUnless(hasattr(ctypes, "c_uint64"), "c_int64 is required") + @need_symbol('c_uint64') def test_uint64(self): class X(Structure): _fields_ = [("a", c_uint64, 64)] diff --git a/Lib/ctypes/test/test_buffers.py b/Lib/ctypes/test/test_buffers.py --- a/Lib/ctypes/test/test_buffers.py +++ b/Lib/ctypes/test/test_buffers.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest class StringBufferTestCase(unittest.TestCase): @@ -36,39 +37,36 @@ self.assertEqual(b[::2], "ac") self.assertEqual(b[::5], "a") - try: - c_wchar - except NameError: - pass - else: - def test_unicode_buffer(self): - b = create_unicode_buffer(32) - self.assertEqual(len(b), 32) - self.assertEqual(sizeof(b), 32 * sizeof(c_wchar)) - self.assertIs(type(b[0]), unicode) + @need_symbol('c_wchar') + def test_unicode_buffer(self): + b = create_unicode_buffer(32) + self.assertEqual(len(b), 32) + self.assertEqual(sizeof(b), 32 * sizeof(c_wchar)) + self.assertIs(type(b[0]), unicode) - b = create_unicode_buffer(u"abc") - self.assertEqual(len(b), 4) # trailing nul char - self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) - self.assertIs(type(b[0]), unicode) - self.assertEqual(b[0], u"a") - self.assertEqual(b[:], "abc\0") - self.assertEqual(b[::], "abc\0") - self.assertEqual(b[::-1], "\0cba") - self.assertEqual(b[::2], "ac") - self.assertEqual(b[::5], "a") + b = create_unicode_buffer(u"abc") + self.assertEqual(len(b), 4) # trailing nul char + self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) + self.assertIs(type(b[0]), unicode) + self.assertEqual(b[0], u"a") + self.assertEqual(b[:], "abc\0") + self.assertEqual(b[::], "abc\0") + self.assertEqual(b[::-1], "\0cba") + self.assertEqual(b[::2], "ac") + self.assertEqual(b[::5], "a") - def test_unicode_conversion(self): - b = create_unicode_buffer("abc") - self.assertEqual(len(b), 4) # trailing nul char - self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) - self.assertIs(type(b[0]), unicode) - self.assertEqual(b[0], u"a") - self.assertEqual(b[:], "abc\0") - self.assertEqual(b[::], "abc\0") - self.assertEqual(b[::-1], "\0cba") - self.assertEqual(b[::2], "ac") - self.assertEqual(b[::5], "a") + @need_symbol('c_wchar') + def test_unicode_conversion(self): + b = create_unicode_buffer("abc") + self.assertEqual(len(b), 4) # trailing nul char + self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) + self.assertIs(type(b[0]), unicode) + self.assertEqual(b[0], u"a") + self.assertEqual(b[:], "abc\0") + self.assertEqual(b[::], "abc\0") + self.assertEqual(b[::-1], "\0cba") + self.assertEqual(b[::2], "ac") + self.assertEqual(b[::5], "a") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_byteswap.py b/Lib/ctypes/test/test_byteswap.py --- a/Lib/ctypes/test/test_byteswap.py +++ b/Lib/ctypes/test/test_byteswap.py @@ -14,7 +14,8 @@ # For Structures and Unions, these types are created on demand. class Test(unittest.TestCase): - def X_test(self): + @unittest.skip('test disabled') + def test_X(self): print >> sys.stderr, sys.byteorder for i in range(32): bits = BITS() diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test class Callbacks(unittest.TestCase): @@ -94,9 +95,10 @@ # disabled: would now (correctly) raise a RuntimeWarning about # a memory leak. A callback function cannot return a non-integral # C type without causing a memory leak. -## def test_char_p(self): -## self.check_type(c_char_p, "abc") -## self.check_type(c_char_p, "def") + @unittest.skip('test disabled') + def test_char_p(self): + self.check_type(c_char_p, "abc") + self.check_type(c_char_p, "def") def test_pyobject(self): o = () @@ -148,13 +150,12 @@ CFUNCTYPE(None)(lambda x=Nasty(): None) -try: - WINFUNCTYPE -except NameError: - pass -else: - class StdcallCallbacks(Callbacks): + at need_symbol('WINFUNCTYPE') +class StdcallCallbacks(Callbacks): + try: functype = WINFUNCTYPE + except NameError: + pass ################################################################ @@ -184,7 +185,7 @@ from ctypes.util import find_library libc_path = find_library("c") if not libc_path: - return # cannot test + self.skipTest('could not find libc') libc = CDLL(libc_path) @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) @@ -196,23 +197,19 @@ libc.qsort(array, len(array), sizeof(c_int), cmp_func) self.assertEqual(array[:], [1, 5, 7, 33, 99]) - try: - WINFUNCTYPE - except NameError: - pass - else: - def test_issue_8959_b(self): - from ctypes.wintypes import BOOL, HWND, LPARAM + @need_symbol('WINFUNCTYPE') + def test_issue_8959_b(self): + from ctypes.wintypes import BOOL, HWND, LPARAM + global windowCount + windowCount = 0 + + @WINFUNCTYPE(BOOL, HWND, LPARAM) + def EnumWindowsCallbackFunc(hwnd, lParam): global windowCount - windowCount = 0 + windowCount += 1 + return True #Allow windows to keep enumerating - @WINFUNCTYPE(BOOL, HWND, LPARAM) - def EnumWindowsCallbackFunc(hwnd, lParam): - global windowCount - windowCount += 1 - return True #Allow windows to keep enumerating - - windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0) + windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0) def test_callback_register_int(self): # Issue #8275: buggy handling of callback args under Win64 diff --git a/Lib/ctypes/test/test_cast.py b/Lib/ctypes/test/test_cast.py --- a/Lib/ctypes/test/test_cast.py +++ b/Lib/ctypes/test/test_cast.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest import sys @@ -75,15 +76,11 @@ self.assertEqual(cast(cast(s, c_void_p), c_char_p).value, "hiho") - try: - c_wchar_p - except NameError: - pass - else: - def test_wchar_p(self): - s = c_wchar_p("hiho") - self.assertEqual(cast(cast(s, c_void_p), c_wchar_p).value, - "hiho") + @need_symbol('c_wchar_p') + def test_wchar_p(self): + s = c_wchar_p("hiho") + self.assertEqual(cast(cast(s, c_void_p), c_wchar_p).value, + "hiho") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py --- a/Lib/ctypes/test/test_cfuncs.py +++ b/Lib/ctypes/test/test_cfuncs.py @@ -3,6 +3,7 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test @@ -193,7 +194,7 @@ try: WinDLL except NameError: - pass + def stdcall_dll(*_): pass else: class stdcall_dll(WinDLL): def __getattr__(self, name): @@ -203,9 +204,9 @@ setattr(self, name, func) return func - class stdcallCFunctions(CFunctions): - _dll = stdcall_dll(_ctypes_test.__file__) - pass + at need_symbol('WinDLL') +class stdcallCFunctions(CFunctions): + _dll = stdcall_dll(_ctypes_test.__file__) if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py --- a/Lib/ctypes/test/test_checkretval.py +++ b/Lib/ctypes/test/test_checkretval.py @@ -1,6 +1,7 @@ import unittest from ctypes import * +from ctypes.test import need_symbol class CHECKED(c_int): def _check_retval_(value): @@ -25,15 +26,11 @@ del dll._testfunc_p_p.restype self.assertEqual(42, dll._testfunc_p_p(42)) - try: - oledll - except NameError: - pass - else: - def test_oledll(self): - self.assertRaises(WindowsError, - oledll.oleaut32.CreateTypeLib2, - 0, None, None) + @need_symbol('oledll') + def test_oledll(self): + self.assertRaises(WindowsError, + oledll.oleaut32.CreateTypeLib2, + 0, None, None) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_errcheck.py b/Lib/ctypes/test/test_errcheck.py deleted file mode 100644 --- a/Lib/ctypes/test/test_errcheck.py +++ /dev/null @@ -1,19 +0,0 @@ -import sys -from ctypes import * - -##class HMODULE(Structure): -## _fields_ = [("value", c_void_p)] - -## def __repr__(self): -## return "" % self.value - -##windll.kernel32.GetModuleHandleA.restype = HMODULE - -##print windll.kernel32.GetModuleHandleA("python23.dll") -##print hex(sys.dllhandle) - -##def nonzero(handle): -## return (GetLastError(), handle) - -##windll.kernel32.GetModuleHandleA.errcheck = nonzero -##print windll.kernel32.GetModuleHandleA("spam") diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py --- a/Lib/ctypes/test/test_find.py +++ b/Lib/ctypes/test/test_find.py @@ -1,4 +1,5 @@ import unittest +import os import sys from ctypes import * from ctypes.util import find_library @@ -40,43 +41,43 @@ except OSError: pass - if lib_gl: - def test_gl(self): - if self.gl: - self.gl.glClearIndex + @unittest.skipUnless(lib_gl, 'lib_gl not available') + def test_gl(self): + if self.gl: + self.gl.glClearIndex - if lib_glu: - def test_glu(self): - if self.glu: - self.glu.gluBeginCurve + @unittest.skipUnless(lib_glu, 'lib_glu not available') + def test_glu(self): + if self.glu: + self.glu.gluBeginCurve - if lib_gle: - def test_gle(self): - if self.gle: - self.gle.gleGetJoinStyle + @unittest.skipUnless(lib_gle, 'lib_gle not available') + def test_gle(self): + if self.gle: + self.gle.gleGetJoinStyle -##if os.name == "posix" and sys.platform != "darwin": - -## # On platforms where the default shared library suffix is '.so', -## # at least some libraries can be loaded as attributes of the cdll -## # object, since ctypes now tries loading the lib again -## # with '.so' appended of the first try fails. -## # -## # Won't work for libc, unfortunately. OTOH, it isn't -## # needed for libc since this is already mapped into the current -## # process (?) -## # -## # On MAC OSX, it won't work either, because dlopen() needs a full path, -## # and the default suffix is either none or '.dylib'. - -## class LoadLibs(unittest.TestCase): -## def test_libm(self): -## import math -## libm = cdll.libm -## sqrt = libm.sqrt -## sqrt.argtypes = (c_double,) -## sqrt.restype = c_double -## self.assertEqual(sqrt(2), math.sqrt(2)) +# On platforms where the default shared library suffix is '.so', +# at least some libraries can be loaded as attributes of the cdll +# object, since ctypes now tries loading the lib again +# with '.so' appended of the first try fails. +# +# Won't work for libc, unfortunately. OTOH, it isn't +# needed for libc since this is already mapped into the current +# process (?) +# +# On MAC OSX, it won't work either, because dlopen() needs a full path, +# and the default suffix is either none or '.dylib'. + at unittest.skip('test disabled') + at unittest.skipUnless(os.name=="posix" and sys.platform != "darwin", + 'test not suitable for this platform') +class LoadLibs(unittest.TestCase): + def test_libm(self): + import math + libm = cdll.libm + sqrt = libm.sqrt + sqrt.argtypes = (c_double,) + sqrt.restype = c_double + self.assertEqual(sqrt(2), math.sqrt(2)) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -6,6 +6,7 @@ """ from ctypes import * +from ctypes.test import need_symbol import sys, unittest try: @@ -63,22 +64,16 @@ pass + @need_symbol('c_wchar') def test_wchar_parm(self): - try: - c_wchar - except NameError: - return f = dll._testfunc_i_bhilfd f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double] result = f(1, u"x", 3, 4, 5.0, 6.0) self.assertEqual(result, 139) self.assertEqual(type(result), int) + @need_symbol('c_wchar') def test_wchar_result(self): - try: - c_wchar - except NameError: - return f = dll._testfunc_i_bhilfd f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double] f.restype = c_wchar @@ -155,11 +150,8 @@ self.assertEqual(result, -21) self.assertEqual(type(result), float) + @need_symbol('c_longlong') def test_longlongresult(self): - try: - c_longlong - except NameError: - return f = dll._testfunc_q_bhilfd f.restype = c_longlong f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double] @@ -296,6 +288,7 @@ result = f(-10, cb) self.assertEqual(result, -18) + @need_symbol('c_longlong') def test_longlong_callbacks(self): f = dll._testfunc_callback_q_qf @@ -348,16 +341,16 @@ s2h = dll.ret_2h_func(inp) self.assertEqual((s2h.x, s2h.y), (99*2, 88*3)) - if sys.platform == "win32": - def test_struct_return_2H_stdcall(self): - class S2H(Structure): - _fields_ = [("x", c_short), - ("y", c_short)] + @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + def test_struct_return_2H_stdcall(self): + class S2H(Structure): + _fields_ = [("x", c_short), + ("y", c_short)] - windll.s_ret_2h_func.restype = S2H - windll.s_ret_2h_func.argtypes = [S2H] - s2h = windll.s_ret_2h_func(S2H(99, 88)) - self.assertEqual((s2h.x, s2h.y), (99*2, 88*3)) + windll.s_ret_2h_func.restype = S2H + windll.s_ret_2h_func.argtypes = [S2H] + s2h = windll.s_ret_2h_func(S2H(99, 88)) + self.assertEqual((s2h.x, s2h.y), (99*2, 88*3)) def test_struct_return_8H(self): class S8I(Structure): @@ -376,23 +369,24 @@ self.assertEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) - if sys.platform == "win32": - def test_struct_return_8H_stdcall(self): - class S8I(Structure): - _fields_ = [("a", c_int), - ("b", c_int), - ("c", c_int), - ("d", c_int), - ("e", c_int), - ("f", c_int), - ("g", c_int), - ("h", c_int)] - windll.s_ret_8i_func.restype = S8I - windll.s_ret_8i_func.argtypes = [S8I] - inp = S8I(9, 8, 7, 6, 5, 4, 3, 2) - s8i = windll.s_ret_8i_func(inp) - self.assertEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), - (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) + @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + def test_struct_return_8H_stdcall(self): + class S8I(Structure): + _fields_ = [("a", c_int), + ("b", c_int), + ("c", c_int), + ("d", c_int), + ("e", c_int), + ("f", c_int), + ("g", c_int), + ("h", c_int)] + windll.s_ret_8i_func.restype = S8I + windll.s_ret_8i_func.argtypes = [S8I] + inp = S8I(9, 8, 7, 6, 5, 4, 3, 2) + s8i = windll.s_ret_8i_func(inp) + self.assertEqual( + (s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), + (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) def test_sf1651235(self): # see http://www.python.org/sf/1651235 diff --git a/Lib/ctypes/test/test_integers.py b/Lib/ctypes/test/test_integers.py deleted file mode 100644 --- a/Lib/ctypes/test/test_integers.py +++ /dev/null @@ -1,5 +0,0 @@ -# superseded by test_numbers.py -import unittest - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/ctypes/test/test_keeprefs.py b/Lib/ctypes/test/test_keeprefs.py --- a/Lib/ctypes/test/test_keeprefs.py +++ b/Lib/ctypes/test/test_keeprefs.py @@ -94,7 +94,8 @@ self.assertEqual(x._objects, {'1': i}) class DeletePointerTestCase(unittest.TestCase): - def X_test(self): + @unittest.skip('test disabled') + def test_X(self): class X(Structure): _fields_ = [("p", POINTER(c_char_p))] x = X() diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -21,18 +21,21 @@ unknowndll = "xxrandomnamexx" - if libc_name is not None: - def test_load(self): - CDLL(libc_name) - CDLL(os.path.basename(libc_name)) - self.assertRaises(OSError, CDLL, self.unknowndll) + @unittest.skipUnless(libc_name is not None, 'could not find libc') + def test_load(self): + CDLL(libc_name) + CDLL(os.path.basename(libc_name)) + self.assertRaises(OSError, CDLL, self.unknowndll) - if libc_name is not None and os.path.basename(libc_name) == "libc.so.6": - def test_load_version(self): - cdll.LoadLibrary("libc.so.6") - # linux uses version, libc 9 should not exist - self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9") - self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll) + @unittest.skipUnless(libc_name is not None, 'could not find libc') + @unittest.skipUnless(libc_name is not None and + os.path.basename(libc_name) == "libc.so.6", + 'wrong libc path for test') + def test_load_version(self): + cdll.LoadLibrary("libc.so.6") + # linux uses version, libc 9 should not exist + self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9") + self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll) def test_find(self): for name in ("c", "m"): @@ -41,66 +44,71 @@ cdll.LoadLibrary(lib) CDLL(lib) - if os.name in ("nt", "ce"): - def test_load_library(self): - self.assertIsNotNone(libc_name) - if is_resource_enabled("printing"): - print find_library("kernel32") - print find_library("user32") + @unittest.skipUnless(os.name in ("nt", "ce"), + 'test specific to Windows (NT/CE)') + def test_load_library(self): + self.assertIsNotNone(libc_name) + if is_resource_enabled("printing"): + print find_library("kernel32") + print find_library("user32") - if os.name == "nt": - windll.kernel32.GetModuleHandleW - windll["kernel32"].GetModuleHandleW - windll.LoadLibrary("kernel32").GetModuleHandleW - WinDLL("kernel32").GetModuleHandleW - elif os.name == "ce": - windll.coredll.GetModuleHandleW - windll["coredll"].GetModuleHandleW - windll.LoadLibrary("coredll").GetModuleHandleW - WinDLL("coredll").GetModuleHandleW + if os.name == "nt": + windll.kernel32.GetModuleHandleW + windll["kernel32"].GetModuleHandleW + windll.LoadLibrary("kernel32").GetModuleHandleW + WinDLL("kernel32").GetModuleHandleW + elif os.name == "ce": + windll.coredll.GetModuleHandleW + windll["coredll"].GetModuleHandleW + windll.LoadLibrary("coredll").GetModuleHandleW + WinDLL("coredll").GetModuleHandleW - def test_load_ordinal_functions(self): - import _ctypes_test - dll = WinDLL(_ctypes_test.__file__) - # We load the same function both via ordinal and name - func_ord = dll[2] - func_name = dll.GetString - # addressof gets the address where the function pointer is stored - a_ord = addressof(func_ord) - a_name = addressof(func_name) - f_ord_addr = c_void_p.from_address(a_ord).value - f_name_addr = c_void_p.from_address(a_name).value - self.assertEqual(hex(f_ord_addr), hex(f_name_addr)) + @unittest.skipUnless(os.name in ("nt", "ce"), + 'test specific to Windows (NT/CE)') + def test_load_ordinal_functions(self): + import _ctypes_test + dll = WinDLL(_ctypes_test.__file__) + # We load the same function both via ordinal and name + func_ord = dll[2] + func_name = dll.GetString + # addressof gets the address where the function pointer is stored + a_ord = addressof(func_ord) + a_name = addressof(func_name) + f_ord_addr = c_void_p.from_address(a_ord).value + f_name_addr = c_void_p.from_address(a_name).value + self.assertEqual(hex(f_ord_addr), hex(f_name_addr)) - self.assertRaises(AttributeError, dll.__getitem__, 1234) + self.assertRaises(AttributeError, dll.__getitem__, 1234) - if os.name == "nt": - def test_1703286_A(self): - from _ctypes import LoadLibrary, FreeLibrary - # On winXP 64-bit, advapi32 loads at an address that does - # NOT fit into a 32-bit integer. FreeLibrary must be able - # to accept this address. + @unittest.skipUnless(os.name == "nt", 'Windows-specific test') + def test_1703286_A(self): + from _ctypes import LoadLibrary, FreeLibrary + # On winXP 64-bit, advapi32 loads at an address that does + # NOT fit into a 32-bit integer. FreeLibrary must be able + # to accept this address. - # These are tests for http://www.python.org/sf/1703286 - handle = LoadLibrary("advapi32") - FreeLibrary(handle) + # These are tests for http://www.python.org/sf/1703286 + handle = LoadLibrary("advapi32") + FreeLibrary(handle) - def test_1703286_B(self): - # Since on winXP 64-bit advapi32 loads like described - # above, the (arbitrarily selected) CloseEventLog function - # also has a high address. 'call_function' should accept - # addresses so large. - from _ctypes import call_function - advapi32 = windll.advapi32 - # Calling CloseEventLog with a NULL argument should fail, - # but the call should not segfault or so. - self.assertEqual(0, advapi32.CloseEventLog(None)) - windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p - windll.kernel32.GetProcAddress.restype = c_void_p - proc = windll.kernel32.GetProcAddress(advapi32._handle, "CloseEventLog") - self.assertTrue(proc) - # This is the real test: call the function via 'call_function' - self.assertEqual(0, call_function(proc, (None,))) + @unittest.skipUnless(os.name == "nt", 'Windows-specific test') + def test_1703286_B(self): + # Since on winXP 64-bit advapi32 loads like described + # above, the (arbitrarily selected) CloseEventLog function + # also has a high address. 'call_function' should accept + # addresses so large. + from _ctypes import call_function + advapi32 = windll.advapi32 + # Calling CloseEventLog with a NULL argument should fail, + # but the call should not segfault or so. + self.assertEqual(0, advapi32.CloseEventLog(None)) + windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p + windll.kernel32.GetProcAddress.restype = c_void_p + proc = windll.kernel32.GetProcAddress(advapi32._handle, + "CloseEventLog") + self.assertTrue(proc) + # This is the real test: call the function via 'call_function' + self.assertEqual(0, call_function(proc, (None,))) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_macholib.py b/Lib/ctypes/test/test_macholib.py --- a/Lib/ctypes/test/test_macholib.py +++ b/Lib/ctypes/test/test_macholib.py @@ -45,21 +45,21 @@ raise ValueError("%s not found" % (name,)) class MachOTest(unittest.TestCase): - if sys.platform == "darwin": - def test_find(self): + @unittest.skipUnless(sys.platform == "darwin", 'OSX-specific test') + def test_find(self): - self.assertEqual(find_lib('pthread'), - '/usr/lib/libSystem.B.dylib') + self.assertEqual(find_lib('pthread'), + '/usr/lib/libSystem.B.dylib') - result = find_lib('z') - # Issue #21093: dyld default search path includes $HOME/lib and - # /usr/local/lib before /usr/lib, which caused test failures if - # a local copy of libz exists in one of them. Now ignore the head - # of the path. - self.assertRegexpMatches(result, r".*/lib/libz\..*.*\.dylib") + result = find_lib('z') + # Issue #21093: dyld default search path includes $HOME/lib and + # /usr/local/lib before /usr/lib, which caused test failures if + # a local copy of libz exists in one of them. Now ignore the head + # of the path. + self.assertRegexpMatches(result, r".*/lib/libz\..*.*\.dylib") - self.assertEqual(find_lib('IOKit'), - '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit') + self.assertEqual(find_lib('IOKit'), + '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit') if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_memfunctions.py b/Lib/ctypes/test/test_memfunctions.py --- a/Lib/ctypes/test/test_memfunctions.py +++ b/Lib/ctypes/test/test_memfunctions.py @@ -1,17 +1,19 @@ import sys import unittest from ctypes import * +from ctypes.test import need_symbol class MemFunctionsTest(unittest.TestCase): -## def test_overflow(self): -## # string_at and wstring_at must use the Python calling -## # convention (which acquires the GIL and checks the Python -## # error flag). Provoke an error and catch it; see also issue -## # #3554: -## self.assertRaises((OverflowError, MemoryError, SystemError), -## lambda: wstring_at(u"foo", sys.maxint - 1)) -## self.assertRaises((OverflowError, MemoryError, SystemError), -## lambda: string_at("foo", sys.maxint - 1)) + @unittest.skip('test disabled') + def test_overflow(self): + # string_at and wstring_at must use the Python calling + # convention (which acquires the GIL and checks the Python + # error flag). Provoke an error and catch it; see also issue + # #3554: + self.assertRaises((OverflowError, MemoryError, SystemError), + lambda: wstring_at(u"foo", sys.maxint - 1)) + self.assertRaises((OverflowError, MemoryError, SystemError), + lambda: string_at("foo", sys.maxint - 1)) def test_memmove(self): # large buffers apparently increase the chance that the memory @@ -59,21 +61,17 @@ self.assertEqual(string_at("foo bar", 8), "foo bar\0") self.assertEqual(string_at("foo bar", 3), "foo") - try: - create_unicode_buffer - except NameError: - pass - else: - def test_wstring_at(self): - p = create_unicode_buffer("Hello, World") - a = create_unicode_buffer(1000000) - result = memmove(a, p, len(p) * sizeof(c_wchar)) - self.assertEqual(a.value, "Hello, World") + @need_symbol('create_unicode_buffer') + def test_wstring_at(self): + p = create_unicode_buffer("Hello, World") + a = create_unicode_buffer(1000000) + result = memmove(a, p, len(p) * sizeof(c_wchar)) + self.assertEqual(a.value, "Hello, World") - self.assertEqual(wstring_at(a), "Hello, World") - self.assertEqual(wstring_at(a, 5), "Hello") - self.assertEqual(wstring_at(a, 16), "Hello, World\0\0\0\0") - self.assertEqual(wstring_at(a, 0), "") + self.assertEqual(wstring_at(a), "Hello, World") + self.assertEqual(wstring_at(a, 5), "Hello") + self.assertEqual(wstring_at(a, 16), "Hello, World\0\0\0\0") + self.assertEqual(wstring_at(a, 0), "") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_numbers.py b/Lib/ctypes/test/test_numbers.py --- a/Lib/ctypes/test/test_numbers.py +++ b/Lib/ctypes/test/test_numbers.py @@ -82,12 +82,13 @@ self.assertRaises(TypeError, t, "") self.assertRaises(TypeError, t, None) -## def test_valid_ranges(self): -## # invalid values of the correct type -## # raise ValueError (not OverflowError) -## for t, (l, h) in zip(unsigned_types, unsigned_ranges): -## self.assertRaises(ValueError, t, l-1) -## self.assertRaises(ValueError, t, h+1) + @unittest.skip('test disabled') + def test_valid_ranges(self): + # invalid values of the correct type + # raise ValueError (not OverflowError) + for t, (l, h) in zip(unsigned_types, unsigned_ranges): + self.assertRaises(ValueError, t, l-1) + self.assertRaises(ValueError, t, h+1) def test_from_param(self): # the from_param class method attribute always @@ -199,16 +200,17 @@ self.assertEqual(v.value, a[0]) # array does not support c_bool / 't' - # def test_bool_from_address(self): - # from ctypes import c_bool - # from array import array - # a = array(c_bool._type_, [True]) - # v = t.from_address(a.buffer_info()[0]) - # self.assertEqual(v.value, a[0]) - # self.assertEqual(type(v) is t) - # a[0] = False - # self.assertEqual(v.value, a[0]) - # self.assertEqual(type(v) is t) + @unittest.skip('test disabled') + def test_bool_from_address(self): + from ctypes import c_bool + from array import array + a = array(c_bool._type_, [True]) + v = t.from_address(a.buffer_info()[0]) + self.assertEqual(v.value, a[0]) + self.assertEqual(type(v) is t) + a[0] = False + self.assertEqual(v.value, a[0]) + self.assertEqual(type(v) is t) def test_init(self): # c_int() can be initialized from Python's int, and c_int. @@ -226,8 +228,9 @@ if (hasattr(t, "__ctype_le__")): self.assertRaises(OverflowError, t.__ctype_le__, big_int) -## def test_perf(self): -## check_perf() + @unittest.skip('test disabled') + def test_perf(self): + check_perf() from ctypes import _SimpleCData class c_int_S(_SimpleCData): diff --git a/Lib/ctypes/test/test_objects.py b/Lib/ctypes/test/test_objects.py --- a/Lib/ctypes/test/test_objects.py +++ b/Lib/ctypes/test/test_objects.py @@ -59,12 +59,9 @@ import ctypes.test.test_objects class TestCase(unittest.TestCase): - if sys.hexversion > 0x02040000: - # Python 2.3 has no ELLIPSIS flag, so we don't test with this - # version: - def test(self): - doctest.testmod(ctypes.test.test_objects) + def test(self): + failures, tests = doctest.testmod(ctypes.test.test_objects) + self.assertFalse(failures, 'doctests failed, see output above') if __name__ == '__main__': - if sys.hexversion > 0x02040000: - doctest.testmod(ctypes.test.test_objects) + doctest.testmod(ctypes.test.test_objects) diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -1,4 +1,5 @@ import unittest, sys +from ctypes.test import need_symbol class SimpleTypesTestCase(unittest.TestCase): @@ -36,10 +37,9 @@ self.assertEqual(CVOIDP.from_param("abc"), "abcabc") self.assertEqual(CCHARP.from_param("abc"), "abcabcabcabc") - try: - from ctypes import c_wchar_p - except ImportError: - return + @need_symbol('c_wchar_p') + def test_subclasses_c_wchar_p(self): + from ctypes import c_wchar_p class CWCHARP(c_wchar_p): def from_param(cls, value): @@ -68,13 +68,9 @@ a = c_char_p("123") self.assertIs(c_char_p.from_param(a), a) + @need_symbol('c_wchar_p') def test_cw_strings(self): - from ctypes import byref - try: - from ctypes import c_wchar_p - except ImportError: -## print "(No c_wchar_p)" - return + from ctypes import byref, c_wchar_p s = u"123" if sys.platform == "win32": self.assertTrue(c_wchar_p.from_param(s)._obj is s) @@ -144,9 +140,6 @@ self.assertRaises(TypeError, LPINT.from_param, c_long*3) self.assertRaises(TypeError, LPINT.from_param, c_uint*3) -## def test_performance(self): -## check_perf() - def test_noctypes_argtype(self): import _ctypes_test from ctypes import CDLL, c_void_p, ArgumentError diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py --- a/Lib/ctypes/test/test_prototypes.py +++ b/Lib/ctypes/test/test_prototypes.py @@ -1,4 +1,5 @@ from ctypes import * +from ctypes.test import need_symbol import unittest # IMPORTANT INFO: @@ -135,13 +136,14 @@ func(pointer(c_int())) func((c_int * 3)()) - try: - func.restype = c_wchar_p - except NameError: - pass - else: - self.assertEqual(None, func(c_wchar_p(None))) - self.assertEqual(u"123", func(c_wchar_p(u"123"))) + @need_symbol('c_wchar_p') + def test_c_void_p_arg_with_c_wchar_p(self): + func = testdll._testfunc_p_p + func.restype = c_wchar_p + func.argtypes = c_void_p, + + self.assertEqual(None, func(c_wchar_p(None))) + self.assertEqual(u"123", func(c_wchar_p(u"123"))) def test_instance(self): func = testdll._testfunc_p_p @@ -156,51 +158,47 @@ func.argtypes = None self.assertEqual(None, func(X())) -try: - c_wchar -except NameError: - pass -else: - class WCharPointersTestCase(unittest.TestCase): + at need_symbol('c_wchar') +class WCharPointersTestCase(unittest.TestCase): - def setUp(self): - func = testdll._testfunc_p_p - func.restype = c_int - func.argtypes = None + def setUp(self): + func = testdll._testfunc_p_p + func.restype = c_int + func.argtypes = None - def test_POINTER_c_wchar_arg(self): - func = testdll._testfunc_p_p - func.restype = c_wchar_p - func.argtypes = POINTER(c_wchar), + def test_POINTER_c_wchar_arg(self): + func = testdll._testfunc_p_p + func.restype = c_wchar_p + func.argtypes = POINTER(c_wchar), - self.assertEqual(None, func(None)) - self.assertEqual(u"123", func(u"123")) - self.assertEqual(None, func(c_wchar_p(None))) - self.assertEqual(u"123", func(c_wchar_p(u"123"))) + self.assertEqual(None, func(None)) + self.assertEqual(u"123", func(u"123")) + self.assertEqual(None, func(c_wchar_p(None))) + self.assertEqual(u"123", func(c_wchar_p(u"123"))) - self.assertEqual(u"123", func(c_wbuffer(u"123"))) - ca = c_wchar("a") - self.assertEqual(u"a", func(pointer(ca))[0]) - self.assertEqual(u"a", func(byref(ca))[0]) + self.assertEqual(u"123", func(c_wbuffer(u"123"))) + ca = c_wchar("a") + self.assertEqual(u"a", func(pointer(ca))[0]) + self.assertEqual(u"a", func(byref(ca))[0]) - def test_c_wchar_p_arg(self): - func = testdll._testfunc_p_p - func.restype = c_wchar_p - func.argtypes = c_wchar_p, + def test_c_wchar_p_arg(self): + func = testdll._testfunc_p_p + func.restype = c_wchar_p + func.argtypes = c_wchar_p, - c_wchar_p.from_param(u"123") + c_wchar_p.from_param(u"123") - self.assertEqual(None, func(None)) - self.assertEqual("123", func(u"123")) - self.assertEqual(None, func(c_wchar_p(None))) - self.assertEqual("123", func(c_wchar_p("123"))) + self.assertEqual(None, func(None)) + self.assertEqual("123", func(u"123")) + self.assertEqual(None, func(c_wchar_p(None))) + self.assertEqual("123", func(c_wchar_p("123"))) - # XXX Currently, these raise TypeErrors, although they shouldn't: - self.assertEqual("123", func(c_wbuffer("123"))) - ca = c_wchar("a") - self.assertEqual("a", func(pointer(ca))[0]) - self.assertEqual("a", func(byref(ca))[0]) + # XXX Currently, these raise TypeErrors, although they shouldn't: + self.assertEqual("123", func(c_wbuffer("123"))) + ca = c_wchar("a") + self.assertEqual("a", func(pointer(ca))[0]) + self.assertEqual("a", func(byref(ca))[0]) class ArrayTest(unittest.TestCase): def test(self): diff --git a/Lib/ctypes/test/test_python_api.py b/Lib/ctypes/test/test_python_api.py --- a/Lib/ctypes/test/test_python_api.py +++ b/Lib/ctypes/test/test_python_api.py @@ -1,6 +1,6 @@ from ctypes import * import unittest, sys -from ctypes.test import is_resource_enabled +from ctypes.test import requires ################################################################ # This section should be moved into ctypes\__init__.py, when it's ready. @@ -37,24 +37,24 @@ del pyob self.assertEqual(grc(s), refcnt) - if is_resource_enabled("refcount"): - # This test is unreliable, because it is possible that code in - # unittest changes the refcount of the '42' integer. So, it - # is disabled by default. - def test_PyInt_Long(self): - ref42 = grc(42) - pythonapi.PyInt_FromLong.restype = py_object - self.assertEqual(pythonapi.PyInt_FromLong(42), 42) + # This test is unreliable, because it is possible that code in + # unittest changes the refcount of the '42' integer. So, it + # is disabled by default. + @requires("refcount") + def test_PyInt_Long(self): + ref42 = grc(42) + pythonapi.PyInt_FromLong.restype = py_object + self.assertEqual(pythonapi.PyInt_FromLong(42), 42) - self.assertEqual(grc(42), ref42) + self.assertEqual(grc(42), ref42) - pythonapi.PyInt_AsLong.argtypes = (py_object,) - pythonapi.PyInt_AsLong.restype = c_long + pythonapi.PyInt_AsLong.argtypes = (py_object,) + pythonapi.PyInt_AsLong.restype = c_long - res = pythonapi.PyInt_AsLong(42) - self.assertEqual(grc(res), ref42 + 1) - del res - self.assertEqual(grc(42), ref42) + res = pythonapi.PyInt_AsLong(42) + self.assertEqual(grc(res), ref42 + 1) + del res + self.assertEqual(grc(42), ref42) def test_PyObj_FromPtr(self): s = "abc def ghi jkl" diff --git a/Lib/ctypes/test/test_random_things.py b/Lib/ctypes/test/test_random_things.py --- a/Lib/ctypes/test/test_random_things.py +++ b/Lib/ctypes/test/test_random_things.py @@ -5,23 +5,22 @@ 42 // arg raise ValueError(arg) -if sys.platform == "win32": + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class call_function_TestCase(unittest.TestCase): + # _ctypes.call_function is deprecated and private, but used by + # Gary Bishp's readline module. If we have it, we must test it as well. - class call_function_TestCase(unittest.TestCase): - # _ctypes.call_function is deprecated and private, but used by - # Gary Bishp's readline module. If we have it, we must test it as well. + def test(self): + from _ctypes import call_function + windll.kernel32.LoadLibraryA.restype = c_void_p + windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p + windll.kernel32.GetProcAddress.restype = c_void_p - def test(self): - from _ctypes import call_function - windll.kernel32.LoadLibraryA.restype = c_void_p - windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p - windll.kernel32.GetProcAddress.restype = c_void_p + hdll = windll.kernel32.LoadLibraryA("kernel32") + funcaddr = windll.kernel32.GetProcAddress(hdll, "GetModuleHandleA") - hdll = windll.kernel32.LoadLibraryA("kernel32") - funcaddr = windll.kernel32.GetProcAddress(hdll, "GetModuleHandleA") - - self.assertEqual(call_function(funcaddr, (None,)), - windll.kernel32.GetModuleHandleA(None)) + self.assertEqual(call_function(funcaddr, (None,)), + windll.kernel32.GetModuleHandleA(None)) class CallbackTracbackTestCase(unittest.TestCase): # When an exception is raised in a ctypes callback function, the C diff --git a/Lib/ctypes/test/test_slicing.py b/Lib/ctypes/test/test_slicing.py --- a/Lib/ctypes/test/test_slicing.py +++ b/Lib/ctypes/test/test_slicing.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol import _ctypes_test @@ -131,46 +132,42 @@ self.assertEqual(p[2:5:-3], s[2:5:-3]) - try: - c_wchar - except NameError: - pass - else: - def test_wchar_ptr(self): - s = u"abcdefghijklmnopqrstuvwxyz\0" + @need_symbol('c_wchar') + def test_wchar_ptr(self): + s = u"abcdefghijklmnopqrstuvwxyz\0" - dll = CDLL(_ctypes_test.__file__) - dll.my_wcsdup.restype = POINTER(c_wchar) - dll.my_wcsdup.argtypes = POINTER(c_wchar), - dll.my_free.restype = None - res = dll.my_wcsdup(s) - self.assertEqual(res[:len(s)], s) - self.assertEqual(res[:len(s):], s) - self.assertEqual(res[len(s)-1:-1:-1], s[::-1]) - self.assertEqual(res[len(s)-1:5:-7], s[:5:-7]) + dll = CDLL(_ctypes_test.__file__) + dll.my_wcsdup.restype = POINTER(c_wchar) + dll.my_wcsdup.argtypes = POINTER(c_wchar), + dll.my_free.restype = None + res = dll.my_wcsdup(s) + self.assertEqual(res[:len(s)], s) + self.assertEqual(res[:len(s):], s) + self.assertEqual(res[len(s)-1:-1:-1], s[::-1]) + self.assertEqual(res[len(s)-1:5:-7], s[:5:-7]) - import operator - self.assertRaises(TypeError, operator.setslice, - res, 0, 5, u"abcde") - self.assertRaises(TypeError, operator.setitem, - res, slice(0, 5), u"abcde") - dll.my_free(res) + import operator + self.assertRaises(TypeError, operator.setslice, + res, 0, 5, u"abcde") + self.assertRaises(TypeError, operator.setitem, + res, slice(0, 5), u"abcde") + dll.my_free(res) - if sizeof(c_wchar) == sizeof(c_short): - dll.my_wcsdup.restype = POINTER(c_short) - elif sizeof(c_wchar) == sizeof(c_int): - dll.my_wcsdup.restype = POINTER(c_int) - elif sizeof(c_wchar) == sizeof(c_long): - dll.my_wcsdup.restype = POINTER(c_long) - else: - return - res = dll.my_wcsdup(s) - tmpl = range(ord("a"), ord("z")+1) - self.assertEqual(res[:len(s)-1], tmpl) - self.assertEqual(res[:len(s)-1:], tmpl) - self.assertEqual(res[len(s)-2:-1:-1], tmpl[::-1]) - self.assertEqual(res[len(s)-2:5:-7], tmpl[:5:-7]) - dll.my_free(res) + if sizeof(c_wchar) == sizeof(c_short): + dll.my_wcsdup.restype = POINTER(c_short) + elif sizeof(c_wchar) == sizeof(c_int): + dll.my_wcsdup.restype = POINTER(c_int) + elif sizeof(c_wchar) == sizeof(c_long): + dll.my_wcsdup.restype = POINTER(c_long) + else: + self.skipTest('Pointers to c_wchar are not supported') + res = dll.my_wcsdup(s) + tmpl = range(ord("a"), ord("z")+1) + self.assertEqual(res[:len(s)-1], tmpl) + self.assertEqual(res[:len(s)-1:], tmpl) + self.assertEqual(res[len(s)-2:-1:-1], tmpl[::-1]) + self.assertEqual(res[len(s)-2:5:-7], tmpl[:5:-7]) + dll.my_free(res) ################################################################ diff --git a/Lib/ctypes/test/test_strings.py b/Lib/ctypes/test/test_strings.py --- a/Lib/ctypes/test/test_strings.py +++ b/Lib/ctypes/test/test_strings.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol from test import test_support class StringArrayTestCase(unittest.TestCase): @@ -60,29 +61,26 @@ ## print BUF.from_param(c_char_p("python")) ## print BUF.from_param(BUF(*"pyth")) -try: - c_wchar -except NameError: - pass -else: - class WStringArrayTestCase(unittest.TestCase): - def test(self): - BUF = c_wchar * 4 + at need_symbol('c_wchar') +class WStringArrayTestCase(unittest.TestCase): + def test(self): + BUF = c_wchar * 4 - buf = BUF(u"a", u"b", u"c") - self.assertEqual(buf.value, u"abc") + buf = BUF(u"a", u"b", u"c") + self.assertEqual(buf.value, u"abc") - buf.value = u"ABCD" - self.assertEqual(buf.value, u"ABCD") + buf.value = u"ABCD" + self.assertEqual(buf.value, u"ABCD") - buf.value = u"x" - self.assertEqual(buf.value, u"x") + buf.value = u"x" + self.assertEqual(buf.value, u"x") - buf[1] = u"Z" - self.assertEqual(buf.value, u"xZCD") + buf[1] = u"Z" + self.assertEqual(buf.value, u"xZCD") class StringTestCase(unittest.TestCase): - def XX_test_basic_strings(self): + @unittest.skip('test disabled') + def test_basic_strings(self): cs = c_string("abcdef") # Cannot call len on a c_string any longer @@ -108,7 +106,8 @@ self.assertRaises(TypeError, c_string, u"123") - def XX_test_sized_strings(self): + @unittest.skip('test disabled') + def test_sized_strings(self): # New in releases later than 0.4.0: self.assertRaises(TypeError, c_string, None) @@ -125,7 +124,8 @@ self.assertEqual(c_string(2).raw[-1], "\000") self.assertEqual(len(c_string(2).raw), 2) - def XX_test_initialized_strings(self): + @unittest.skip('test disabled') + def test_initialized_strings(self): self.assertEqual(c_string("ab", 4).raw[:2], "ab") self.assertEqual(c_string("ab", 4).raw[:2:], "ab") @@ -134,7 +134,8 @@ self.assertEqual(c_string("ab", 4).raw[-1], "\000") self.assertEqual(c_string("ab", 2).raw, "a\000") - def XX_test_toolong(self): + @unittest.skip('test disabled') + def test_toolong(self): cs = c_string("abcdef") # Much too long string: self.assertRaises(ValueError, setattr, cs, "value", "123456789012345") @@ -142,54 +143,53 @@ # One char too long values: self.assertRaises(ValueError, setattr, cs, "value", "1234567") -## def test_perf(self): -## check_perf() + @unittest.skip('test disabled') + def test_perf(self): + check_perf() -try: - c_wchar -except NameError: - pass -else: - class WStringTestCase(unittest.TestCase): - def test_wchar(self): - c_wchar(u"x") - repr(byref(c_wchar(u"x"))) - c_wchar("x") + at need_symbol('c_wchar') +class WStringTestCase(unittest.TestCase): + def test_wchar(self): + c_wchar(u"x") + repr(byref(c_wchar(u"x"))) + c_wchar("x") - def X_test_basic_wstrings(self): - cs = c_wstring(u"abcdef") + @unittest.skip('test disabled') + def test_basic_wstrings(self): + cs = c_wstring(u"abcdef") - # XXX This behaviour is about to change: - # len returns the size of the internal buffer in bytes. - # This includes the terminating NUL character. - self.assertEqual(sizeof(cs), 14) + # XXX This behaviour is about to change: + # len returns the size of the internal buffer in bytes. + # This includes the terminating NUL character. + self.assertEqual(sizeof(cs), 14) - # The value property is the string up to the first terminating NUL. - self.assertEqual(cs.value, u"abcdef") - self.assertEqual(c_wstring(u"abc\000def").value, u"abc") + # The value property is the string up to the first terminating NUL. + self.assertEqual(cs.value, u"abcdef") + self.assertEqual(c_wstring(u"abc\000def").value, u"abc") - self.assertEqual(c_wstring(u"abc\000def").value, u"abc") + self.assertEqual(c_wstring(u"abc\000def").value, u"abc") - # The raw property is the total buffer contents: - self.assertEqual(cs.raw, u"abcdef\000") - self.assertEqual(c_wstring(u"abc\000def").raw, u"abc\000def\000") + # The raw property is the total buffer contents: + self.assertEqual(cs.raw, u"abcdef\000") + self.assertEqual(c_wstring(u"abc\000def").raw, u"abc\000def\000") - # We can change the value: - cs.value = u"ab" - self.assertEqual(cs.value, u"ab") - self.assertEqual(cs.raw, u"ab\000\000\000\000\000") + # We can change the value: + cs.value = u"ab" + self.assertEqual(cs.value, u"ab") + self.assertEqual(cs.raw, u"ab\000\000\000\000\000") - self.assertRaises(TypeError, c_wstring, "123") - self.assertRaises(ValueError, c_wstring, 0) + self.assertRaises(TypeError, c_wstring, "123") + self.assertRaises(ValueError, c_wstring, 0) - def X_test_toolong(self): - cs = c_wstring(u"abcdef") - # Much too long string: - self.assertRaises(ValueError, setattr, cs, "value", u"123456789012345") + @unittest.skip('test disabled') + def test_toolong(self): + cs = c_wstring(u"abcdef") + # Much too long string: + self.assertRaises(ValueError, setattr, cs, "value", u"123456789012345") - # One char too long values: - self.assertRaises(ValueError, setattr, cs, "value", u"1234567") + # One char too long values: + self.assertRaises(ValueError, setattr, cs, "value", u"1234567") def run_test(rep, msg, func, arg): diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -1,5 +1,6 @@ import unittest from ctypes import * +from ctypes.test import need_symbol from struct import calcsize import _testcapi @@ -291,12 +292,8 @@ self.assertEqual(p.phone.number, "5678") self.assertEqual(p.age, 5) + @need_symbol('c_wchar') def test_structures_with_wchar(self): - try: - c_wchar - except NameError: - return # no unicode - class PersonW(Structure): _fields_ = [("name", c_wchar * 12), ("age", c_int)] @@ -360,14 +357,14 @@ except Exception, detail: return detail.__class__, str(detail) - -## def test_subclass_creation(self): -## meta = type(Structure) -## # same as 'class X(Structure): pass' -## # fails, since we need either a _fields_ or a _abstract_ attribute -## cls, msg = self.get_except(meta, "X", (Structure,), {}) -## self.assertEqual((cls, msg), -## (AttributeError, "class must define a '_fields_' attribute")) + @unittest.skip('test disabled') + def test_subclass_creation(self): + meta = type(Structure) + # same as 'class X(Structure): pass' + # fails, since we need either a _fields_ or a _abstract_ attribute + cls, msg = self.get_except(meta, "X", (Structure,), {}) + self.assertEqual((cls, msg), + (AttributeError, "class must define a '_fields_' attribute")) def test_abstract_class(self): class X(Structure): diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py --- a/Lib/ctypes/test/test_unicode.py +++ b/Lib/ctypes/test/test_unicode.py @@ -1,129 +1,138 @@ # coding: latin-1 import unittest import ctypes +from ctypes.test import need_symbol +import _ctypes_test -try: - ctypes.c_wchar -except AttributeError: - pass -else: - import _ctypes_test - dll = ctypes.CDLL(_ctypes_test.__file__) - wcslen = dll.my_wcslen - wcslen.argtypes = [ctypes.c_wchar_p] + at need_symbol('c_wchar') +class UnicodeTestCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + dll = ctypes.CDLL(_ctypes_test.__file__) + cls.wcslen = dll.my_wcslen + cls.wcslen.argtypes = [ctypes.c_wchar_p] + def setUp(self): + self.prev_conv_mode = ctypes.set_conversion_mode("ascii", "strict") + def tearDown(self): + ctypes.set_conversion_mode(*self.prev_conv_mode) - class UnicodeTestCase(unittest.TestCase): - def setUp(self): - self.prev_conv_mode = ctypes.set_conversion_mode("ascii", "strict") + def test_ascii_strict(self): + wcslen = self.wcslen + ctypes.set_conversion_mode("ascii", "strict") + # no conversions take place with unicode arguments + self.assertEqual(wcslen(u"abc"), 3) + self.assertEqual(wcslen(u"ab\u2070"), 3) + # string args are converted + self.assertEqual(wcslen("abc"), 3) + self.assertRaises(ctypes.ArgumentError, wcslen, "ab?") - def tearDown(self): - ctypes.set_conversion_mode(*self.prev_conv_mode) + def test_ascii_replace(self): + wcslen = self.wcslen + ctypes.set_conversion_mode("ascii", "replace") + self.assertEqual(wcslen(u"abc"), 3) + self.assertEqual(wcslen(u"ab\u2070"), 3) + self.assertEqual(wcslen("abc"), 3) + self.assertEqual(wcslen("ab?"), 3) - def test_ascii_strict(self): - ctypes.set_conversion_mode("ascii", "strict") - # no conversions take place with unicode arguments - self.assertEqual(wcslen(u"abc"), 3) - self.assertEqual(wcslen(u"ab\u2070"), 3) - # string args are converted - self.assertEqual(wcslen("abc"), 3) - self.assertRaises(ctypes.ArgumentError, wcslen, "ab?") + def test_ascii_ignore(self): + wcslen = self.wcslen + ctypes.set_conversion_mode("ascii", "ignore") + self.assertEqual(wcslen(u"abc"), 3) + self.assertEqual(wcslen(u"ab\u2070"), 3) + # ignore error mode skips non-ascii characters + self.assertEqual(wcslen("abc"), 3) + self.assertEqual(wcslen("????"), 0) - def test_ascii_replace(self): - ctypes.set_conversion_mode("ascii", "replace") - self.assertEqual(wcslen(u"abc"), 3) - self.assertEqual(wcslen(u"ab\u2070"), 3) - self.assertEqual(wcslen("abc"), 3) - self.assertEqual(wcslen("ab?"), 3) + def test_latin1_strict(self): + wcslen = self.wcslen + ctypes.set_conversion_mode("latin-1", "strict") + self.assertEqual(wcslen(u"abc"), 3) + self.assertEqual(wcslen(u"ab\u2070"), 3) + self.assertEqual(wcslen("abc"), 3) + self.assertEqual(wcslen("????"), 4) - def test_ascii_ignore(self): - ctypes.set_conversion_mode("ascii", "ignore") - self.assertEqual(wcslen(u"abc"), 3) - self.assertEqual(wcslen(u"ab\u2070"), 3) - # ignore error mode skips non-ascii characters - self.assertEqual(wcslen("abc"), 3) - self.assertEqual(wcslen("????"), 0) + def test_buffers(self): + ctypes.set_conversion_mode("ascii", "strict") + buf = ctypes.create_unicode_buffer("abc") + self.assertEqual(len(buf), 3+1) - def test_latin1_strict(self): - ctypes.set_conversion_mode("latin-1", "strict") - self.assertEqual(wcslen(u"abc"), 3) - self.assertEqual(wcslen(u"ab\u2070"), 3) - self.assertEqual(wcslen("abc"), 3) - self.assertEqual(wcslen("????"), 4) + ctypes.set_conversion_mode("ascii", "replace") + buf = ctypes.create_unicode_buffer("ab???") + self.assertEqual(buf[:], u"ab\uFFFD\uFFFD\uFFFD\0") + self.assertEqual(buf[::], u"ab\uFFFD\uFFFD\uFFFD\0") + self.assertEqual(buf[::-1], u"\0\uFFFD\uFFFD\uFFFDba") + self.assertEqual(buf[::2], u"a\uFFFD\uFFFD") + self.assertEqual(buf[6:5:-1], u"") - def test_buffers(self): - ctypes.set_conversion_mode("ascii", "strict") - buf = ctypes.create_unicode_buffer("abc") - self.assertEqual(len(buf), 3+1) + ctypes.set_conversion_mode("ascii", "ignore") + buf = ctypes.create_unicode_buffer("ab???") + # is that correct? not sure. But with 'ignore', you get what you pay for.. + self.assertEqual(buf[:], u"ab\0\0\0\0") + self.assertEqual(buf[::], u"ab\0\0\0\0") + self.assertEqual(buf[::-1], u"\0\0\0\0ba") + self.assertEqual(buf[::2], u"a\0\0") + self.assertEqual(buf[6:5:-1], u"") - ctypes.set_conversion_mode("ascii", "replace") - buf = ctypes.create_unicode_buffer("ab???") - self.assertEqual(buf[:], u"ab\uFFFD\uFFFD\uFFFD\0") - self.assertEqual(buf[::], u"ab\uFFFD\uFFFD\uFFFD\0") - self.assertEqual(buf[::-1], u"\0\uFFFD\uFFFD\uFFFDba") - self.assertEqual(buf[::2], u"a\uFFFD\uFFFD") - self.assertEqual(buf[6:5:-1], u"") + at need_symbol('c_wchar') +class StringTestCase(UnicodeTestCase): + @classmethod + def setUpClass(cls): + super(StringTestCase, cls).setUpClass() + cls.func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p - ctypes.set_conversion_mode("ascii", "ignore") - buf = ctypes.create_unicode_buffer("ab???") - # is that correct? not sure. But with 'ignore', you get what you pay for.. - self.assertEqual(buf[:], u"ab\0\0\0\0") - self.assertEqual(buf[::], u"ab\0\0\0\0") - self.assertEqual(buf[::-1], u"\0\0\0\0ba") - self.assertEqual(buf[::2], u"a\0\0") - self.assertEqual(buf[6:5:-1], u"") + def setUp(self): + func = self.func + self.prev_conv_mode = ctypes.set_conversion_mode("ascii", "strict") + func.argtypes = [ctypes.c_char_p] + func.restype = ctypes.c_char_p - import _ctypes_test - func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p + def tearDown(self): + func = self.func + ctypes.set_conversion_mode(*self.prev_conv_mode) + func.argtypes = None + func.restype = ctypes.c_int - class StringTestCase(UnicodeTestCase): - def setUp(self): - self.prev_conv_mode = ctypes.set_conversion_mode("ascii", "strict") - func.argtypes = [ctypes.c_char_p] - func.restype = ctypes.c_char_p + def test_ascii_replace(self): + func = self.func + ctypes.set_conversion_mode("ascii", "strict") + self.assertEqual(func("abc"), "abc") + self.assertEqual(func(u"abc"), "abc") + self.assertRaises(ctypes.ArgumentError, func, u"ab?") - def tearDown(self): - ctypes.set_conversion_mode(*self.prev_conv_mode) - func.argtypes = None - func.restype = ctypes.c_int + def test_ascii_ignore(self): + func = self.func + ctypes.set_conversion_mode("ascii", "ignore") + self.assertEqual(func("abc"), "abc") + self.assertEqual(func(u"abc"), "abc") + self.assertEqual(func(u"????"), "") - def test_ascii_replace(self): - ctypes.set_conversion_mode("ascii", "strict") - self.assertEqual(func("abc"), "abc") - self.assertEqual(func(u"abc"), "abc") - self.assertRaises(ctypes.ArgumentError, func, u"ab?") + def test_ascii_replace(self): + func = self.func + ctypes.set_conversion_mode("ascii", "replace") + self.assertEqual(func("abc"), "abc") + self.assertEqual(func(u"abc"), "abc") + self.assertEqual(func(u"????"), "????") - def test_ascii_ignore(self): - ctypes.set_conversion_mode("ascii", "ignore") - self.assertEqual(func("abc"), "abc") - self.assertEqual(func(u"abc"), "abc") - self.assertEqual(func(u"????"), "") + def test_buffers(self): + ctypes.set_conversion_mode("ascii", "strict") + buf = ctypes.create_string_buffer(u"abc") + self.assertEqual(len(buf), 3+1) - def test_ascii_replace(self): - ctypes.set_conversion_mode("ascii", "replace") - self.assertEqual(func("abc"), "abc") - self.assertEqual(func(u"abc"), "abc") - self.assertEqual(func(u"????"), "????") + ctypes.set_conversion_mode("ascii", "replace") + buf = ctypes.create_string_buffer(u"ab???") + self.assertEqual(buf[:], "ab???\0") + self.assertEqual(buf[::], "ab???\0") + self.assertEqual(buf[::-1], "\0???ba") + self.assertEqual(buf[::2], "a??") + self.assertEqual(buf[6:5:-1], "") - def test_buffers(self): - ctypes.set_conversion_mode("ascii", "strict") - buf = ctypes.create_string_buffer(u"abc") - self.assertEqual(len(buf), 3+1) - - ctypes.set_conversion_mode("ascii", "replace") - buf = ctypes.create_string_buffer(u"ab???") - self.assertEqual(buf[:], "ab???\0") - self.assertEqual(buf[::], "ab???\0") - self.assertEqual(buf[::-1], "\0???ba") - self.assertEqual(buf[::2], "a??") - self.assertEqual(buf[6:5:-1], "") - - ctypes.set_conversion_mode("ascii", "ignore") - buf = ctypes.create_string_buffer(u"ab???") - # is that correct? not sure. But with 'ignore', you get what you pay for.. - self.assertEqual(buf[:], "ab\0\0\0\0") - self.assertEqual(buf[::], "ab\0\0\0\0") - self.assertEqual(buf[::-1], "\0\0\0\0ba") + ctypes.set_conversion_mode("ascii", "ignore") + buf = ctypes.create_string_buffer(u"ab???") + # is that correct? not sure. But with 'ignore', you get what you pay for.. + self.assertEqual(buf[:], "ab\0\0\0\0") + self.assertEqual(buf[::], "ab\0\0\0\0") + self.assertEqual(buf[::-1], "\0\0\0\0ba") if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py --- a/Lib/ctypes/test/test_values.py +++ b/Lib/ctypes/test/test_values.py @@ -3,6 +3,7 @@ """ import unittest +import sys from ctypes import * import _ctypes_test @@ -21,62 +22,63 @@ ctdll = CDLL(_ctypes_test.__file__) self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") - class Win_ValuesTestCase(unittest.TestCase): - """This test only works when python itself is a dll/shared library""" + at unittest.skipUnless(sys.platform == 'win32', 'Windows-specific test') +class Win_ValuesTestCase(unittest.TestCase): + """This test only works when python itself is a dll/shared library""" - def test_optimizeflag(self): - # This test accesses the Py_OptimizeFlag intger, which is - # exported by the Python dll. + def test_optimizeflag(self): + # This test accesses the Py_OptimizeFlag intger, which is + # exported by the Python dll. - # It's value is set depending on the -O and -OO flags: - # if not given, it is 0 and __debug__ is 1. - # If -O is given, the flag is 1, for -OO it is 2. - # docstrings are also removed in the latter case. - opt = c_int.in_dll(pydll, "Py_OptimizeFlag").value - if __debug__: - self.assertEqual(opt, 0) - elif ValuesTestCase.__doc__ is not None: - self.assertEqual(opt, 1) - else: - self.assertEqual(opt, 2) + # It's value is set depending on the -O and -OO flags: + # if not given, it is 0 and __debug__ is 1. + # If -O is given, the flag is 1, for -OO it is 2. + # docstrings are also removed in the latter case. + opt = c_int.in_dll(pythonapi, "Py_OptimizeFlag").value + if __debug__: + self.assertEqual(opt, 0) + elif ValuesTestCase.__doc__ is not None: + self.assertEqual(opt, 1) + else: + self.assertEqual(opt, 2) - def test_frozentable(self): - # Python exports a PyImport_FrozenModules symbol. This is a - # pointer to an array of struct _frozen entries. The end of the - # array is marked by an entry containing a NULL name and zero - # size. + def test_frozentable(self): + # Python exports a PyImport_FrozenModules symbol. This is a + # pointer to an array of struct _frozen entries. The end of the + # array is marked by an entry containing a NULL name and zero + # size. - # In standard Python, this table contains a __hello__ - # module, and a __phello__ package containing a spam - # module. - class struct_frozen(Structure): - _fields_ = [("name", c_char_p), - ("code", POINTER(c_ubyte)), - ("size", c_int)] - FrozenTable = POINTER(struct_frozen) + # In standard Python, this table contains a __hello__ + # module, and a __phello__ package containing a spam + # module. + class struct_frozen(Structure): + _fields_ = [("name", c_char_p), + ("code", POINTER(c_ubyte)), + ("size", c_int)] + FrozenTable = POINTER(struct_frozen) - ft = FrozenTable.in_dll(pydll, "PyImport_FrozenModules") - # ft is a pointer to the struct_frozen entries: - items = [] - for entry in ft: - # This is dangerous. We *can* iterate over a pointer, but - # the loop will not terminate (maybe with an access - # violation;-) because the pointer instance has no size. - if entry.name is None: - break - items.append((entry.name, entry.size)) - import sys - if sys.version_info[:2] >= (2, 3): - expected = [("__hello__", 104), ("__phello__", -104), ("__phello__.spam", 104)] - else: - expected = [("__hello__", 100), ("__phello__", -100), ("__phello__.spam", 100)] - self.assertEqual(items, expected) + ft = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules") + # ft is a pointer to the struct_frozen entries: + items = [] + for entry in ft: + # This is dangerous. We *can* iterate over a pointer, but + # the loop will not terminate (maybe with an access + # violation;-) because the pointer instance has no size. + if entry.name is None: + break + items.append((entry.name, entry.size)) - from ctypes import _pointer_type_cache - del _pointer_type_cache[struct_frozen] + expected = [("__hello__", 104), + ("__phello__", -104), + ("__phello__.spam", 104)] + self.assertEqual(items, expected) - def test_undefined(self): - self.assertRaises(ValueError, c_int.in_dll, pydll, "Undefined_Symbol") + from ctypes import _pointer_type_cache + del _pointer_type_cache[struct_frozen] + + def test_undefined(self): + self.assertRaises(ValueError, c_int.in_dll, pythonapi, + "Undefined_Symbol") if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py --- a/Lib/ctypes/test/test_win32.py +++ b/Lib/ctypes/test/test_win32.py @@ -1,77 +1,79 @@ # Windows specific tests from ctypes import * -from ctypes.test import is_resource_enabled +from ctypes.test import requires import unittest, sys from test import test_support as support import _ctypes_test -if sys.platform == "win32" and sizeof(c_void_p) == sizeof(c_int): - # Only windows 32-bit has different calling conventions. +# Only windows 32-bit has different calling conventions. + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') + at unittest.skipUnless(sizeof(c_void_p) == sizeof(c_int), + "sizeof c_void_p and c_int differ") +class WindowsTestCase(unittest.TestCase): + def test_callconv_1(self): + # Testing stdcall function - class WindowsTestCase(unittest.TestCase): - def test_callconv_1(self): - # Testing stdcall function + IsWindow = windll.user32.IsWindow + # ValueError: Procedure probably called with not enough arguments + # (4 bytes missing) + self.assertRaises(ValueError, IsWindow) - IsWindow = windll.user32.IsWindow - # ValueError: Procedure probably called with not enough arguments (4 bytes missing) - self.assertRaises(ValueError, IsWindow) + # This one should succeed... + self.assertEqual(0, IsWindow(0)) - # This one should succeed... - self.assertEqual(0, IsWindow(0)) + # ValueError: Procedure probably called with too many arguments + # (8 bytes in excess) + self.assertRaises(ValueError, IsWindow, 0, 0, 0) - # ValueError: Procedure probably called with too many arguments (8 bytes in excess) - self.assertRaises(ValueError, IsWindow, 0, 0, 0) + def test_callconv_2(self): + # Calling stdcall function as cdecl - def test_callconv_2(self): - # Calling stdcall function as cdecl + IsWindow = cdll.user32.IsWindow - IsWindow = cdll.user32.IsWindow + # ValueError: Procedure called with not enough arguments + # (4 bytes missing) or wrong calling convention + self.assertRaises(ValueError, IsWindow, None) - # ValueError: Procedure called with not enough arguments (4 bytes missing) - # or wrong calling convention - self.assertRaises(ValueError, IsWindow, None) + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class FunctionCallTestCase(unittest.TestCase): + @requires("SEH") + def test_SEH(self): + # Call functions with invalid arguments, and make sure + # that access violations are trapped and raise an + # exception. + self.assertRaises(WindowsError, windll.kernel32.GetModuleHandleA, 32) -if sys.platform == "win32": - class FunctionCallTestCase(unittest.TestCase): + def test_noargs(self): + # This is a special case on win32 x64 + windll.user32.GetDesktopWindow() - if is_resource_enabled("SEH"): - def test_SEH(self): - # Call functions with invalid arguments, and make sure - # that access violations are trapped and raise an - # exception. - self.assertRaises(WindowsError, windll.kernel32.GetModuleHandleA, 32) + at unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') +class TestWintypes(unittest.TestCase): + def test_HWND(self): + from ctypes import wintypes + self.assertEqual(sizeof(wintypes.HWND), sizeof(c_void_p)) - def test_noargs(self): - # This is a special case on win32 x64 - windll.user32.GetDesktopWindow() + def test_PARAM(self): + from ctypes import wintypes + self.assertEqual(sizeof(wintypes.WPARAM), + sizeof(c_void_p)) + self.assertEqual(sizeof(wintypes.LPARAM), + sizeof(c_void_p)) - class TestWintypes(unittest.TestCase): - def test_HWND(self): - from ctypes import wintypes - self.assertEqual(sizeof(wintypes.HWND), sizeof(c_void_p)) + def test_COMError(self): + from _ctypes import COMError + if support.HAVE_DOCSTRINGS: + self.assertEqual(COMError.__doc__, + "Raised when a COM method call failed.") - def test_PARAM(self): - from ctypes import wintypes - self.assertEqual(sizeof(wintypes.WPARAM), - sizeof(c_void_p)) - self.assertEqual(sizeof(wintypes.LPARAM), - sizeof(c_void_p)) - - def test_COMError(self): - from _ctypes import COMError - if support.HAVE_DOCSTRINGS: - self.assertEqual(COMError.__doc__, - "Raised when a COM method call failed.") - - ex = COMError(-1, "text", ("details",)) - self.assertEqual(ex.hresult, -1) - self.assertEqual(ex.text, "text") - self.assertEqual(ex.details, ("details",)) + ex = COMError(-1, "text", ("details",)) + self.assertEqual(ex.hresult, -1) + self.assertEqual(ex.text, "text") + self.assertEqual(ex.details, ("details",)) class Structures(unittest.TestCase): - def test_struct_by_value(self): class POINT(Structure): _fields_ = [("x", c_long), diff --git a/Lib/ctypes/test/test_wintypes.py b/Lib/ctypes/test/test_wintypes.py --- a/Lib/ctypes/test/test_wintypes.py +++ b/Lib/ctypes/test/test_wintypes.py @@ -1,14 +1,12 @@ import sys import unittest -if not sys.platform.startswith('win'): - raise unittest.SkipTest('Windows-only test') +from ctypes import * -from ctypes import * -from ctypes import wintypes - + at unittest.skipUnless(sys.platform.startswith('win'), 'Windows-only test') class WinTypesTest(unittest.TestCase): def test_variant_bool(self): + from ctypes import wintypes # reads 16-bits from memory, anything non-zero is True for true_value in (1, 32767, 32768, 65535, 65537): true = POINTER(c_int16)(c_int16(true_value)) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -67,6 +67,9 @@ Tests ----- +- Issue #19493: Refactored the ctypes test package to skip tests explicitly + rather than silently. + - Issue #18492: All resources are now allowed when tests are not run by regrtest.py. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 13 23:23:48 2014 From: python-checkins at python.org (r.david.murray) Date: Fri, 13 Jun 2014 23:23:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzU5MDQ6IEFkZCBz?= =?utf-8?q?entence_about_the_encoding_of_strftime=27s_result=2E?= Message-ID: <3gqw0h3FHVz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/31adcc4c4391 changeset: 91168:31adcc4c4391 branch: 2.7 user: R David Murray date: Fri Jun 13 17:23:34 2014 -0400 summary: #5904: Add sentence about the encoding of strftime's result. files: Doc/library/time.rst | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Doc/library/time.rst b/Doc/library/time.rst --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -236,7 +236,9 @@ :func:`gmtime` or :func:`localtime` to a string as specified by the *format* argument. If *t* is not provided, the current time as returned by :func:`localtime` is used. *format* must be a string. :exc:`ValueError` is - raised if any field in *t* is outside of the allowed range. + raised if any field in *t* is outside of the allowed range. :func:`strftime` + returns a locale depedent byte string; the result may be converted to unicode + by doing ``strftime().decode(locale.getlocale()[1])``. .. versionchanged:: 2.1 Allowed *t* to be omitted. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 14 09:03:37 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 14 Jun 2014 09:03:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_typo?= Message-ID: <3gr8sj30hbz7LjV@mail.python.org> http://hg.python.org/cpython/rev/439ae988be8d changeset: 91169:439ae988be8d parent: 91166:273e3d52c0c9 user: Raymond Hettinger date: Sat Jun 14 00:03:28 2014 -0700 summary: Fix typo files: Lib/heapq.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/heapq.py b/Lib/heapq.py --- a/Lib/heapq.py +++ b/Lib/heapq.py @@ -430,7 +430,7 @@ # # Combining and simplifying for a rough estimate gives: # -# comparisons = n + k * (log(k, 2) * log(n/k)) + log(k, 2) + log(n/k)) +# comparisons = n + k * (log(k, 2) * log(n/k) + log(k, 2) + log(n/k)) # # Computing the number of comparisons for step 3: # ----------------------------------------------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 14 10:27:27 2014 From: python-checkins at python.org (vinay.sajip) Date: Sat, 14 Jun 2014 10:27:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzUy?= =?utf-8?q?=3A_Documented_change_to_behaviour_of_logging=2EgetLevelName=28?= =?utf-8?b?KS4=?= Message-ID: <3grBkR0dGWz7LjN@mail.python.org> http://hg.python.org/cpython/rev/277d099a134b changeset: 91170:277d099a134b branch: 3.4 parent: 91165:f44275c66fcf user: Vinay Sajip date: Sat Jun 14 09:26:26 2014 +0100 summary: Issue #21752: Documented change to behaviour of logging.getLevelName(). files: Doc/library/logging.rst | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -1049,6 +1049,11 @@ of the defined levels is passed in, the corresponding string representation is returned. Otherwise, the string 'Level %s' % lvl is returned. + .. versionchanged:: 3.4 + In Python versions earlier than 3.4, this function could also be passed a + text level, and would return the corresponding numeric value of the level. + This undocumented behaviour was a mistake, and has been removed in Python + 3.4. .. function:: makeLogRecord(attrdict) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 14 10:27:28 2014 From: python-checkins at python.org (vinay.sajip) Date: Sat, 14 Jun 2014 10:27:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Closes_=2321752=3A_Merged_update_from_3=2E4=2E?= Message-ID: <3grBkS2LdJz7Ljl@mail.python.org> http://hg.python.org/cpython/rev/174c30103b57 changeset: 91171:174c30103b57 parent: 91169:439ae988be8d parent: 91170:277d099a134b user: Vinay Sajip date: Sat Jun 14 09:27:10 2014 +0100 summary: Closes #21752: Merged update from 3.4. files: Doc/library/logging.rst | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -1049,6 +1049,11 @@ of the defined levels is passed in, the corresponding string representation is returned. Otherwise, the string 'Level %s' % lvl is returned. + .. versionchanged:: 3.4 + In Python versions earlier than 3.4, this function could also be passed a + text level, and would return the corresponding numeric value of the level. + This undocumented behaviour was a mistake, and has been removed in Python + 3.4. .. function:: makeLogRecord(attrdict) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Jun 14 10:47:22 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 14 Jun 2014 10:47:22 +0200 Subject: [Python-checkins] Daily reference leaks (273e3d52c0c9): sum=-6 Message-ID: results for 273e3d52c0c9 on branch "default" -------------------------------------------- test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [-2, 2, -2] references, sum=-2 test_site leaked [-2, 2, -2] memory blocks, sum=-2 test_urllib2net leaked [0, 1587, -1598] references, sum=-11 test_urllib2net leaked [0, 1406, -1406] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogV527Y_', '-x'] From python-checkins at python.org Sat Jun 14 11:23:35 2014 From: python-checkins at python.org (vinay.sajip) Date: Sat, 14 Jun 2014 11:23:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNzQy?= =?utf-8?q?=3A_Set_stream_to_None_after_closing=2E?= Message-ID: <3grCzC2x30z7Ljc@mail.python.org> http://hg.python.org/cpython/rev/bb8b0c7fefd0 changeset: 91172:bb8b0c7fefd0 branch: 2.7 parent: 91168:31adcc4c4391 user: Vinay Sajip date: Sat Jun 14 10:19:54 2014 +0100 summary: Issue #21742: Set stream to None after closing. files: Lib/logging/handlers.py | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -423,6 +423,7 @@ # we have an open file handle, clean it up self.stream.flush() self.stream.close() + self.stream = None # See Issue #21742: _open () might fail. # open a new file handle and get new stat info from that fd self.stream = self._open() self._statstream() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 14 11:23:36 2014 From: python-checkins at python.org (vinay.sajip) Date: Sat, 14 Jun 2014 11:23:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzQy?= =?utf-8?q?=3A_Set_stream_to_None_after_closing=2E?= Message-ID: <3grCzD4g7dz7Ljk@mail.python.org> http://hg.python.org/cpython/rev/6f1f38775991 changeset: 91173:6f1f38775991 branch: 3.4 parent: 91170:277d099a134b user: Vinay Sajip date: Sat Jun 14 10:22:05 2014 +0100 summary: Issue #21742: Set stream to None after closing. files: Lib/logging/handlers.py | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -463,6 +463,7 @@ # we have an open file handle, clean it up self.stream.flush() self.stream.close() + self.stream = None # See Issue #21742: _open () might fail. # open a new file handle and get new stat info from that fd self.stream = self._open() self._statstream() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 14 11:23:37 2014 From: python-checkins at python.org (vinay.sajip) Date: Sat, 14 Jun 2014 11:23:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Closes_=2321742=3A_Merged_fix_from_3=2E4=2E?= Message-ID: <3grCzF6Tqcz7Lk5@mail.python.org> http://hg.python.org/cpython/rev/9913ab26ca6f changeset: 91174:9913ab26ca6f parent: 91171:174c30103b57 parent: 91173:6f1f38775991 user: Vinay Sajip date: Sat Jun 14 10:23:20 2014 +0100 summary: Closes #21742: Merged fix from 3.4. files: Lib/logging/handlers.py | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -463,6 +463,7 @@ # we have an open file handle, clean it up self.stream.flush() self.stream.close() + self.stream = None # See Issue #21742: _open () might fail. # open a new file handle and get new stat info from that fd self.stream = self._open() self._statstream() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 14 17:04:12 2014 From: python-checkins at python.org (giampaolo.rodola) Date: Sat, 14 Jun 2014 17:04:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_fix_issue_=236916=3A_undoc?= =?utf-8?q?ument_deprecated_asynchat=2Efifo_class=2Eq?= Message-ID: <3grMXD2dGYz7Ljd@mail.python.org> http://hg.python.org/cpython/rev/42a645d74e9d changeset: 91175:42a645d74e9d user: Giampaolo Rodola' date: Sat Jun 14 17:03:42 2014 +0200 summary: fix issue #6916: undocument deprecated asynchat.fifo class.q files: Doc/library/asynchat.rst | 34 ---------------------------- Misc/NEWS | 2 + 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/Doc/library/asynchat.rst b/Doc/library/asynchat.rst --- a/Doc/library/asynchat.rst +++ b/Doc/library/asynchat.rst @@ -147,40 +147,6 @@ by the channel after :meth:`found_terminator` is called. -asynchat - Auxiliary Classes ------------------------------------------- - -.. class:: fifo(list=None) - - A :class:`fifo` holding data which has been pushed by the application but - not yet popped for writing to the channel. A :class:`fifo` is a list used - to hold data and/or producers until they are required. If the *list* - argument is provided then it should contain producers or data items to be - written to the channel. - - - .. method:: is_empty() - - Returns ``True`` if and only if the fifo is empty. - - - .. method:: first() - - Returns the least-recently :meth:`push`\ ed item from the fifo. - - - .. method:: push(data) - - Adds the given data (which may be a string or a producer object) to the - producer fifo. - - - .. method:: pop() - - If the fifo is not empty, returns ``True, first()``, deleting the popped - item. Returns ``False, None`` for an empty fifo. - - .. _asynchat-example: asynchat Example diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -550,6 +550,8 @@ Documentation ------------- +- Issue #6916: undocument deprecated asynchat.fifo class. + - Issue #17386: Expanded functionality of the ``Doc/make.bat`` script to make it much more comparable to ``Doc/Makefile``. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 01:43:55 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 15 Jun 2014 01:43:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Factor_common_code_into_in?= =?utf-8?q?ternal_functions=2E?= Message-ID: <3grb3v5C5dz7LjV@mail.python.org> http://hg.python.org/cpython/rev/7884e42183ed changeset: 91176:7884e42183ed user: Raymond Hettinger date: Sat Jun 14 16:43:35 2014 -0700 summary: Factor common code into internal functions. Clean-up names of static functions. Use Py_RETURN_NONE macro. Expose private functions needed to support merge(). Move C imports to the bottom of the Python file. files: Lib/heapq.py | 28 +++++--- Lib/test/test_heapq.py | 4 +- Modules/_heapqmodule.c | 98 +++++++++++++++++------------ 3 files changed, 76 insertions(+), 54 deletions(-) diff --git a/Lib/heapq.py b/Lib/heapq.py --- a/Lib/heapq.py +++ b/Lib/heapq.py @@ -311,16 +311,6 @@ heap[pos] = newitem _siftdown_max(heap, startpos, pos) -# If available, use C implementation -try: - from _heapq import * -except ImportError: - pass -try: - from _heapq import _heapreplace_max -except ImportError: - pass - def merge(*iterables, key=None, reverse=False): '''Merge multiple sorted inputs into a single sorted output. @@ -592,6 +582,24 @@ result.sort(reverse=True) return [r[2] for r in result] +# If available, use C implementation +try: + from _heapq import * +except ImportError: + pass +try: + from _heapq import _heapreplace_max +except ImportError: + pass +try: + from _heapq import _heapify_max +except ImportError: + pass +try: + from _heapq import _heappop_max +except ImportError: + pass + if __name__ == "__main__": diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py --- a/Lib/test/test_heapq.py +++ b/Lib/test/test_heapq.py @@ -13,8 +13,8 @@ # _heapq.nlargest/nsmallest are saved in heapq._nlargest/_smallest when # _heapq is imported, so check them there -func_names = ['heapify', 'heappop', 'heappush', 'heappushpop', - 'heapreplace', '_heapreplace_max'] +func_names = ['heapify', 'heappop', 'heappush', 'heappushpop', 'heapreplace', + '_heappop_max', '_heapreplace_max', '_heapify_max'] class TestModules(TestCase): def test_py_functions(self): diff --git a/Modules/_heapqmodule.c b/Modules/_heapqmodule.c --- a/Modules/_heapqmodule.c +++ b/Modules/_heapqmodule.c @@ -9,7 +9,7 @@ #include "Python.h" static int -_siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) +siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) { PyObject *newitem, *parent; Py_ssize_t parentpos, size; @@ -48,7 +48,7 @@ } static int -_siftup(PyListObject *heap, Py_ssize_t pos) +siftup(PyListObject *heap, Py_ssize_t pos) { Py_ssize_t startpos, endpos, childpos, rightpos, limit; PyObject *tmp1, *tmp2; @@ -91,7 +91,7 @@ pos = childpos; } /* Bubble it up to its final resting place (by sifting its parents down). */ - return _siftdown(heap, startpos, pos); + return siftdown(heap, startpos, pos); } static PyObject * @@ -110,17 +110,16 @@ if (PyList_Append(heap, item) == -1) return NULL; - if (_siftdown((PyListObject *)heap, 0, PyList_GET_SIZE(heap)-1) == -1) + if (siftdown((PyListObject *)heap, 0, PyList_GET_SIZE(heap)-1) == -1) return NULL; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyDoc_STRVAR(heappush_doc, "heappush(heap, item) -> None. Push item onto heap, maintaining the heap invariant."); static PyObject * -heappop(PyObject *self, PyObject *heap) +heappop_internal(PyObject *heap, int siftup_func(PyListObject *, Py_ssize_t)) { PyObject *lastelt, *returnitem; Py_ssize_t n; @@ -130,7 +129,7 @@ return NULL; } - /* # raises appropriate IndexError if heap is empty */ + /* raises IndexError if the heap is empty */ n = PyList_GET_SIZE(heap); if (n == 0) { PyErr_SetString(PyExc_IndexError, "index out of range"); @@ -149,18 +148,24 @@ return lastelt; returnitem = PyList_GET_ITEM(heap, 0); PyList_SET_ITEM(heap, 0, lastelt); - if (_siftup((PyListObject *)heap, 0) == -1) { + if (siftup_func((PyListObject *)heap, 0) == -1) { Py_DECREF(returnitem); return NULL; } return returnitem; } +static PyObject * +heappop(PyObject *self, PyObject *heap) +{ + return heappop_internal(heap, siftup); +} + PyDoc_STRVAR(heappop_doc, "Pop the smallest item off the heap, maintaining the heap invariant."); static PyObject * -heapreplace(PyObject *self, PyObject *args) +heapreplace_internal(PyObject *args, int siftup_func(PyListObject *, Py_ssize_t)) { PyObject *heap, *item, *returnitem; @@ -180,13 +185,19 @@ returnitem = PyList_GET_ITEM(heap, 0); Py_INCREF(item); PyList_SET_ITEM(heap, 0, item); - if (_siftup((PyListObject *)heap, 0) == -1) { + if (siftup_func((PyListObject *)heap, 0) == -1) { Py_DECREF(returnitem); return NULL; } return returnitem; } +static PyObject * +heapreplace(PyObject *self, PyObject *args) +{ + return heapreplace_internal(args, siftup); +} + PyDoc_STRVAR(heapreplace_doc, "heapreplace(heap, item) -> value. Pop and return the current smallest value, and add the new item.\n\ \n\ @@ -227,7 +238,7 @@ returnitem = PyList_GET_ITEM(heap, 0); Py_INCREF(item); PyList_SET_ITEM(heap, 0, item); - if (_siftup((PyListObject *)heap, 0) == -1) { + if (siftup((PyListObject *)heap, 0) == -1) { Py_DECREF(returnitem); return NULL; } @@ -240,7 +251,7 @@ heappush() followed by a separate call to heappop()."); static PyObject * -heapify(PyObject *self, PyObject *heap) +heapify_internal(PyObject *heap, int siftup_func(PyListObject *, Py_ssize_t)) { Py_ssize_t i, n; @@ -258,17 +269,22 @@ and that's again n//2-1. */ for (i=n/2-1 ; i>=0 ; i--) - if(_siftup((PyListObject *)heap, i) == -1) + if(siftup_func((PyListObject *)heap, i) == -1) return NULL; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; +} + +static PyObject * +heapify(PyObject *self, PyObject *heap) +{ + return heapify_internal(heap, siftup); } PyDoc_STRVAR(heapify_doc, "Transform list into a heap, in-place, in O(len(heap)) time."); static int -_siftdownmax(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) +siftdown_max(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) { PyObject *newitem, *parent; Py_ssize_t parentpos, size; @@ -307,7 +323,7 @@ } static int -_siftupmax(PyListObject *heap, Py_ssize_t pos) +siftup_max(PyListObject *heap, Py_ssize_t pos) { Py_ssize_t startpos, endpos, childpos, rightpos, limit; PyObject *tmp1, *tmp2; @@ -350,39 +366,33 @@ pos = childpos; } /* Bubble it up to its final resting place (by sifting its parents down). */ - return _siftdownmax(heap, startpos, pos); + return siftdown_max(heap, startpos, pos); } static PyObject * -_heapreplace_max(PyObject *self, PyObject *args) +heappop_max(PyObject *self, PyObject *heap) { - PyObject *heap, *item, *returnitem; + return heappop_internal(heap, siftup_max); +} - if (!PyArg_UnpackTuple(args, "_heapreplace_max", 2, 2, &heap, &item)) - return NULL; +PyDoc_STRVAR(heappop_max_doc, "Maxheap variant of heappop."); - if (!PyList_Check(heap)) { - PyErr_SetString(PyExc_TypeError, "heap argument must be a list"); - return NULL; - } - - if (PyList_GET_SIZE(heap) < 1) { - PyErr_SetString(PyExc_IndexError, "index out of range"); - return NULL; - } - - returnitem = PyList_GET_ITEM(heap, 0); - Py_INCREF(item); - PyList_SET_ITEM(heap, 0, item); - if (_siftupmax((PyListObject *)heap, 0) == -1) { - Py_DECREF(returnitem); - return NULL; - } - return returnitem; +static PyObject * +heapreplace_max(PyObject *self, PyObject *args) +{ + return heapreplace_internal(args, siftup_max); } PyDoc_STRVAR(heapreplace_max_doc, "Maxheap variant of heapreplace"); +static PyObject * +heapify_max(PyObject *self, PyObject *heap) +{ + return heapify_internal(heap, siftup_max); +} + +PyDoc_STRVAR(heapify_max_doc, "Maxheap variant of heapify."); + static PyMethodDef heapq_methods[] = { {"heappush", (PyCFunction)heappush, METH_VARARGS, heappush_doc}, @@ -394,8 +404,12 @@ METH_VARARGS, heapreplace_doc}, {"heapify", (PyCFunction)heapify, METH_O, heapify_doc}, - {"_heapreplace_max",(PyCFunction)_heapreplace_max, + {"_heappop_max", (PyCFunction)heappop_max, + METH_O, heappop_max_doc}, + {"_heapreplace_max",(PyCFunction)heapreplace_max, METH_VARARGS, heapreplace_max_doc}, + {"_heapify_max", (PyCFunction)heapify_max, + METH_O, heapify_max_doc}, {NULL, NULL} /* sentinel */ }; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 03:41:57 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 15 Jun 2014 03:41:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_url_unquote_th?= =?utf-8?q?e_path_before_checking_if_it_refers_to_a_CGI_script_=28closes?= Message-ID: <3grdh52gnLz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/b4bab0788768 changeset: 91177:b4bab0788768 branch: 2.7 parent: 91172:bb8b0c7fefd0 user: Benjamin Peterson date: Sat Jun 14 18:36:29 2014 -0700 summary: url unquote the path before checking if it refers to a CGI script (closes #21766) files: Lib/CGIHTTPServer.py | 2 +- Lib/test/test_httpservers.py | 5 +++++ Misc/NEWS | 3 +++ 3 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Lib/CGIHTTPServer.py b/Lib/CGIHTTPServer.py --- a/Lib/CGIHTTPServer.py +++ b/Lib/CGIHTTPServer.py @@ -84,7 +84,7 @@ path begins with one of the strings in self.cgi_directories (and the next character is a '/' or the end of the string). """ - collapsed_path = _url_collapse_path(self.path) + collapsed_path = _url_collapse_path(urllib.unquote(self.path)) dir_sep = collapsed_path.find('/', 1) head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] if head in self.cgi_directories: 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 @@ -510,6 +510,11 @@ (res.read(), res.getheader('Content-type'), res.status)) self.assertEqual(os.environ['SERVER_SOFTWARE'], signature) + def test_urlquote_decoding_in_cgi_check(self): + res = self.request('/cgi-bin%2ffile1.py') + self.assertEqual((b'Hello World\n', 'text/html', 200), + (res.read(), res.getheader('Content-type'), res.status)) + class SimpleHTTPRequestHandlerTestCase(unittest.TestCase): """ Test url parsing """ diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -25,6 +25,9 @@ Library ------- +- Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths + before checking for a CGI script at that path. + - Issue #21310: Fixed possible resource leak in failed open(). - Issue #21304: Backport the key derivation function hashlib.pbkdf2_hmac from -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 03:41:58 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 15 Jun 2014 03:41:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E2=29=3A_url_unquote_th?= =?utf-8?q?e_path_before_checking_if_it_refers_to_a_CGI_script_=28closes?= Message-ID: <3grdh64YTmz7Ljq@mail.python.org> http://hg.python.org/cpython/rev/e47422855841 changeset: 91178:e47422855841 branch: 3.2 parent: 90265:0a7d4cdc4c8d user: Benjamin Peterson date: Sat Jun 14 18:36:29 2014 -0700 summary: url unquote the path before checking if it refers to a CGI script (closes #21766) files: Lib/http/server.py | 2 +- Lib/test/test_httpservers.py | 5 +++++ Misc/NEWS | 3 +++ 3 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -946,7 +946,7 @@ (and the next character is a '/' or the end of the string). """ - collapsed_path = _url_collapse_path(self.path) + collapsed_path = _url_collapse_path(urllib.parse.unquote(self.path)) dir_sep = collapsed_path.find('/', 1) head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] if head in self.cgi_directories: 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 @@ -461,6 +461,11 @@ (res.read(), res.getheader('Content-type'), res.status)) self.assertEqual(os.environ['SERVER_SOFTWARE'], signature) + def test_urlquote_decoding_in_cgi_check(self): + res = self.request('/cgi-bin%2ffile1.py') + self.assertEqual((b'Hello World\n', 'text/html', 200), + (res.read(), res.getheader('Content-type'), res.status)) + class SocketlessRequestHandler(SimpleHTTPRequestHandler): def __init__(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Library ------- +- Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths + before checking for a CGI script at that path. + - Fix arbitrary memory access in JSONDecoder.raw_decode with a negative second parameter. Bug reported by Guido Vranken. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 03:41:59 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 15 Jun 2014 03:41:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy4yIC0+IDMuMyk6?= =?utf-8?q?_merge_3=2E2_=28=2321766=29?= Message-ID: <3grdh76SlFz7Lk4@mail.python.org> http://hg.python.org/cpython/rev/5676797f3a3e changeset: 91179:5676797f3a3e branch: 3.3 parent: 90675:1a3f2d0ced35 parent: 91178:e47422855841 user: Benjamin Peterson date: Sat Jun 14 18:40:10 2014 -0700 summary: merge 3.2 (#21766) files: Lib/http/server.py | 2 +- Lib/test/test_httpservers.py | 5 +++++ Misc/NEWS | 3 +++ 3 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -971,7 +971,7 @@ (and the next character is a '/' or the end of the string). """ - collapsed_path = _url_collapse_path(self.path) + collapsed_path = _url_collapse_path(urllib.parse.unquote(self.path)) dir_sep = collapsed_path.find('/', 1) head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] if head in self.cgi_directories: 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 @@ -464,6 +464,11 @@ (res.read(), res.getheader('Content-type'), res.status)) self.assertEqual(os.environ['SERVER_SOFTWARE'], signature) + def test_urlquote_decoding_in_cgi_check(self): + res = self.request('/cgi-bin%2ffile1.py') + self.assertEqual((b'Hello World\n', 'text/html', 200), + (res.read(), res.getheader('Content-type'), res.status)) + class SocketlessRequestHandler(SimpleHTTPRequestHandler): def __init__(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,9 @@ Library ------- +- Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths + before checking for a CGI script at that path. + - Fix arbitrary memory access in JSONDecoder.raw_decode with a negative second parameter. Bug reported by Guido Vranken. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 03:42:01 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 15 Jun 2014 03:42:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy4zIC0+IDMuNCk6?= =?utf-8?q?_merge_3=2E3_=28=2321766=29?= Message-ID: <3grdh91GPXz7LkM@mail.python.org> http://hg.python.org/cpython/rev/847e288d6e93 changeset: 91180:847e288d6e93 branch: 3.4 parent: 91173:6f1f38775991 parent: 91179:5676797f3a3e user: Benjamin Peterson date: Sat Jun 14 18:41:13 2014 -0700 summary: merge 3.3 (#21766) files: Lib/http/server.py | 2 +- Lib/test/test_httpservers.py | 5 +++++ Misc/NEWS | 3 +++ 3 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -977,7 +977,7 @@ (and the next character is a '/' or the end of the string). """ - collapsed_path = _url_collapse_path(self.path) + collapsed_path = _url_collapse_path(urllib.parse.unquote(self.path)) dir_sep = collapsed_path.find('/', 1) head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] if head in self.cgi_directories: 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 @@ -485,6 +485,11 @@ (res.read(), res.getheader('Content-type'), res.status)) self.assertEqual(os.environ['SERVER_SOFTWARE'], signature) + def test_urlquote_decoding_in_cgi_check(self): + res = self.request('/cgi-bin%2ffile1.py') + self.assertEqual((b'Hello World\n', 'text/html', 200), + (res.read(), res.getheader('Content-type'), res.status)) + class SocketlessRequestHandler(SimpleHTTPRequestHandler): def __init__(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -26,6 +26,9 @@ run_forever() and run_until_complete() methods of asyncio.BaseEventLoop now raise an exception if the event loop was closed. +- Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths + before checking for a CGI script at that path. + - Issue #21310: Fixed possible resource leak in failed open(). - Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 03:42:02 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 15 Jun 2014 03:42:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjE3NjYp?= Message-ID: <3grdhB38xbz7LkM@mail.python.org> http://hg.python.org/cpython/rev/f8b3bb5eb190 changeset: 91181:f8b3bb5eb190 parent: 91176:7884e42183ed parent: 91180:847e288d6e93 user: Benjamin Peterson date: Sat Jun 14 18:41:31 2014 -0700 summary: merge 3.4 (#21766) files: Lib/http/server.py | 2 +- Lib/test/test_httpservers.py | 5 +++++ Misc/NEWS | 3 +++ 3 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -977,7 +977,7 @@ (and the next character is a '/' or the end of the string). """ - collapsed_path = _url_collapse_path(self.path) + collapsed_path = _url_collapse_path(urllib.parse.unquote(self.path)) dir_sep = collapsed_path.find('/', 1) head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] if head in self.cgi_directories: 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 @@ -485,6 +485,11 @@ (res.read(), res.getheader('Content-type'), res.status)) self.assertEqual(os.environ['SERVER_SOFTWARE'], signature) + def test_urlquote_decoding_in_cgi_check(self): + res = self.request('/cgi-bin%2ffile1.py') + self.assertEqual((b'Hello World\n', 'text/html', 200), + (res.read(), res.getheader('Content-type'), res.status)) + class SocketlessRequestHandler(SimpleHTTPRequestHandler): def __init__(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -106,6 +106,9 @@ run_forever() and run_until_complete() methods of asyncio.BaseEventLoop now raise an exception if the event loop was closed. +- Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths + before checking for a CGI script at that path. + - Issue #21310: Fixed possible resource leak in failed open(). - Issue #21256: Printout of keyword args should be in deterministic order in -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 03:52:21 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 15 Jun 2014 03:52:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_document_IOBas?= =?utf-8?b?ZS5fX2RlbF9fJ3MgYmVoYXZpb3IgKGNsb3NlcyAjMjE3NjQp?= Message-ID: <3grdw569wqz7LjT@mail.python.org> http://hg.python.org/cpython/rev/601a08fcb507 changeset: 91182:601a08fcb507 branch: 3.4 parent: 91180:847e288d6e93 user: Benjamin Peterson date: Sat Jun 14 18:51:34 2014 -0700 summary: document IOBase.__del__'s behavior (closes #21764) Patch from Nikolaus Rath. files: Doc/library/io.rst | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -353,6 +353,12 @@ is usual for each of the lines provided to have a line separator at the end. + .. method:: __del__() + + Prepare for object destruction. :class:`IOBase` provides a default + implementation of this method that calls the instance's + :meth:`~IOBase.close` method. + .. class:: RawIOBase -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 03:52:23 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 15 Jun 2014 03:52:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_document_IOBas?= =?utf-8?b?ZS5fX2RlbF9fJ3MgYmVoYXZpb3IgKGNsb3NlcyAjMjE3NjQp?= Message-ID: <3grdw70s5xz7LjT@mail.python.org> http://hg.python.org/cpython/rev/4dca82f8d91d changeset: 91183:4dca82f8d91d branch: 2.7 parent: 91177:b4bab0788768 user: Benjamin Peterson date: Sat Jun 14 18:51:34 2014 -0700 summary: document IOBase.__del__'s behavior (closes #21764) Patch from Nikolaus Rath. files: Doc/library/io.rst | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -346,6 +346,12 @@ is usual for each of the lines provided to have a line separator at the end. + .. method:: __del__() + + Prepare for object destruction. :class:`IOBase` provides a default + implementation of this method that calls the instance's + :meth:`~IOBase.close` method. + .. class:: RawIOBase -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 03:52:24 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 15 Jun 2014 03:52:24 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjE3NjQp?= Message-ID: <3grdw82gSkz7Ljy@mail.python.org> http://hg.python.org/cpython/rev/787cd41d0404 changeset: 91184:787cd41d0404 parent: 91181:f8b3bb5eb190 parent: 91182:601a08fcb507 user: Benjamin Peterson date: Sat Jun 14 18:52:14 2014 -0700 summary: merge 3.4 (#21764) files: Doc/library/io.rst | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -353,6 +353,12 @@ is usual for each of the lines provided to have a line separator at the end. + .. method:: __del__() + + Prepare for object destruction. :class:`IOBase` provides a default + implementation of this method that calls the instance's + :meth:`~IOBase.close` method. + .. class:: RawIOBase -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 05:41:42 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 15 Jun 2014 05:41:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_19898=3A__Add_test_f?= =?utf-8?q?or_dequereviter=5Fnew=2E?= Message-ID: <3grhLG5lVHz7Ljd@mail.python.org> http://hg.python.org/cpython/rev/4a48450f2505 changeset: 91185:4a48450f2505 user: Raymond Hettinger date: Sat Jun 14 20:41:22 2014 -0700 summary: Issue 19898: Add test for dequereviter_new. (Patch contributed by Claudiu Popa.) files: Lib/test/test_deque.py | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -507,6 +507,11 @@ for s in ('abcd', range(2000)): self.assertEqual(list(reversed(deque(s))), list(reversed(s))) + def test_reversed_new(self): + klass = type(reversed(deque())) + for s in ('abcd', range(2000)): + self.assertEqual(list(klass(deque(s))), list(reversed(s))) + def test_gc_doesnt_blowup(self): import gc # This used to assert-fail in deque_traverse() under a debug -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sun Jun 15 10:48:07 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 15 Jun 2014 10:48:07 +0200 Subject: [Python-checkins] Daily reference leaks (7884e42183ed): sum=-2995 Message-ID: results for 7884e42183ed on branch "default" -------------------------------------------- test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [0, 2, -2] references, sum=0 test_site leaked [0, 2, -2] memory blocks, sum=0 test_urllib2net leaked [-1598, 0, 0] references, sum=-1598 test_urllib2net leaked [-1408, 2, 0] memory blocks, sum=-1406 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogftM2Cb', '-x'] From python-checkins at python.org Sun Jun 15 23:40:23 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 15 Jun 2014 23:40:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Update_comment_to_reflect_?= =?utf-8?q?using_the_default_parameter_with_min=28=29_and_max=28=29=2E?= Message-ID: <3gs8Gv0hJCz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/4b0775631ebb changeset: 91186:4b0775631ebb user: Raymond Hettinger date: Sun Jun 15 14:40:18 2014 -0700 summary: Update comment to reflect using the default parameter with min() and max(). files: Lib/heapq.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/heapq.py b/Lib/heapq.py --- a/Lib/heapq.py +++ b/Lib/heapq.py @@ -464,7 +464,7 @@ Equivalent to: sorted(iterable, key=key)[:n] """ - # Short-cut for n==1 is to use min() when len(iterable)>0 + # Short-cut for n==1 is to use min() if n == 1: it = iter(iterable) sentinel = object() @@ -527,7 +527,7 @@ Equivalent to: sorted(iterable, key=key, reverse=True)[:n] """ - # Short-cut for n==1 is to use max() when len(iterable)>0 + # Short-cut for n==1 is to use max() if n == 1: it = iter(iterable) sentinel = object() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 23:50:47 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 15 Jun 2014 23:50:47 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzc0?= =?utf-8?q?=3A_Fix_incorrect_variable_in_xml=2Edom=2Eminidom?= Message-ID: <3gs8Vv2xnwz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/ca33faa214ab changeset: 91187:ca33faa214ab branch: 3.4 parent: 91182:601a08fcb507 user: Raymond Hettinger date: Sun Jun 15 14:48:19 2014 -0700 summary: Issue #21774: Fix incorrect variable in xml.dom.minidom files: Lib/test/test_minidom.py | 7 +++++++ Lib/xml/dom/minidom.py | 2 +- Misc/NEWS | 4 ++++ 3 files changed, 12 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1531,6 +1531,13 @@ num_children_after = len(doc.childNodes) self.assertTrue(num_children_after == num_children_before - 1) + def testProcessingInstructionNameError(self): + # wrong variable in .nodeValue property will + # lead to "NameError: name 'data' is not defined" + doc = parse(tstfile) + pi = doc.createProcessingInstruction("y", "z") + pi.nodeValue = "crash" + def test_main(): run_unittest(MinidomTest) diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -976,7 +976,7 @@ def _get_nodeValue(self): return self.data def _set_nodeValue(self, value): - self.data = data + self.data = value nodeValue = property(_get_nodeValue, _set_nodeValue) # nodeName is an alias for target diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -26,6 +26,10 @@ run_forever() and run_until_complete() methods of asyncio.BaseEventLoop now raise an exception if the event loop was closed. +- Issue #21774: Fixed NameError for an incorrect variable reference in the + XML Minidom code for creating processing instructions. + (Found and fixed by Claudiu Popa.) + - Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths before checking for a CGI script at that path. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 15 23:50:48 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 15 Jun 2014 23:50:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gs8Vw4k8wz7Ljb@mail.python.org> http://hg.python.org/cpython/rev/d1d677516e49 changeset: 91188:d1d677516e49 parent: 91186:4b0775631ebb parent: 91187:ca33faa214ab user: Raymond Hettinger date: Sun Jun 15 14:50:39 2014 -0700 summary: merge files: Lib/test/test_minidom.py | 7 +++++++ Lib/xml/dom/minidom.py | 2 +- 2 files changed, 8 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1531,6 +1531,13 @@ num_children_after = len(doc.childNodes) self.assertTrue(num_children_after == num_children_before - 1) + def testProcessingInstructionNameError(self): + # wrong variable in .nodeValue property will + # lead to "NameError: name 'data' is not defined" + doc = parse(tstfile) + pi = doc.createProcessingInstruction("y", "z") + pi.nodeValue = "crash" + def test_main(): run_unittest(MinidomTest) diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -976,7 +976,7 @@ def _get_nodeValue(self): return self.data def _set_nodeValue(self, value): - self.data = data + self.data = value nodeValue = property(_get_nodeValue, _set_nodeValue) # nodeName is an alias for target -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 02:52:35 2014 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 16 Jun 2014 02:52:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Isolate_the_su?= =?utf-8?q?bprocess_test=5Fclose=5Ffds=5Fwhen=5Fmax=5Ffd=5Fis=5Flowered_te?= =?utf-8?q?st_so?= Message-ID: <3gsDXg5zrYz7LjV@mail.python.org> http://hg.python.org/cpython/rev/3c57f7c4f13e changeset: 91189:3c57f7c4f13e branch: 3.4 parent: 91187:ca33faa214ab user: Gregory P. Smith date: Sun Jun 15 17:51:04 2014 -0700 summary: Isolate the subprocess test_close_fds_when_max_fd_is_lowered test so that the rlimit calls happens in a child process rather than the TestCase process to attempt to fix the gentoo buildbot's "Too many open files" error. files: Lib/test/test_subprocess.py | 38 +++++++++++++++++++----- 1 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1934,6 +1934,20 @@ """Confirm that issue21618 is fixed (may fail under valgrind).""" fd_status = support.findfile("fd_status.py", subdir="subprocessdata") + # This launches the meat of the test in a child process to + # avoid messing with the larger unittest processes maximum + # number of file descriptors. + # This process launches: + # +--> Process that lowers its RLIMIT_NOFILE aftr setting up + # a bunch of high open fds above the new lower rlimit. + # Those are reported via stdout before launching a new + # process with close_fds=False to run the actual test: + # +--> The TEST: This one launches a fd_status.py + # subprocess with close_fds=True so we can find out if + # any of the fds above the lowered rlimit are still open. + p = subprocess.Popen([sys.executable, '-c', textwrap.dedent( + ''' + import os, resource, subprocess, sys, textwrap open_fds = set() # Add a bunch more fds to pass down. for _ in range(40): @@ -1949,12 +1963,15 @@ open_fds.remove(fd) for fd in open_fds: - self.addCleanup(os.close, fd) + #self.addCleanup(os.close, fd) os.set_inheritable(fd, True) max_fd_open = max(open_fds) - import resource + # Communicate the open_fds to the parent unittest.TestCase process. + print(','.join(map(str, sorted(open_fds)))) + sys.stdout.flush() + rlim_cur, rlim_max = resource.getrlimit(resource.RLIMIT_NOFILE) try: # 29 is lower than the highest fds we are leaving open. @@ -1965,22 +1982,27 @@ # An explicit list of fds to check is passed to fd_status.py as # letting fd_status rely on its default logic would miss the # fds above rlim_cur as it normally only checks up to that limit. - p = subprocess.Popen( + subprocess.Popen( [sys.executable, '-c', textwrap.dedent(""" import subprocess, sys - subprocess.Popen([sys.executable, {fd_status!r}] + + subprocess.Popen([sys.executable, %r] + [str(x) for x in range({max_fd})], close_fds=True).wait() - """.format(fd_status=fd_status, max_fd=max_fd_open+1))], - stdout=subprocess.PIPE, close_fds=False) + """.format(max_fd=max_fd_open+1))], + close_fds=False).wait() finally: resource.setrlimit(resource.RLIMIT_NOFILE, (rlim_cur, rlim_max)) + ''' % fd_status)], stdout=subprocess.PIPE) output, unused_stderr = p.communicate() - remaining_fds = set(map(int, output.strip().split(b','))) + output_lines = output.splitlines() + self.assertEqual(len(output_lines), 2, + msg="expected exactly two lines of output:\n%s" % output) + opened_fds = set(map(int, output_lines[0].strip().split(b','))) + remaining_fds = set(map(int, output_lines[1].strip().split(b','))) - self.assertFalse(remaining_fds & open_fds, + self.assertFalse(remaining_fds & opened_fds, msg="Some fds were left open.") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 02:52:37 2014 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 16 Jun 2014 02:52:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Isolate_the_subprocess_test=5Fclose=5Ffds=5Fwhen=5Fmax?= =?utf-8?q?=5Ffd=5Fis=5Flowered_test_so?= Message-ID: <3gsDXj31rcz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/13343e6696a4 changeset: 91190:13343e6696a4 parent: 91188:d1d677516e49 parent: 91189:3c57f7c4f13e user: Gregory P. Smith date: Sun Jun 15 17:52:26 2014 -0700 summary: Isolate the subprocess test_close_fds_when_max_fd_is_lowered test so that the rlimit calls happens in a child process rather than the TestCase process to attempt to fix the gentoo buildbot's "Too many open files" error. files: Lib/test/test_subprocess.py | 38 +++++++++++++++++++----- 1 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1934,6 +1934,20 @@ """Confirm that issue21618 is fixed (may fail under valgrind).""" fd_status = support.findfile("fd_status.py", subdir="subprocessdata") + # This launches the meat of the test in a child process to + # avoid messing with the larger unittest processes maximum + # number of file descriptors. + # This process launches: + # +--> Process that lowers its RLIMIT_NOFILE aftr setting up + # a bunch of high open fds above the new lower rlimit. + # Those are reported via stdout before launching a new + # process with close_fds=False to run the actual test: + # +--> The TEST: This one launches a fd_status.py + # subprocess with close_fds=True so we can find out if + # any of the fds above the lowered rlimit are still open. + p = subprocess.Popen([sys.executable, '-c', textwrap.dedent( + ''' + import os, resource, subprocess, sys, textwrap open_fds = set() # Add a bunch more fds to pass down. for _ in range(40): @@ -1949,12 +1963,15 @@ open_fds.remove(fd) for fd in open_fds: - self.addCleanup(os.close, fd) + #self.addCleanup(os.close, fd) os.set_inheritable(fd, True) max_fd_open = max(open_fds) - import resource + # Communicate the open_fds to the parent unittest.TestCase process. + print(','.join(map(str, sorted(open_fds)))) + sys.stdout.flush() + rlim_cur, rlim_max = resource.getrlimit(resource.RLIMIT_NOFILE) try: # 29 is lower than the highest fds we are leaving open. @@ -1965,22 +1982,27 @@ # An explicit list of fds to check is passed to fd_status.py as # letting fd_status rely on its default logic would miss the # fds above rlim_cur as it normally only checks up to that limit. - p = subprocess.Popen( + subprocess.Popen( [sys.executable, '-c', textwrap.dedent(""" import subprocess, sys - subprocess.Popen([sys.executable, {fd_status!r}] + + subprocess.Popen([sys.executable, %r] + [str(x) for x in range({max_fd})], close_fds=True).wait() - """.format(fd_status=fd_status, max_fd=max_fd_open+1))], - stdout=subprocess.PIPE, close_fds=False) + """.format(max_fd=max_fd_open+1))], + close_fds=False).wait() finally: resource.setrlimit(resource.RLIMIT_NOFILE, (rlim_cur, rlim_max)) + ''' % fd_status)], stdout=subprocess.PIPE) output, unused_stderr = p.communicate() - remaining_fds = set(map(int, output.strip().split(b','))) + output_lines = output.splitlines() + self.assertEqual(len(output_lines), 2, + msg="expected exactly two lines of output:\n%s" % output) + opened_fds = set(map(int, output_lines[0].strip().split(b','))) + remaining_fds = set(map(int, output_lines[1].strip().split(b','))) - self.assertFalse(remaining_fds & open_fds, + self.assertFalse(remaining_fds & opened_fds, msg="Some fds were left open.") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 03:30:35 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 16 Jun 2014 03:30:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_improve_note?= Message-ID: <3gsFNW6sqXz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/7aad195c7c64 changeset: 91191:7aad195c7c64 user: Benjamin Peterson date: Sun Jun 15 18:30:27 2014 -0700 summary: improve note files: Doc/library/os.rst | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1094,7 +1094,8 @@ .. note:: - For a higher-level version of this see :mod:`socket.socket.sendfile`. + For a higher-level wrapper of :func:`sendfile`, see + :mod:`socket.socket.sendfile`. .. versionadded:: 3.3 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 05:17:32 2014 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 16 Jun 2014 05:17:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_fix_a_BytesWar?= =?utf-8?q?ning_in_my_previous_commit=2E?= Message-ID: <3gsHlw5kJZz7LjV@mail.python.org> http://hg.python.org/cpython/rev/7d777aa53ea6 changeset: 91192:7d777aa53ea6 branch: 3.4 parent: 91189:3c57f7c4f13e user: Gregory P. Smith date: Sun Jun 15 20:16:01 2014 -0700 summary: fix a BytesWarning in my previous commit. files: Lib/test/test_subprocess.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1998,7 +1998,7 @@ output, unused_stderr = p.communicate() output_lines = output.splitlines() self.assertEqual(len(output_lines), 2, - msg="expected exactly two lines of output:\n%s" % output) + msg="expected exactly two lines of output:\n%r" % output) opened_fds = set(map(int, output_lines[0].strip().split(b','))) remaining_fds = set(map(int, output_lines[1].strip().split(b','))) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 05:17:34 2014 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 16 Jun 2014 05:17:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_fix_a_BytesWarning_in_my_previous_commit=2E?= Message-ID: <3gsHly0c5Gz7Lk4@mail.python.org> http://hg.python.org/cpython/rev/da783a1f1a62 changeset: 91193:da783a1f1a62 parent: 91191:7aad195c7c64 parent: 91192:7d777aa53ea6 user: Gregory P. Smith date: Sun Jun 15 20:17:23 2014 -0700 summary: fix a BytesWarning in my previous commit. files: Lib/test/test_subprocess.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1998,7 +1998,7 @@ output, unused_stderr = p.communicate() output_lines = output.splitlines() self.assertEqual(len(output_lines), 2, - msg="expected exactly two lines of output:\n%s" % output) + msg="expected exactly two lines of output:\n%r" % output) opened_fds = set(map(int, output_lines[0].strip().split(b','))) remaining_fds = set(map(int, output_lines[1].strip().split(b','))) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 05:52:12 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 16 Jun 2014 05:52:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_clarify_when_t?= =?utf-8?q?he_list_of_subdirectories_is_read_=28closes_=2313779=29?= Message-ID: <3gsJWw2Xq5z7LkH@mail.python.org> http://hg.python.org/cpython/rev/351c1422848f changeset: 91194:351c1422848f branch: 2.7 parent: 91183:4dca82f8d91d user: Benjamin Peterson date: Sun Jun 15 20:51:12 2014 -0700 summary: clarify when the list of subdirectories is read (closes #13779) files: Doc/library/os.rst | 8 +++++--- Lib/os.py | 12 +++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1604,9 +1604,11 @@ If optional argument *topdown* is ``True`` or not specified, the triple for a directory is generated before the triples for any of its subdirectories - (directories are generated top-down). If *topdown* is ``False``, the triple for a - directory is generated after the triples for all of its subdirectories - (directories are generated bottom-up). + (directories are generated top-down). If *topdown* is ``False``, the triple + for a directory is generated after the triples for all of its subdirectories + (directories are generated bottom-up). No matter the value of *topdown*, the + list of subdirectories is retrieved before the tuples for the directory and + its subdirectories are generated. When *topdown* is ``True``, the caller can modify the *dirnames* list in-place (perhaps using :keyword:`del` or slice assignment), and :func:`walk` will only diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -229,11 +229,12 @@ When topdown is true, the caller can modify the dirnames list in-place (e.g., via del or slice assignment), and walk will only recurse into the - subdirectories whose names remain in dirnames; this can be used to prune - the search, or to impose a specific order of visiting. Modifying - dirnames when topdown is false is ineffective, since the directories in - dirnames have already been generated by the time dirnames itself is - generated. + subdirectories whose names remain in dirnames; this can be used to prune the + search, or to impose a specific order of visiting. Modifying dirnames when + topdown is false is ineffective, since the directories in dirnames have + already been generated by the time dirnames itself is generated. No matter + the value of topdown, the list of subdirectories is retrieved before the + tuples for the directory and its subdirectories are generated. By default errors from the os.listdir() call are ignored. If optional arg 'onerror' is specified, it should be a function; it @@ -261,6 +262,7 @@ print "bytes in", len(files), "non-directory files" if 'CVS' in dirs: dirs.remove('CVS') # don't visit CVS directories + """ islink, join, isdir = path.islink, path.join, path.isdir -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 05:52:13 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 16 Jun 2014 05:52:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_clarify_when_t?= =?utf-8?q?he_list_of_subdirectories_is_read_=28closes_=2313779=29?= Message-ID: <3gsJWx4MC0z7LkH@mail.python.org> http://hg.python.org/cpython/rev/b10322b5ef0f changeset: 91195:b10322b5ef0f branch: 3.4 parent: 91192:7d777aa53ea6 user: Benjamin Peterson date: Sun Jun 15 20:51:12 2014 -0700 summary: clarify when the list of subdirectories is read (closes #13779) files: Doc/library/os.rst | 8 +++++--- Lib/os.py | 12 +++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2228,9 +2228,11 @@ If optional argument *topdown* is ``True`` or not specified, the triple for a directory is generated before the triples for any of its subdirectories - (directories are generated top-down). If *topdown* is ``False``, the triple for a - directory is generated after the triples for all of its subdirectories - (directories are generated bottom-up). + (directories are generated top-down). If *topdown* is ``False``, the triple + for a directory is generated after the triples for all of its subdirectories + (directories are generated bottom-up). No matter the value of *topdown*, the + list of subdirectories is retrieved before the tuples for the directory and + its subdirectories are generated. When *topdown* is ``True``, the caller can modify the *dirnames* list in-place (perhaps using :keyword:`del` or slice assignment), and :func:`walk` will only diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -312,11 +312,12 @@ When topdown is true, the caller can modify the dirnames list in-place (e.g., via del or slice assignment), and walk will only recurse into the - subdirectories whose names remain in dirnames; this can be used to prune - the search, or to impose a specific order of visiting. Modifying - dirnames when topdown is false is ineffective, since the directories in - dirnames have already been generated by the time dirnames itself is - generated. + subdirectories whose names remain in dirnames; this can be used to prune the + search, or to impose a specific order of visiting. Modifying dirnames when + topdown is false is ineffective, since the directories in dirnames have + already been generated by the time dirnames itself is generated. No matter + the value of topdown, the list of subdirectories is retrieved before the + tuples for the directory and its subdirectories are generated. By default errors from the os.listdir() call are ignored. If optional arg 'onerror' is specified, it should be a function; it @@ -344,6 +345,7 @@ print("bytes in", len(files), "non-directory files") if 'CVS' in dirs: dirs.remove('CVS') # don't visit CVS directories + """ islink, join, isdir = path.islink, path.join, path.isdir -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 05:52:14 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 16 Jun 2014 05:52:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMTM3Nzkp?= Message-ID: <3gsJWy6FPdz7LkN@mail.python.org> http://hg.python.org/cpython/rev/1411df211159 changeset: 91196:1411df211159 parent: 91193:da783a1f1a62 parent: 91195:b10322b5ef0f user: Benjamin Peterson date: Sun Jun 15 20:52:02 2014 -0700 summary: merge 3.4 (#13779) files: Doc/library/os.rst | 8 +++++--- Lib/os.py | 12 +++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2236,9 +2236,11 @@ If optional argument *topdown* is ``True`` or not specified, the triple for a directory is generated before the triples for any of its subdirectories - (directories are generated top-down). If *topdown* is ``False``, the triple for a - directory is generated after the triples for all of its subdirectories - (directories are generated bottom-up). + (directories are generated top-down). If *topdown* is ``False``, the triple + for a directory is generated after the triples for all of its subdirectories + (directories are generated bottom-up). No matter the value of *topdown*, the + list of subdirectories is retrieved before the tuples for the directory and + its subdirectories are generated. When *topdown* is ``True``, the caller can modify the *dirnames* list in-place (perhaps using :keyword:`del` or slice assignment), and :func:`walk` will only diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -312,11 +312,12 @@ When topdown is true, the caller can modify the dirnames list in-place (e.g., via del or slice assignment), and walk will only recurse into the - subdirectories whose names remain in dirnames; this can be used to prune - the search, or to impose a specific order of visiting. Modifying - dirnames when topdown is false is ineffective, since the directories in - dirnames have already been generated by the time dirnames itself is - generated. + subdirectories whose names remain in dirnames; this can be used to prune the + search, or to impose a specific order of visiting. Modifying dirnames when + topdown is false is ineffective, since the directories in dirnames have + already been generated by the time dirnames itself is generated. No matter + the value of topdown, the list of subdirectories is retrieved before the + tuples for the directory and its subdirectories are generated. By default errors from the os.listdir() call are ignored. If optional arg 'onerror' is specified, it should be a function; it @@ -344,6 +345,7 @@ print("bytes in", len(files), "non-directory files") if 'CVS' in dirs: dirs.remove('CVS') # don't visit CVS directories + """ islink, join, isdir = path.islink, path.join, path.isdir -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 08:41:08 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 08:41:08 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNjg2?= =?utf-8?q?=3A_idlelib/HyperParser=2Epy_-_Update_docstrings_and_comments_a?= =?utf-8?q?nd?= Message-ID: <3gsNGr14lnz7LjT@mail.python.org> http://hg.python.org/cpython/rev/5063df721985 changeset: 91197:5063df721985 branch: 2.7 parent: 91194:351c1422848f user: Terry Jan Reedy date: Mon Jun 16 02:33:18 2014 -0400 summary: Issue #21686: idlelib/HyperParser.py - Update docstrings and comments and replace \ line contiuation. Tested against nearly done test_hyperparser.py. files: Lib/idlelib/HyperParser.py | 164 ++++++++++++------------ 1 files changed, 84 insertions(+), 80 deletions(-) diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -1,11 +1,8 @@ -""" -HyperParser -=========== -This module defines the HyperParser class, which provides advanced parsing -abilities for the ParenMatch and other extensions. -The HyperParser uses PyParser. PyParser is intended mostly to give information -on the proper indentation of code. HyperParser gives some information on the -structure of code, used by extensions to help the user. +"""Provide advanced parsing abilities for the ParenMatch and other extensions. + +HyperParser uses PyParser. PyParser mostly gives information on the +proper indentation of code. HyperParser gives additional information on +the structure of code. """ import string @@ -15,9 +12,7 @@ class HyperParser: def __init__(self, editwin, index): - """Initialize the HyperParser to analyze the surroundings of the given - index. - """ + "To initialize, analyze the surroundings of the given index." self.editwin = editwin self.text = text = editwin.text @@ -33,9 +28,10 @@ startat = max(lno - context, 1) startatindex = repr(startat) + ".0" stopatindex = "%d.end" % lno - # We add the newline because PyParse requires a newline at end. - # We add a space so that index won't be at end of line, so that - # its status will be the same as the char before it, if should. + # We add the newline because PyParse requires a newline + # at end. We add a space so that index won't be at end + # of line, so that its status will be the same as the + # char before it, if should. parser.set_str(text.get(startatindex, stopatindex)+' \n') bod = parser.find_good_parse_start( editwin._build_char_in_string_func(startatindex)) @@ -49,122 +45,130 @@ else: startatindex = "1.0" stopatindex = "%d.end" % lno - # We add the newline because PyParse requires a newline at end. - # We add a space so that index won't be at end of line, so that - # its status will be the same as the char before it, if should. + # We add the newline because PyParse requires it. We add a + # space so that index won't be at end of line, so that its + # status will be the same as the char before it, if should. parser.set_str(text.get(startatindex, stopatindex)+' \n') parser.set_lo(0) - # We want what the parser has, except for the last newline and space. + # We want what the parser has, minus the last newline and space. self.rawtext = parser.str[:-2] - # As far as I can see, parser.str preserves the statement we are in, - # so that stopatindex can be used to synchronize the string with the - # text box indices. + # Parser.str apparently preserves the statement we are in, so + # that stopatindex can be used to synchronize the string with + # the text box indices. self.stopatindex = stopatindex self.bracketing = parser.get_last_stmt_bracketing() - # find which pairs of bracketing are openers. These always correspond - # to a character of rawtext. - self.isopener = [i>0 and self.bracketing[i][1] > self.bracketing[i-1][1] + # find which pairs of bracketing are openers. These always + # correspond to a character of rawtext. + self.isopener = [i>0 and self.bracketing[i][1] > + self.bracketing[i-1][1] for i in range(len(self.bracketing))] self.set_index(index) def set_index(self, index): - """Set the index to which the functions relate. Note that it must be - in the same statement. + """Set the index to which the functions relate. + + The index must be in the same statement. """ - indexinrawtext = \ - len(self.rawtext) - len(self.text.get(index, self.stopatindex)) + indexinrawtext = (len(self.rawtext) - + len(self.text.get(index, self.stopatindex))) if indexinrawtext < 0: - raise ValueError("The index given is before the analyzed statement") + raise ValueError("Index %s precedes the analyzed statement" + % index) self.indexinrawtext = indexinrawtext # find the rightmost bracket to which index belongs self.indexbracket = 0 - while self.indexbracket < len(self.bracketing)-1 and \ - self.bracketing[self.indexbracket+1][0] < self.indexinrawtext: + while (self.indexbracket < len(self.bracketing)-1 and + self.bracketing[self.indexbracket+1][0] < self.indexinrawtext): self.indexbracket += 1 - if self.indexbracket < len(self.bracketing)-1 and \ - self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and \ - not self.isopener[self.indexbracket+1]: + if (self.indexbracket < len(self.bracketing)-1 and + self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and + not self.isopener[self.indexbracket+1]): self.indexbracket += 1 def is_in_string(self): """Is the index given to the HyperParser is in a string?""" # The bracket to which we belong should be an opener. # If it's an opener, it has to have a character. - return self.isopener[self.indexbracket] and \ - self.rawtext[self.bracketing[self.indexbracket][0]] in ('"', "'") + return (self.isopener[self.indexbracket] and + self.rawtext[self.bracketing[self.indexbracket][0]] + in ('"', "'")) def is_in_code(self): """Is the index given to the HyperParser is in a normal code?""" - return not self.isopener[self.indexbracket] or \ - self.rawtext[self.bracketing[self.indexbracket][0]] not in \ - ('#', '"', "'") + return (not self.isopener[self.indexbracket] or + self.rawtext[self.bracketing[self.indexbracket][0]] + not in ('#', '"', "'")) def get_surrounding_brackets(self, openers='([{', mustclose=False): - """If the index given to the HyperParser is surrounded by a bracket - defined in openers (or at least has one before it), return the - indices of the opening bracket and the closing bracket (or the - end of line, whichever comes first). - If it is not surrounded by brackets, or the end of line comes before - the closing bracket and mustclose is True, returns None. + """Return bracket indexes or None. + + If the index given to the HyperParser is surrounded by a + bracket defined in openers (or at least has one before it), + return the indices of the opening bracket and the closing + bracket (or the end of line, whichever comes first). + + If it is not surrounded by brackets, or the end of line comes + before the closing bracket and mustclose is True, returns None. """ + bracketinglevel = self.bracketing[self.indexbracket][1] before = self.indexbracket - while not self.isopener[before] or \ - self.rawtext[self.bracketing[before][0]] not in openers or \ - self.bracketing[before][1] > bracketinglevel: + while (not self.isopener[before] or + self.rawtext[self.bracketing[before][0]] not in openers or + self.bracketing[before][1] > bracketinglevel): before -= 1 if before < 0: return None bracketinglevel = min(bracketinglevel, self.bracketing[before][1]) after = self.indexbracket + 1 - while after < len(self.bracketing) and \ - self.bracketing[after][1] >= bracketinglevel: + while (after < len(self.bracketing) and + self.bracketing[after][1] >= bracketinglevel): after += 1 beforeindex = self.text.index("%s-%dc" % (self.stopatindex, len(self.rawtext)-self.bracketing[before][0])) - if after >= len(self.bracketing) or \ - self.bracketing[after][0] > len(self.rawtext): + if (after >= len(self.bracketing) or + self.bracketing[after][0] > len(self.rawtext)): if mustclose: return None afterindex = self.stopatindex else: - # We are after a real char, so it is a ')' and we give the index - # before it. - afterindex = self.text.index("%s-%dc" % - (self.stopatindex, + # We are after a real char, so it is a ')' and we give the + # index before it. + afterindex = self.text.index( + "%s-%dc" % (self.stopatindex, len(self.rawtext)-(self.bracketing[after][0]-1))) return beforeindex, afterindex - # This string includes all chars that may be in a white space + # Ascii chars that may be in a white space _whitespace_chars = " \t\n\\" - # This string includes all chars that may be in an identifier + # Ascii chars that may be in an identifier _id_chars = string.ascii_letters + string.digits + "_" - # This string includes all chars that may be the first char of an identifier + # Ascii chars that may be the first char of an identifier _id_first_chars = string.ascii_letters + "_" - # Given a string and pos, return the number of chars in the identifier - # which ends at pos, or 0 if there is no such one. Saved words are not - # identifiers. + # Given a string and pos, return the number of chars in the + # identifier which ends at pos, or 0 if there is no such one. Saved + # words are not identifiers. def _eat_identifier(self, str, limit, pos): i = pos while i > limit and str[i-1] in self._id_chars: i -= 1 - if i < pos and (str[i] not in self._id_first_chars or \ - keyword.iskeyword(str[i:pos])): + if (i < pos and (str[i] not in self._id_first_chars or + keyword.iskeyword(str[i:pos]))): i = pos return pos - i def get_expression(self): - """Return a string with the Python expression which ends at the given - index, which is empty if there is no real one. + """Return a string with the Python expression which ends at the + given index, which is empty if there is no real one. """ if not self.is_in_code(): - raise ValueError("get_expression should only be called if index "\ - "is inside a code.") + raise ValueError("get_expression should only be called" + "if index is inside a code.") rawtext = self.rawtext bracketing = self.bracketing @@ -177,20 +181,20 @@ postdot_phase = True while 1: - # Eat whitespaces, comments, and if postdot_phase is False - one dot + # Eat whitespaces, comments, and if postdot_phase is False - a dot while 1: if pos>brck_limit and rawtext[pos-1] in self._whitespace_chars: # Eat a whitespace pos -= 1 - elif not postdot_phase and \ - pos > brck_limit and rawtext[pos-1] == '.': + elif (not postdot_phase and + pos > brck_limit and rawtext[pos-1] == '.'): # Eat a dot pos -= 1 postdot_phase = True - # The next line will fail if we are *inside* a comment, but we - # shouldn't be. - elif pos == brck_limit and brck_index > 0 and \ - rawtext[bracketing[brck_index-1][0]] == '#': + # The next line will fail if we are *inside* a comment, + # but we shouldn't be. + elif (pos == brck_limit and brck_index > 0 and + rawtext[bracketing[brck_index-1][0]] == '#'): # Eat a comment brck_index -= 2 brck_limit = bracketing[brck_index][0] @@ -200,8 +204,8 @@ break if not postdot_phase: - # We didn't find a dot, so the expression end at the last - # identifier pos. + # We didn't find a dot, so the expression end at the + # last identifier pos. break ret = self._eat_identifier(rawtext, brck_limit, pos) @@ -209,13 +213,13 @@ # There is an identifier to eat pos = pos - ret last_identifier_pos = pos - # Now, in order to continue the search, we must find a dot. + # Now, to continue the search, we must find a dot. postdot_phase = False # (the loop continues now) elif pos == brck_limit: - # We are at a bracketing limit. If it is a closing bracket, - # eat the bracket, otherwise, stop the search. + # We are at a bracketing limit. If it is a closing + # bracket, eat the bracket, otherwise, stop the search. level = bracketing[brck_index][1] while brck_index > 0 and bracketing[brck_index-1][1] > level: brck_index -= 1 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 08:41:09 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 08:41:09 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjg2?= =?utf-8?q?=3A_idlelib/HyperParser=2Epy_-_Update_docstrings_and_comments_a?= =?utf-8?q?nd?= Message-ID: <3gsNGs4576z7Ljk@mail.python.org> http://hg.python.org/cpython/rev/ddf15cf0db72 changeset: 91198:ddf15cf0db72 branch: 3.4 parent: 91195:b10322b5ef0f user: Terry Jan Reedy date: Mon Jun 16 02:33:35 2014 -0400 summary: Issue #21686: idlelib/HyperParser.py - Update docstrings and comments and replace \ line contiuation. Tested against nearly done test_hyperparser.py. files: Lib/idlelib/HyperParser.py | 164 ++++++++++++------------ 1 files changed, 84 insertions(+), 80 deletions(-) diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -1,11 +1,8 @@ -""" -HyperParser -=========== -This module defines the HyperParser class, which provides advanced parsing -abilities for the ParenMatch and other extensions. -The HyperParser uses PyParser. PyParser is intended mostly to give information -on the proper indentation of code. HyperParser gives some information on the -structure of code, used by extensions to help the user. +"""Provide advanced parsing abilities for the ParenMatch and other extensions. + +HyperParser uses PyParser. PyParser mostly gives information on the +proper indentation of code. HyperParser gives additional information on +the structure of code. """ import string @@ -15,9 +12,7 @@ class HyperParser: def __init__(self, editwin, index): - """Initialize the HyperParser to analyze the surroundings of the given - index. - """ + "To initialize, analyze the surroundings of the given index." self.editwin = editwin self.text = text = editwin.text @@ -33,9 +28,10 @@ startat = max(lno - context, 1) startatindex = repr(startat) + ".0" stopatindex = "%d.end" % lno - # We add the newline because PyParse requires a newline at end. - # We add a space so that index won't be at end of line, so that - # its status will be the same as the char before it, if should. + # We add the newline because PyParse requires a newline + # at end. We add a space so that index won't be at end + # of line, so that its status will be the same as the + # char before it, if should. parser.set_str(text.get(startatindex, stopatindex)+' \n') bod = parser.find_good_parse_start( editwin._build_char_in_string_func(startatindex)) @@ -49,122 +45,130 @@ else: startatindex = "1.0" stopatindex = "%d.end" % lno - # We add the newline because PyParse requires a newline at end. - # We add a space so that index won't be at end of line, so that - # its status will be the same as the char before it, if should. + # We add the newline because PyParse requires it. We add a + # space so that index won't be at end of line, so that its + # status will be the same as the char before it, if should. parser.set_str(text.get(startatindex, stopatindex)+' \n') parser.set_lo(0) - # We want what the parser has, except for the last newline and space. + # We want what the parser has, minus the last newline and space. self.rawtext = parser.str[:-2] - # As far as I can see, parser.str preserves the statement we are in, - # so that stopatindex can be used to synchronize the string with the - # text box indices. + # Parser.str apparently preserves the statement we are in, so + # that stopatindex can be used to synchronize the string with + # the text box indices. self.stopatindex = stopatindex self.bracketing = parser.get_last_stmt_bracketing() - # find which pairs of bracketing are openers. These always correspond - # to a character of rawtext. - self.isopener = [i>0 and self.bracketing[i][1] > self.bracketing[i-1][1] + # find which pairs of bracketing are openers. These always + # correspond to a character of rawtext. + self.isopener = [i>0 and self.bracketing[i][1] > + self.bracketing[i-1][1] for i in range(len(self.bracketing))] self.set_index(index) def set_index(self, index): - """Set the index to which the functions relate. Note that it must be - in the same statement. + """Set the index to which the functions relate. + + The index must be in the same statement. """ - indexinrawtext = \ - len(self.rawtext) - len(self.text.get(index, self.stopatindex)) + indexinrawtext = (len(self.rawtext) - + len(self.text.get(index, self.stopatindex))) if indexinrawtext < 0: - raise ValueError("The index given is before the analyzed statement") + raise ValueError("Index %s precedes the analyzed statement" + % index) self.indexinrawtext = indexinrawtext # find the rightmost bracket to which index belongs self.indexbracket = 0 - while self.indexbracket < len(self.bracketing)-1 and \ - self.bracketing[self.indexbracket+1][0] < self.indexinrawtext: + while (self.indexbracket < len(self.bracketing)-1 and + self.bracketing[self.indexbracket+1][0] < self.indexinrawtext): self.indexbracket += 1 - if self.indexbracket < len(self.bracketing)-1 and \ - self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and \ - not self.isopener[self.indexbracket+1]: + if (self.indexbracket < len(self.bracketing)-1 and + self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and + not self.isopener[self.indexbracket+1]): self.indexbracket += 1 def is_in_string(self): """Is the index given to the HyperParser is in a string?""" # The bracket to which we belong should be an opener. # If it's an opener, it has to have a character. - return self.isopener[self.indexbracket] and \ - self.rawtext[self.bracketing[self.indexbracket][0]] in ('"', "'") + return (self.isopener[self.indexbracket] and + self.rawtext[self.bracketing[self.indexbracket][0]] + in ('"', "'")) def is_in_code(self): """Is the index given to the HyperParser is in a normal code?""" - return not self.isopener[self.indexbracket] or \ - self.rawtext[self.bracketing[self.indexbracket][0]] not in \ - ('#', '"', "'") + return (not self.isopener[self.indexbracket] or + self.rawtext[self.bracketing[self.indexbracket][0]] + not in ('#', '"', "'")) def get_surrounding_brackets(self, openers='([{', mustclose=False): - """If the index given to the HyperParser is surrounded by a bracket - defined in openers (or at least has one before it), return the - indices of the opening bracket and the closing bracket (or the - end of line, whichever comes first). - If it is not surrounded by brackets, or the end of line comes before - the closing bracket and mustclose is True, returns None. + """Return bracket indexes or None. + + If the index given to the HyperParser is surrounded by a + bracket defined in openers (or at least has one before it), + return the indices of the opening bracket and the closing + bracket (or the end of line, whichever comes first). + + If it is not surrounded by brackets, or the end of line comes + before the closing bracket and mustclose is True, returns None. """ + bracketinglevel = self.bracketing[self.indexbracket][1] before = self.indexbracket - while not self.isopener[before] or \ - self.rawtext[self.bracketing[before][0]] not in openers or \ - self.bracketing[before][1] > bracketinglevel: + while (not self.isopener[before] or + self.rawtext[self.bracketing[before][0]] not in openers or + self.bracketing[before][1] > bracketinglevel): before -= 1 if before < 0: return None bracketinglevel = min(bracketinglevel, self.bracketing[before][1]) after = self.indexbracket + 1 - while after < len(self.bracketing) and \ - self.bracketing[after][1] >= bracketinglevel: + while (after < len(self.bracketing) and + self.bracketing[after][1] >= bracketinglevel): after += 1 beforeindex = self.text.index("%s-%dc" % (self.stopatindex, len(self.rawtext)-self.bracketing[before][0])) - if after >= len(self.bracketing) or \ - self.bracketing[after][0] > len(self.rawtext): + if (after >= len(self.bracketing) or + self.bracketing[after][0] > len(self.rawtext)): if mustclose: return None afterindex = self.stopatindex else: - # We are after a real char, so it is a ')' and we give the index - # before it. - afterindex = self.text.index("%s-%dc" % - (self.stopatindex, + # We are after a real char, so it is a ')' and we give the + # index before it. + afterindex = self.text.index( + "%s-%dc" % (self.stopatindex, len(self.rawtext)-(self.bracketing[after][0]-1))) return beforeindex, afterindex - # This string includes all chars that may be in a white space + # Ascii chars that may be in a white space _whitespace_chars = " \t\n\\" - # This string includes all chars that may be in an identifier + # Ascii chars that may be in an identifier _id_chars = string.ascii_letters + string.digits + "_" - # This string includes all chars that may be the first char of an identifier + # Ascii chars that may be the first char of an identifier _id_first_chars = string.ascii_letters + "_" - # Given a string and pos, return the number of chars in the identifier - # which ends at pos, or 0 if there is no such one. Saved words are not - # identifiers. + # Given a string and pos, return the number of chars in the + # identifier which ends at pos, or 0 if there is no such one. Saved + # words are not identifiers. def _eat_identifier(self, str, limit, pos): i = pos while i > limit and str[i-1] in self._id_chars: i -= 1 - if i < pos and (str[i] not in self._id_first_chars or \ - keyword.iskeyword(str[i:pos])): + if (i < pos and (str[i] not in self._id_first_chars or + keyword.iskeyword(str[i:pos]))): i = pos return pos - i def get_expression(self): - """Return a string with the Python expression which ends at the given - index, which is empty if there is no real one. + """Return a string with the Python expression which ends at the + given index, which is empty if there is no real one. """ if not self.is_in_code(): - raise ValueError("get_expression should only be called if index "\ - "is inside a code.") + raise ValueError("get_expression should only be called" + "if index is inside a code.") rawtext = self.rawtext bracketing = self.bracketing @@ -177,20 +181,20 @@ postdot_phase = True while 1: - # Eat whitespaces, comments, and if postdot_phase is False - one dot + # Eat whitespaces, comments, and if postdot_phase is False - a dot while 1: if pos>brck_limit and rawtext[pos-1] in self._whitespace_chars: # Eat a whitespace pos -= 1 - elif not postdot_phase and \ - pos > brck_limit and rawtext[pos-1] == '.': + elif (not postdot_phase and + pos > brck_limit and rawtext[pos-1] == '.'): # Eat a dot pos -= 1 postdot_phase = True - # The next line will fail if we are *inside* a comment, but we - # shouldn't be. - elif pos == brck_limit and brck_index > 0 and \ - rawtext[bracketing[brck_index-1][0]] == '#': + # The next line will fail if we are *inside* a comment, + # but we shouldn't be. + elif (pos == brck_limit and brck_index > 0 and + rawtext[bracketing[brck_index-1][0]] == '#'): # Eat a comment brck_index -= 2 brck_limit = bracketing[brck_index][0] @@ -200,8 +204,8 @@ break if not postdot_phase: - # We didn't find a dot, so the expression end at the last - # identifier pos. + # We didn't find a dot, so the expression end at the + # last identifier pos. break ret = self._eat_identifier(rawtext, brck_limit, pos) @@ -209,13 +213,13 @@ # There is an identifier to eat pos = pos - ret last_identifier_pos = pos - # Now, in order to continue the search, we must find a dot. + # Now, to continue the search, we must find a dot. postdot_phase = False # (the loop continues now) elif pos == brck_limit: - # We are at a bracketing limit. If it is a closing bracket, - # eat the bracket, otherwise, stop the search. + # We are at a bracketing limit. If it is a closing + # bracket, eat the bracket, otherwise, stop the search. level = bracketing[brck_index][1] while brck_index > 0 and bracketing[brck_index-1][1] > level: brck_index -= 1 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 08:41:10 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 08:41:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gsNGt74r9z7Lk1@mail.python.org> http://hg.python.org/cpython/rev/754995a0e20d changeset: 91199:754995a0e20d parent: 91196:1411df211159 parent: 91198:ddf15cf0db72 user: Terry Jan Reedy date: Mon Jun 16 02:33:56 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/HyperParser.py | 164 ++++++++++++------------ 1 files changed, 84 insertions(+), 80 deletions(-) diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -1,11 +1,8 @@ -""" -HyperParser -=========== -This module defines the HyperParser class, which provides advanced parsing -abilities for the ParenMatch and other extensions. -The HyperParser uses PyParser. PyParser is intended mostly to give information -on the proper indentation of code. HyperParser gives some information on the -structure of code, used by extensions to help the user. +"""Provide advanced parsing abilities for the ParenMatch and other extensions. + +HyperParser uses PyParser. PyParser mostly gives information on the +proper indentation of code. HyperParser gives additional information on +the structure of code. """ import string @@ -15,9 +12,7 @@ class HyperParser: def __init__(self, editwin, index): - """Initialize the HyperParser to analyze the surroundings of the given - index. - """ + "To initialize, analyze the surroundings of the given index." self.editwin = editwin self.text = text = editwin.text @@ -33,9 +28,10 @@ startat = max(lno - context, 1) startatindex = repr(startat) + ".0" stopatindex = "%d.end" % lno - # We add the newline because PyParse requires a newline at end. - # We add a space so that index won't be at end of line, so that - # its status will be the same as the char before it, if should. + # We add the newline because PyParse requires a newline + # at end. We add a space so that index won't be at end + # of line, so that its status will be the same as the + # char before it, if should. parser.set_str(text.get(startatindex, stopatindex)+' \n') bod = parser.find_good_parse_start( editwin._build_char_in_string_func(startatindex)) @@ -49,122 +45,130 @@ else: startatindex = "1.0" stopatindex = "%d.end" % lno - # We add the newline because PyParse requires a newline at end. - # We add a space so that index won't be at end of line, so that - # its status will be the same as the char before it, if should. + # We add the newline because PyParse requires it. We add a + # space so that index won't be at end of line, so that its + # status will be the same as the char before it, if should. parser.set_str(text.get(startatindex, stopatindex)+' \n') parser.set_lo(0) - # We want what the parser has, except for the last newline and space. + # We want what the parser has, minus the last newline and space. self.rawtext = parser.str[:-2] - # As far as I can see, parser.str preserves the statement we are in, - # so that stopatindex can be used to synchronize the string with the - # text box indices. + # Parser.str apparently preserves the statement we are in, so + # that stopatindex can be used to synchronize the string with + # the text box indices. self.stopatindex = stopatindex self.bracketing = parser.get_last_stmt_bracketing() - # find which pairs of bracketing are openers. These always correspond - # to a character of rawtext. - self.isopener = [i>0 and self.bracketing[i][1] > self.bracketing[i-1][1] + # find which pairs of bracketing are openers. These always + # correspond to a character of rawtext. + self.isopener = [i>0 and self.bracketing[i][1] > + self.bracketing[i-1][1] for i in range(len(self.bracketing))] self.set_index(index) def set_index(self, index): - """Set the index to which the functions relate. Note that it must be - in the same statement. + """Set the index to which the functions relate. + + The index must be in the same statement. """ - indexinrawtext = \ - len(self.rawtext) - len(self.text.get(index, self.stopatindex)) + indexinrawtext = (len(self.rawtext) - + len(self.text.get(index, self.stopatindex))) if indexinrawtext < 0: - raise ValueError("The index given is before the analyzed statement") + raise ValueError("Index %s precedes the analyzed statement" + % index) self.indexinrawtext = indexinrawtext # find the rightmost bracket to which index belongs self.indexbracket = 0 - while self.indexbracket < len(self.bracketing)-1 and \ - self.bracketing[self.indexbracket+1][0] < self.indexinrawtext: + while (self.indexbracket < len(self.bracketing)-1 and + self.bracketing[self.indexbracket+1][0] < self.indexinrawtext): self.indexbracket += 1 - if self.indexbracket < len(self.bracketing)-1 and \ - self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and \ - not self.isopener[self.indexbracket+1]: + if (self.indexbracket < len(self.bracketing)-1 and + self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and + not self.isopener[self.indexbracket+1]): self.indexbracket += 1 def is_in_string(self): """Is the index given to the HyperParser is in a string?""" # The bracket to which we belong should be an opener. # If it's an opener, it has to have a character. - return self.isopener[self.indexbracket] and \ - self.rawtext[self.bracketing[self.indexbracket][0]] in ('"', "'") + return (self.isopener[self.indexbracket] and + self.rawtext[self.bracketing[self.indexbracket][0]] + in ('"', "'")) def is_in_code(self): """Is the index given to the HyperParser is in a normal code?""" - return not self.isopener[self.indexbracket] or \ - self.rawtext[self.bracketing[self.indexbracket][0]] not in \ - ('#', '"', "'") + return (not self.isopener[self.indexbracket] or + self.rawtext[self.bracketing[self.indexbracket][0]] + not in ('#', '"', "'")) def get_surrounding_brackets(self, openers='([{', mustclose=False): - """If the index given to the HyperParser is surrounded by a bracket - defined in openers (or at least has one before it), return the - indices of the opening bracket and the closing bracket (or the - end of line, whichever comes first). - If it is not surrounded by brackets, or the end of line comes before - the closing bracket and mustclose is True, returns None. + """Return bracket indexes or None. + + If the index given to the HyperParser is surrounded by a + bracket defined in openers (or at least has one before it), + return the indices of the opening bracket and the closing + bracket (or the end of line, whichever comes first). + + If it is not surrounded by brackets, or the end of line comes + before the closing bracket and mustclose is True, returns None. """ + bracketinglevel = self.bracketing[self.indexbracket][1] before = self.indexbracket - while not self.isopener[before] or \ - self.rawtext[self.bracketing[before][0]] not in openers or \ - self.bracketing[before][1] > bracketinglevel: + while (not self.isopener[before] or + self.rawtext[self.bracketing[before][0]] not in openers or + self.bracketing[before][1] > bracketinglevel): before -= 1 if before < 0: return None bracketinglevel = min(bracketinglevel, self.bracketing[before][1]) after = self.indexbracket + 1 - while after < len(self.bracketing) and \ - self.bracketing[after][1] >= bracketinglevel: + while (after < len(self.bracketing) and + self.bracketing[after][1] >= bracketinglevel): after += 1 beforeindex = self.text.index("%s-%dc" % (self.stopatindex, len(self.rawtext)-self.bracketing[before][0])) - if after >= len(self.bracketing) or \ - self.bracketing[after][0] > len(self.rawtext): + if (after >= len(self.bracketing) or + self.bracketing[after][0] > len(self.rawtext)): if mustclose: return None afterindex = self.stopatindex else: - # We are after a real char, so it is a ')' and we give the index - # before it. - afterindex = self.text.index("%s-%dc" % - (self.stopatindex, + # We are after a real char, so it is a ')' and we give the + # index before it. + afterindex = self.text.index( + "%s-%dc" % (self.stopatindex, len(self.rawtext)-(self.bracketing[after][0]-1))) return beforeindex, afterindex - # This string includes all chars that may be in a white space + # Ascii chars that may be in a white space _whitespace_chars = " \t\n\\" - # This string includes all chars that may be in an identifier + # Ascii chars that may be in an identifier _id_chars = string.ascii_letters + string.digits + "_" - # This string includes all chars that may be the first char of an identifier + # Ascii chars that may be the first char of an identifier _id_first_chars = string.ascii_letters + "_" - # Given a string and pos, return the number of chars in the identifier - # which ends at pos, or 0 if there is no such one. Saved words are not - # identifiers. + # Given a string and pos, return the number of chars in the + # identifier which ends at pos, or 0 if there is no such one. Saved + # words are not identifiers. def _eat_identifier(self, str, limit, pos): i = pos while i > limit and str[i-1] in self._id_chars: i -= 1 - if i < pos and (str[i] not in self._id_first_chars or \ - keyword.iskeyword(str[i:pos])): + if (i < pos and (str[i] not in self._id_first_chars or + keyword.iskeyword(str[i:pos]))): i = pos return pos - i def get_expression(self): - """Return a string with the Python expression which ends at the given - index, which is empty if there is no real one. + """Return a string with the Python expression which ends at the + given index, which is empty if there is no real one. """ if not self.is_in_code(): - raise ValueError("get_expression should only be called if index "\ - "is inside a code.") + raise ValueError("get_expression should only be called" + "if index is inside a code.") rawtext = self.rawtext bracketing = self.bracketing @@ -177,20 +181,20 @@ postdot_phase = True while 1: - # Eat whitespaces, comments, and if postdot_phase is False - one dot + # Eat whitespaces, comments, and if postdot_phase is False - a dot while 1: if pos>brck_limit and rawtext[pos-1] in self._whitespace_chars: # Eat a whitespace pos -= 1 - elif not postdot_phase and \ - pos > brck_limit and rawtext[pos-1] == '.': + elif (not postdot_phase and + pos > brck_limit and rawtext[pos-1] == '.'): # Eat a dot pos -= 1 postdot_phase = True - # The next line will fail if we are *inside* a comment, but we - # shouldn't be. - elif pos == brck_limit and brck_index > 0 and \ - rawtext[bracketing[brck_index-1][0]] == '#': + # The next line will fail if we are *inside* a comment, + # but we shouldn't be. + elif (pos == brck_limit and brck_index > 0 and + rawtext[bracketing[brck_index-1][0]] == '#'): # Eat a comment brck_index -= 2 brck_limit = bracketing[brck_index][0] @@ -200,8 +204,8 @@ break if not postdot_phase: - # We didn't find a dot, so the expression end at the last - # identifier pos. + # We didn't find a dot, so the expression end at the + # last identifier pos. break ret = self._eat_identifier(rawtext, brck_limit, pos) @@ -209,13 +213,13 @@ # There is an identifier to eat pos = pos - ret last_identifier_pos = pos - # Now, in order to continue the search, we must find a dot. + # Now, to continue the search, we must find a dot. postdot_phase = False # (the loop continues now) elif pos == brck_limit: - # We are at a bracketing limit. If it is a closing bracket, - # eat the bracket, otherwise, stop the search. + # We are at a bracketing limit. If it is a closing + # bracket, eat the bracket, otherwise, stop the search. level = bracketing[brck_index][1] while brck_index > 0 and bracketing[brck_index-1][1] > level: brck_index -= 1 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 08:41:12 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 08:41:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_whitespace?= Message-ID: <3gsNGw1ZgWz7LjZ@mail.python.org> http://hg.python.org/cpython/rev/2d5a9565ea7b changeset: 91200:2d5a9565ea7b branch: 2.7 parent: 91197:5063df721985 user: Terry Jan Reedy date: Mon Jun 16 02:40:08 2014 -0400 summary: whitespace files: Lib/idlelib/HyperParser.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -112,7 +112,7 @@ If it is not surrounded by brackets, or the end of line comes before the closing bracket and mustclose is True, returns None. """ - + bracketinglevel = self.bracketing[self.indexbracket][1] before = self.indexbracket while (not self.isopener[before] or -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 08:41:13 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 08:41:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_whitespace?= Message-ID: <3gsNGx3JZQz7Lk8@mail.python.org> http://hg.python.org/cpython/rev/f741012f48a0 changeset: 91201:f741012f48a0 branch: 3.4 parent: 91198:ddf15cf0db72 user: Terry Jan Reedy date: Mon Jun 16 02:40:24 2014 -0400 summary: whitespace files: Lib/idlelib/HyperParser.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -112,7 +112,7 @@ If it is not surrounded by brackets, or the end of line comes before the closing bracket and mustclose is True, returns None. """ - + bracketinglevel = self.bracketing[self.indexbracket][1] before = self.indexbracket while (not self.isopener[before] or -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 08:41:14 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 08:41:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gsNGy4y3Zz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/7e268ae424a4 changeset: 91202:7e268ae424a4 parent: 91199:754995a0e20d parent: 91201:f741012f48a0 user: Terry Jan Reedy date: Mon Jun 16 02:40:39 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/HyperParser.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -112,7 +112,7 @@ If it is not surrounded by brackets, or the end of line comes before the closing bracket and mustclose is True, returns None. """ - + bracketinglevel = self.bracketing[self.indexbracket][1] before = self.indexbracket while (not self.isopener[before] or -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 09:06:28 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 09:06:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5MzYy?= =?utf-8?q?=3A_Tweek_len=28=29_doc_and_docstring_to_expand_the_indicated_r?= =?utf-8?q?ange_of?= Message-ID: <3gsNr46brcz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/95d487abbfd8 changeset: 91203:95d487abbfd8 branch: 2.7 parent: 91200:2d5a9565ea7b user: Terry Jan Reedy date: Mon Jun 16 03:05:30 2014 -0400 summary: Issue #19362: Tweek len() doc and docstring to expand the indicated range of arguments. Original patch by Gareth Rees. files: Doc/library/functions.rst | 3 ++- Python/bltinmodule.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -726,7 +726,8 @@ .. function:: len(s) Return the length (the number of items) of an object. The argument may be a - sequence (string, tuple or list) or a mapping (dictionary). + sequence (such as a string, bytes, tuple, list, or range) or a collection + (such as a dictionary, set, or frozen set). .. function:: list([iterable]) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1323,7 +1323,7 @@ PyDoc_STRVAR(len_doc, "len(object) -> integer\n\ \n\ -Return the number of items of a sequence or mapping."); +Return the number of items of a sequence or collection."); static PyObject * -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 09:06:30 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 09:06:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE5MzYy?= =?utf-8?q?=3A_Tweek_len=28=29_doc_and_docstring_to_expand_the_indicated_r?= =?utf-8?q?ange_of?= Message-ID: <3gsNr61Djgz7Ljl@mail.python.org> http://hg.python.org/cpython/rev/8fcbe41e1242 changeset: 91204:8fcbe41e1242 branch: 3.4 parent: 91201:f741012f48a0 user: Terry Jan Reedy date: Mon Jun 16 03:05:37 2014 -0400 summary: Issue #19362: Tweek len() doc and docstring to expand the indicated range of arguments. Original patch by Gareth Rees. files: Doc/library/functions.rst | 3 ++- Python/bltinmodule.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -742,7 +742,8 @@ .. function:: len(s) Return the length (the number of items) of an object. The argument may be a - sequence (string, tuple or list) or a mapping (dictionary). + sequence (such as a string, bytes, tuple, list, or range) or a collection + (such as a dictionary, set, or frozen set). .. _func-list: diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1327,7 +1327,7 @@ PyDoc_STRVAR(len_doc, "len(object)\n\ \n\ -Return the number of items of a sequence or mapping."); +Return the number of items of a sequence or collection."); static PyObject * -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 09:06:31 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 09:06:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gsNr73Hgjz7Ljd@mail.python.org> http://hg.python.org/cpython/rev/56fbb15565e2 changeset: 91205:56fbb15565e2 parent: 91202:7e268ae424a4 parent: 91204:8fcbe41e1242 user: Terry Jan Reedy date: Mon Jun 16 03:05:53 2014 -0400 summary: Merge with 3.4 files: Doc/library/functions.rst | 3 ++- Python/bltinmodule.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -742,7 +742,8 @@ .. function:: len(s) Return the length (the number of items) of an object. The argument may be a - sequence (string, tuple or list) or a mapping (dictionary). + sequence (such as a string, bytes, tuple, list, or range) or a collection + (such as a dictionary, set, or frozen set). .. _func-list: diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1327,7 +1327,7 @@ PyDoc_STRVAR(len_doc, "len(object)\n\ \n\ -Return the number of items of a sequence or mapping."); +Return the number of items of a sequence or collection."); static PyObject * -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 09:31:52 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 09:31:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNTU5?= =?utf-8?q?=3A_Add_alternative_=28historical=29_reason_for_OverflowError?= =?utf-8?q?=2E?= Message-ID: <3gsPPN403jz7LjY@mail.python.org> http://hg.python.org/cpython/rev/9ba324a20bad changeset: 91206:9ba324a20bad branch: 3.4 parent: 91204:8fcbe41e1242 user: Terry Jan Reedy date: Mon Jun 16 03:31:00 2014 -0400 summary: Issue #21559: Add alternative (historical) reason for OverflowError. files: Doc/library/exceptions.rst | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -274,9 +274,10 @@ Raised when the result of an arithmetic operation is too large to be represented. This cannot occur for integers (which would rather raise - :exc:`MemoryError` than give up). Because of the lack of standardization of - floating point exception handling in C, most floating point operations also - aren't checked. + :exc:`MemoryError` than give up). However, for historical reasons, + OverflowError is sometimes raised for integers that are outside a required + range. Because of the lack of standardization of floating point exception + handling in C, most floating point operations are not checked. .. exception:: ReferenceError -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 09:31:53 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 16 Jun 2014 09:31:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gsPPP5lPjz7LkF@mail.python.org> http://hg.python.org/cpython/rev/3ada8a7c50f7 changeset: 91207:3ada8a7c50f7 parent: 91205:56fbb15565e2 parent: 91206:9ba324a20bad user: Terry Jan Reedy date: Mon Jun 16 03:31:33 2014 -0400 summary: Merge with 3.4 files: Doc/library/exceptions.rst | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -274,9 +274,10 @@ Raised when the result of an arithmetic operation is too large to be represented. This cannot occur for integers (which would rather raise - :exc:`MemoryError` than give up). Because of the lack of standardization of - floating point exception handling in C, most floating point operations also - aren't checked. + :exc:`MemoryError` than give up). However, for historical reasons, + OverflowError is sometimes raised for integers that are outside a required + range. Because of the lack of standardization of floating point exception + handling in C, most floating point operations are not checked. .. exception:: ReferenceError -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Jun 16 11:02:28 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 16 Jun 2014 11:02:28 +0200 Subject: [Python-checkins] Daily reference leaks (13343e6696a4): sum=3025 Message-ID: results for 13343e6696a4 on branch "default" -------------------------------------------- test_collections leaked [0, 4, 0] references, sum=4 test_collections leaked [0, 2, 0] memory blocks, sum=2 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [0, 2, 0] references, sum=2 test_site leaked [0, 2, 0] memory blocks, sum=2 test_urllib2net leaked [0, 0, 1598] references, sum=1598 test_urllib2net leaked [0, 0, 1408] memory blocks, sum=1408 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogK_5nqP', '-x'] From python-checkins at python.org Mon Jun 16 11:50:08 2014 From: python-checkins at python.org (nick.coghlan) Date: Mon, 16 Jun 2014 11:50:08 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjY5?= =?utf-8?q?=3A_Special_case_print_=26_exec_syntax_errors?= Message-ID: <3gsSSw5j94z7LjW@mail.python.org> http://hg.python.org/cpython/rev/2b8cd2bc2745 changeset: 91208:2b8cd2bc2745 branch: 3.4 parent: 91206:9ba324a20bad user: Nick Coghlan date: Mon Jun 16 19:48:02 2014 +1000 summary: Issue #21669: Special case print & exec syntax errors files: Lib/test/test_grammar.py | 25 +++++ Misc/NEWS | 5 + Objects/exceptions.c | 135 +++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -390,6 +390,31 @@ check_syntax_error(self, "x + 1 = 1") check_syntax_error(self, "a + 1 = b + 2") + # Check the heuristic for print & exec covers significant cases + # As well as placing some limits on false positives + def test_former_statements_refer_to_builtins(self): + keywords = "print", "exec" + # Cases where we want the custom error + cases = [ + "{} foo", + "{} {{1:foo}}", + "if 1: {} foo", + "if 1: {} {{1:foo}}", + "if 1:\n {} foo", + "if 1:\n {} {{1:foo}}", + ] + for keyword in keywords: + custom_msg = "call to '{}'".format(keyword) + for case in cases: + source = case.format(keyword) + with self.subTest(source=source): + with self.assertRaisesRegex(SyntaxError, custom_msg): + exec(source) + source = source.replace("foo", "(foo.)") + with self.subTest(source=source): + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(source) + def test_del_stmt(self): # 'del' exprlist abc = [1,2,3] diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,11 @@ Core and Builtins ----------------- +- Issue #21669: With the aid of heuristics in SyntaxError.__init__, the + parser now attempts to generate more meaningful (or at least more search + engine friendly) error messages when "exec" and "print" are used as + statements. + - Issue #21642: If the conditional if-else expression, allow an integer written with no space between itself and the ``else`` keyword (e.g. ``True if 42else False``) to be valid syntax. diff --git a/Objects/exceptions.c b/Objects/exceptions.c --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1254,6 +1254,9 @@ * SyntaxError extends Exception */ +/* Helper function to customise error message for some syntax errors */ +static int _report_missing_parentheses(PySyntaxErrorObject *self); + static int SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds) { @@ -1298,6 +1301,13 @@ Py_INCREF(self->text); Py_DECREF(info); + + /* Issue #21669: Custom error for 'print' & 'exec' as statements */ + if (self->text && PyUnicode_Check(self->text)) { + if (_report_missing_parentheses(self) < 0) { + return -1; + } + } } return 0; } @@ -2783,3 +2793,128 @@ PyErr_Restore(new_exc, new_val, new_tb); return new_val; } + + +/* To help with migration from Python 2, SyntaxError.__init__ applies some + * heuristics to try to report a more meaningful exception when print and + * exec are used like statements. + * + * The heuristics are currently expected to detect the following cases: + * - top level statement + * - statement in a nested suite + * - trailing section of a one line complex statement + * + * They're currently known not to trigger: + * - after a semi-colon + * + * The error message can be a bit odd in cases where the "arguments" are + * completely illegal syntactically, but that isn't worth the hassle of + * fixing. + * + * We also can't do anything about cases that are legal Python 3 syntax + * but mean something entirely different from what they did in Python 2 + * (omitting the arguments entirely, printing items preceded by a unary plus + * or minus, using the stream redirection syntax). + */ + +static int +_check_for_legacy_statements(PySyntaxErrorObject *self, Py_ssize_t start) +{ + /* Return values: + * -1: an error occurred + * 0: nothing happened + * 1: the check triggered & the error message was changed + */ + static PyObject *print_prefix = NULL; + static PyObject *exec_prefix = NULL; + Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text); + int kind = PyUnicode_KIND(self->text); + void *data = PyUnicode_DATA(self->text); + + /* Ignore leading whitespace */ + while (start < text_len) { + Py_UCS4 ch = PyUnicode_READ(kind, data, start); + if (!Py_UNICODE_ISSPACE(ch)) + break; + start++; + } + /* Checking against an empty or whitespace-only part of the string */ + if (start == text_len) { + return 0; + } + + /* Check for legacy print statements */ + if (print_prefix == NULL) { + print_prefix = PyUnicode_InternFromString("print "); + if (print_prefix == NULL) { + return -1; + } + } + if (PyUnicode_Tailmatch(self->text, print_prefix, + start, text_len, -1)) { + Py_CLEAR(self->msg); + self->msg = PyUnicode_FromString( + "Missing parentheses in call to 'print'"); + return 1; + } + + /* Check for legacy exec statements */ + if (exec_prefix == NULL) { + exec_prefix = PyUnicode_InternFromString("exec "); + if (exec_prefix == NULL) { + return -1; + } + } + if (PyUnicode_Tailmatch(self->text, exec_prefix, + start, text_len, -1)) { + Py_CLEAR(self->msg); + self->msg = PyUnicode_FromString( + "Missing parentheses in call to 'exec'"); + return 1; + } + /* Fall back to the default error message */ + return 0; +} + +static int +_report_missing_parentheses(PySyntaxErrorObject *self) +{ + Py_UCS4 left_paren = 40; + Py_ssize_t left_paren_index; + Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text); + int legacy_check_result = 0; + + /* Skip entirely if there is an opening parenthesis */ + left_paren_index = PyUnicode_FindChar(self->text, left_paren, + 0, text_len, 1); + if (left_paren_index < -1) { + return -1; + } + if (left_paren_index != -1) { + /* Use default error message for any line with an opening paren */ + return 0; + } + /* Handle the simple statement case */ + legacy_check_result = _check_for_legacy_statements(self, 0); + if (legacy_check_result < 0) { + return -1; + + } + if (legacy_check_result == 0) { + /* Handle the one-line complex statement case */ + Py_UCS4 colon = 58; + Py_ssize_t colon_index; + colon_index = PyUnicode_FindChar(self->text, colon, + 0, text_len, 1); + if (colon_index < -1) { + return -1; + } + if (colon_index >= 0 && colon_index < text_len) { + /* Check again, starting from just after the colon */ + if (_check_for_legacy_statements(self, colon_index+1) < 0) { + return -1; + } + } + } + return 0; +} -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 11:50:10 2014 From: python-checkins at python.org (nick.coghlan) Date: Mon, 16 Jun 2014 11:50:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_issue_=2321669_from_3=2E4?= Message-ID: <3gsSSy1Ptdz7Ljk@mail.python.org> http://hg.python.org/cpython/rev/36057f357537 changeset: 91209:36057f357537 parent: 91207:3ada8a7c50f7 parent: 91208:2b8cd2bc2745 user: Nick Coghlan date: Mon Jun 16 19:49:12 2014 +1000 summary: Merge issue #21669 from 3.4 files: Lib/test/test_grammar.py | 25 +++++ Misc/NEWS | 5 + Objects/exceptions.c | 135 +++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -390,6 +390,31 @@ check_syntax_error(self, "x + 1 = 1") check_syntax_error(self, "a + 1 = b + 2") + # Check the heuristic for print & exec covers significant cases + # As well as placing some limits on false positives + def test_former_statements_refer_to_builtins(self): + keywords = "print", "exec" + # Cases where we want the custom error + cases = [ + "{} foo", + "{} {{1:foo}}", + "if 1: {} foo", + "if 1: {} {{1:foo}}", + "if 1:\n {} foo", + "if 1:\n {} {{1:foo}}", + ] + for keyword in keywords: + custom_msg = "call to '{}'".format(keyword) + for case in cases: + source = case.format(keyword) + with self.subTest(source=source): + with self.assertRaisesRegex(SyntaxError, custom_msg): + exec(source) + source = source.replace("foo", "(foo.)") + with self.subTest(source=source): + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(source) + def test_del_stmt(self): # 'del' exprlist abc = [1,2,3] diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,11 @@ Core and Builtins ----------------- +- Issue #21669: With the aid of heuristics in SyntaxError.__init__, the + parser now attempts to generate more meaningful (or at least more search + engine friendly) error messages when "exec" and "print" are used as + statements. + - Issue #21642: If the conditional if-else expression, allow an integer written with no space between itself and the ``else`` keyword (e.g. ``True if 42else False``) to be valid syntax. diff --git a/Objects/exceptions.c b/Objects/exceptions.c --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1254,6 +1254,9 @@ * SyntaxError extends Exception */ +/* Helper function to customise error message for some syntax errors */ +static int _report_missing_parentheses(PySyntaxErrorObject *self); + static int SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds) { @@ -1298,6 +1301,13 @@ Py_INCREF(self->text); Py_DECREF(info); + + /* Issue #21669: Custom error for 'print' & 'exec' as statements */ + if (self->text && PyUnicode_Check(self->text)) { + if (_report_missing_parentheses(self) < 0) { + return -1; + } + } } return 0; } @@ -2783,3 +2793,128 @@ PyErr_Restore(new_exc, new_val, new_tb); return new_val; } + + +/* To help with migration from Python 2, SyntaxError.__init__ applies some + * heuristics to try to report a more meaningful exception when print and + * exec are used like statements. + * + * The heuristics are currently expected to detect the following cases: + * - top level statement + * - statement in a nested suite + * - trailing section of a one line complex statement + * + * They're currently known not to trigger: + * - after a semi-colon + * + * The error message can be a bit odd in cases where the "arguments" are + * completely illegal syntactically, but that isn't worth the hassle of + * fixing. + * + * We also can't do anything about cases that are legal Python 3 syntax + * but mean something entirely different from what they did in Python 2 + * (omitting the arguments entirely, printing items preceded by a unary plus + * or minus, using the stream redirection syntax). + */ + +static int +_check_for_legacy_statements(PySyntaxErrorObject *self, Py_ssize_t start) +{ + /* Return values: + * -1: an error occurred + * 0: nothing happened + * 1: the check triggered & the error message was changed + */ + static PyObject *print_prefix = NULL; + static PyObject *exec_prefix = NULL; + Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text); + int kind = PyUnicode_KIND(self->text); + void *data = PyUnicode_DATA(self->text); + + /* Ignore leading whitespace */ + while (start < text_len) { + Py_UCS4 ch = PyUnicode_READ(kind, data, start); + if (!Py_UNICODE_ISSPACE(ch)) + break; + start++; + } + /* Checking against an empty or whitespace-only part of the string */ + if (start == text_len) { + return 0; + } + + /* Check for legacy print statements */ + if (print_prefix == NULL) { + print_prefix = PyUnicode_InternFromString("print "); + if (print_prefix == NULL) { + return -1; + } + } + if (PyUnicode_Tailmatch(self->text, print_prefix, + start, text_len, -1)) { + Py_CLEAR(self->msg); + self->msg = PyUnicode_FromString( + "Missing parentheses in call to 'print'"); + return 1; + } + + /* Check for legacy exec statements */ + if (exec_prefix == NULL) { + exec_prefix = PyUnicode_InternFromString("exec "); + if (exec_prefix == NULL) { + return -1; + } + } + if (PyUnicode_Tailmatch(self->text, exec_prefix, + start, text_len, -1)) { + Py_CLEAR(self->msg); + self->msg = PyUnicode_FromString( + "Missing parentheses in call to 'exec'"); + return 1; + } + /* Fall back to the default error message */ + return 0; +} + +static int +_report_missing_parentheses(PySyntaxErrorObject *self) +{ + Py_UCS4 left_paren = 40; + Py_ssize_t left_paren_index; + Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text); + int legacy_check_result = 0; + + /* Skip entirely if there is an opening parenthesis */ + left_paren_index = PyUnicode_FindChar(self->text, left_paren, + 0, text_len, 1); + if (left_paren_index < -1) { + return -1; + } + if (left_paren_index != -1) { + /* Use default error message for any line with an opening paren */ + return 0; + } + /* Handle the simple statement case */ + legacy_check_result = _check_for_legacy_statements(self, 0); + if (legacy_check_result < 0) { + return -1; + + } + if (legacy_check_result == 0) { + /* Handle the one-line complex statement case */ + Py_UCS4 colon = 58; + Py_ssize_t colon_index; + colon_index = PyUnicode_FindChar(self->text, colon, + 0, text_len, 1); + if (colon_index < -1) { + return -1; + } + if (colon_index >= 0 && colon_index < text_len) { + /* Check again, starting from just after the colon */ + if (_check_for_legacy_statements(self, colon_index+1) < 0) { + return -1; + } + } + } + return 0; +} -- Repository URL: http://hg.python.org/cpython From root at python.org Mon Jun 16 13:50:23 2014 From: root at python.org (Cron Daemon) Date: Mon, 16 Jun 2014 13:50:23 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From python-checkins at python.org Mon Jun 16 14:11:57 2014 From: python-checkins at python.org (jesus.cea) Date: Mon, 16 Jun 2014 14:11:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogQ2xvc2VzICMyMTc1?= =?utf-8?q?9=3A_URL_Typo_in_Documentation_FAQ?= Message-ID: <3gsWcY24mBz7LjN@mail.python.org> http://hg.python.org/cpython/rev/f254ceec0d45 changeset: 91210:f254ceec0d45 branch: 2.7 parent: 91203:95d487abbfd8 user: Jesus Cea date: Mon Jun 16 14:11:14 2014 +0200 summary: Closes #21759: URL Typo in Documentation FAQ files: Doc/faq/programming.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -149,7 +149,7 @@ `_. Guido van Rossum has written up an anecdote related to optimization at -http://www.python.org/doc/essays/list2str.html. +http://www.python.org/doc/essays/list2str. One thing to notice is that function and (especially) method calls are rather expensive; if you have designed a purely OO interface with lots of tiny -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 16:00:18 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 16 Jun 2014 16:00:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321205=3A_Add_a_ne?= =?utf-8?b?dyBgYF9fcXVhbG5hbWVfX2BgIGF0dHJpYnV0ZSB0byBnZW5lcmF0b3IsIHRo?= =?utf-8?q?e_qualified?= Message-ID: <3gsZ1Z46g1z7Ljg@mail.python.org> http://hg.python.org/cpython/rev/aa85e8d729ae changeset: 91211:aa85e8d729ae parent: 91209:36057f357537 user: Victor Stinner date: Mon Jun 16 15:59:28 2014 +0200 summary: Issue #21205: Add a new ``__qualname__`` attribute to generator, the qualified name, and use it in the representation of a generator (``repr(gen)``). The default name of the generator (``__name__`` attribute) is now get from the function instead of the code. Use ``gen.gi_code.co_name`` to get the name of the code. files: Doc/library/inspect.rst | 14 +++ Doc/whatsnew/3.5.rst | 6 + Include/genobject.h | 8 ++ Lib/test/test_generators.py | 39 ++++++++++ Misc/NEWS | 6 + Objects/genobject.c | 90 ++++++++++++++++++++---- Python/ceval.c | 30 ++++++-- 7 files changed, 170 insertions(+), 23 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -159,6 +159,16 @@ | | | arguments and local | | | | variables | +-----------+-----------------+---------------------------+ +| generator | __name__ | name | ++-----------+-----------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-----------------+---------------------------+ +| | gi_frame | frame | ++-----------+-----------------+---------------------------+ +| | gi_running | is the generator running? | ++-----------+-----------------+---------------------------+ +| | gi_code | code | ++-----------+-----------------+---------------------------+ | builtin | __doc__ | documentation string | +-----------+-----------------+---------------------------+ | | __name__ | original name of this | @@ -169,6 +179,10 @@ | | | ``None`` | +-----------+-----------------+---------------------------+ +.. versionchanged:: 3.5 + + Add ``__qualname__`` attribute to generators. + .. function:: getmembers(object[, predicate]) diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -304,6 +304,12 @@ or :exc:`ssl.SSLWantWriteError` on a non-blocking socket if the operation would block. Previously, it would return 0. See :issue:`20951`. +* The ``__name__`` attribute of generator is now set from the function name, + instead of being set from the code name. Use ``gen.gi_code.co_name`` to + retrieve the code name. Generators also have a new ``__qualname__`` + attribute, the qualified name, which is now used for the representation + of a generator (``repr(gen)``). See :issue:`21205`. + Changes in the C API -------------------- diff --git a/Include/genobject.h b/Include/genobject.h --- a/Include/genobject.h +++ b/Include/genobject.h @@ -25,6 +25,12 @@ /* List of weak reference. */ PyObject *gi_weakreflist; + + /* Name of the generator. */ + PyObject *gi_name; + + /* Qualified name of the generator. */ + PyObject *gi_qualname; } PyGenObject; PyAPI_DATA(PyTypeObject) PyGen_Type; @@ -33,6 +39,8 @@ #define PyGen_CheckExact(op) (Py_TYPE(op) == &PyGen_Type) PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *); +PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *, + PyObject *name, PyObject *qualname); PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *); PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); PyObject *_PyGen_Send(PyGenObject *, PyObject *); diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -50,6 +50,45 @@ self.assertEqual(gc.garbage, old_garbage) +class GeneratorTest(unittest.TestCase): + + def test_name(self): + def func(): + yield 1 + + # check generator names + gen = func() + self.assertEqual(gen.__name__, "func") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name..func") + + # modify generator names + gen.__name__ = "name" + gen.__qualname__ = "qualname" + self.assertEqual(gen.__name__, "name") + self.assertEqual(gen.__qualname__, "qualname") + + # generator names must be a string and cannot be deleted + self.assertRaises(TypeError, setattr, gen, '__name__', 123) + self.assertRaises(TypeError, setattr, gen, '__qualname__', 123) + self.assertRaises(TypeError, delattr, gen, '__name__') + self.assertRaises(TypeError, delattr, gen, '__qualname__') + + # modify names of the function creating the generator + func.__qualname__ = "func_qualname" + func.__name__ = "func_name" + gen = func() + self.assertEqual(gen.__name__, "func_name") + self.assertEqual(gen.__qualname__, "func_qualname") + + # unnamed generator + gen = (x for x in range(10)) + self.assertEqual(gen.__name__, + "") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name..") + + tutorial_tests = """ Let's try a simple generator: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,12 @@ Core and Builtins ----------------- +- Issue #21205: Add a new ``__qualname__`` attribute to generator, the + qualified name, and use it in the representation of a generator + (``repr(gen)``). The default name of the generator (``__name__`` attribute) + is now get from the function instead of the code. Use ``gen.gi_code.co_name`` + to get the name of the code. + - Issue #21669: With the aid of heuristics in SyntaxError.__init__, the parser now attempts to generate more meaningful (or at least more search engine friendly) error messages when "exec" and "print" are used as diff --git a/Objects/genobject.c b/Objects/genobject.c --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -12,6 +12,8 @@ { Py_VISIT((PyObject *)gen->gi_frame); Py_VISIT(gen->gi_code); + Py_VISIT(gen->gi_name); + Py_VISIT(gen->gi_qualname); return 0; } @@ -58,6 +60,8 @@ _PyObject_GC_UNTRACK(self); Py_CLEAR(gen->gi_frame); Py_CLEAR(gen->gi_code); + Py_CLEAR(gen->gi_name); + Py_CLEAR(gen->gi_qualname); PyObject_GC_Del(gen); } @@ -418,33 +422,73 @@ gen_repr(PyGenObject *gen) { return PyUnicode_FromFormat("", - ((PyCodeObject *)gen->gi_code)->co_name, - gen); + gen->gi_qualname, gen); } +static PyObject * +gen_get_name(PyGenObject *op) +{ + Py_INCREF(op->gi_name); + return op->gi_name; +} + +static int +gen_set_name(PyGenObject *op, PyObject *value) +{ + PyObject *tmp; + + /* Not legal to del gen.gi_name or to set it to anything + * other than a string object. */ + if (value == NULL || !PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "__name__ must be set to a string object"); + return -1; + } + tmp = op->gi_name; + Py_INCREF(value); + op->gi_name = value; + Py_DECREF(tmp); + return 0; +} static PyObject * -gen_get_name(PyGenObject *gen) +gen_get_qualname(PyGenObject *op) { - PyObject *name = ((PyCodeObject *)gen->gi_code)->co_name; - Py_INCREF(name); - return name; + Py_INCREF(op->gi_qualname); + return op->gi_qualname; } +static int +gen_set_qualname(PyGenObject *op, PyObject *value) +{ + PyObject *tmp; -PyDoc_STRVAR(gen__name__doc__, -"Return the name of the generator's associated code object."); + /* Not legal to del gen.__qualname__ or to set it to anything + * other than a string object. */ + if (value == NULL || !PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "__qualname__ must be set to a string object"); + return -1; + } + tmp = op->gi_qualname; + Py_INCREF(value); + op->gi_qualname = value; + Py_DECREF(tmp); + return 0; +} static PyGetSetDef gen_getsetlist[] = { - {"__name__", (getter)gen_get_name, NULL, gen__name__doc__}, - {NULL} + {"__name__", (getter)gen_get_name, (setter)gen_set_name, + PyDoc_STR("name of the generator")}, + {"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname, + PyDoc_STR("qualified name of the generator")}, + {NULL} /* Sentinel */ }; - static PyMemberDef gen_memberlist[] = { - {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, - {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, - {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, + {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, + {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, + {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, {NULL} /* Sentinel */ }; @@ -510,7 +554,7 @@ }; PyObject * -PyGen_New(PyFrameObject *f) +PyGen_NewWithQualName(PyFrameObject *f, PyObject *name, PyObject *qualname) { PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type); if (gen == NULL) { @@ -523,10 +567,26 @@ gen->gi_code = (PyObject *)(f->f_code); gen->gi_running = 0; gen->gi_weakreflist = NULL; + if (name != NULL) + gen->gi_name = name; + else + gen->gi_name = ((PyCodeObject *)gen->gi_code)->co_name; + Py_INCREF(gen->gi_name); + if (qualname != NULL) + gen->gi_qualname = qualname; + else + gen->gi_qualname = gen->gi_name; + Py_INCREF(gen->gi_qualname); _PyObject_GC_TRACK(gen); return (PyObject *)gen; } +PyObject * +PyGen_New(PyFrameObject *f) +{ + return PyGen_NewWithQualName(f, NULL, NULL); +} + int PyGen_NeedsFinalizing(PyGenObject *gen) { diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3401,10 +3401,11 @@ PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust the test in the if statements in Misc/gdbinit (pystack and pystackv). */ -PyObject * -PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, +static PyObject * +_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname) { PyCodeObject* co = (PyCodeObject*)_co; PyFrameObject *f; @@ -3596,7 +3597,7 @@ /* Create a new generator that owns the ready to run frame * and return that as the value. */ - return PyGen_New(f); + return PyGen_NewWithQualName(f, name, qualname); } retval = PyEval_EvalFrameEx(f,0); @@ -3615,6 +3616,16 @@ return retval; } +PyObject * +PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, + PyObject **args, int argcount, PyObject **kws, int kwcount, + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) +{ + return _PyEval_EvalCodeWithName(_co, globals, locals, + args, argcount, kws, kwcount, + defs, defcount, kwdefs, closure, + NULL, NULL); +} static PyObject * special_lookup(PyObject *o, _Py_Identifier *id) @@ -4313,6 +4324,8 @@ PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func); + PyObject *name = ((PyFunctionObject *)func) -> func_name; + PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname; PyObject **d = NULL; int nd = 0; @@ -4355,10 +4368,11 @@ d = &PyTuple_GET_ITEM(argdefs, 0); nd = Py_SIZE(argdefs); } - return PyEval_EvalCodeEx((PyObject*)co, globals, - (PyObject *)NULL, (*pp_stack)-n, na, - (*pp_stack)-2*nk, nk, d, nd, kwdefs, - PyFunction_GET_CLOSURE(func)); + return _PyEval_EvalCodeWithName((PyObject*)co, globals, + (PyObject *)NULL, (*pp_stack)-n, na, + (*pp_stack)-2*nk, nk, d, nd, kwdefs, + PyFunction_GET_CLOSURE(func), + name, qualname); } static PyObject * -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 16:24:13 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 16 Jun 2014 16:24:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321205=3A_Fix_unit?= =?utf-8?q?_tests?= Message-ID: <3gsZY90jqlz7LjT@mail.python.org> http://hg.python.org/cpython/rev/901a8265511a changeset: 91212:901a8265511a user: Victor Stinner date: Mon Jun 16 16:21:57 2014 +0200 summary: Issue #21205: Fix unit tests files: Lib/test/test_asyncio/test_tasks.py | 5 +---- Lib/test/test_sys.py | 2 +- 2 files changed, 2 insertions(+), 5 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 @@ -154,10 +154,7 @@ t = MyTask(gen, loop=self.loop) filename = gen.gi_code.co_filename lineno = gen.gi_frame.f_lineno - # FIXME: check for the name "coro" instead of "notmuch" because - # @asyncio.coroutine drops the name of the wrapped function: - # http://bugs.python.org/issue21205 - self.assertEqual(repr(t), 'T[]()' % (filename, lineno)) + self.assertEqual(repr(t), 'T[]()' % (filename, lineno)) def test_task_basics(self): @asyncio.coroutine 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 @@ -885,7 +885,7 @@ check(bar, size('PP')) # generator def get_gen(): yield 1 - check(get_gen(), size('Pb2P')) + check(get_gen(), size('Pb2PPP')) # iterator check(iter('abc'), size('lP')) # callable-iterator -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 16:25:36 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 16 Jun 2014 16:25:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321205=3A_Complete?= =?utf-8?q?_the_=22versionchanged=22_note_in_inspect_documentation?= Message-ID: <3gsZZm1xCvz7LjT@mail.python.org> http://hg.python.org/cpython/rev/28b3b8b22654 changeset: 91213:28b3b8b22654 user: Victor Stinner date: Mon Jun 16 16:25:22 2014 +0200 summary: Issue #21205: Complete the "versionchanged" note in inspect documentation files: Doc/library/inspect.rst | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -181,7 +181,9 @@ .. versionchanged:: 3.5 - Add ``__qualname__`` attribute to generators. + Add ``__qualname__`` attribute to generators. The ``__name__`` attribute of + generators is now set from the function name, instead of the code name, and + it can now be modified. .. function:: getmembers(object[, predicate]) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 17:12:50 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 16 Jun 2014 17:12:50 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Sync_asyncio_w?= =?utf-8?q?ith_Tulip=3A_Fix_test=5Ftasks_for_Python_3=2E5?= Message-ID: <3gsbdG6cfsz7LjN@mail.python.org> http://hg.python.org/cpython/rev/36ef32003a81 changeset: 91214:36ef32003a81 branch: 3.4 parent: 91208:2b8cd2bc2745 user: Victor Stinner date: Mon Jun 16 17:11:05 2014 +0200 summary: Sync asyncio with Tulip: Fix test_tasks for Python 3.5 On Python 3.5, generator now gets their name from the function, no more from the code. So we get the expected "notmuch" name instead of the generic "coro" name. files: Lib/test/test_asyncio/test_tasks.py | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 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 @@ -2,6 +2,7 @@ import gc import os.path +import sys import types import unittest import weakref @@ -154,10 +155,13 @@ t = MyTask(gen, loop=self.loop) filename = gen.gi_code.co_filename lineno = gen.gi_frame.f_lineno - # FIXME: check for the name "coro" instead of "notmuch" because - # @asyncio.coroutine drops the name of the wrapped function: - # http://bugs.python.org/issue21205 - self.assertEqual(repr(t), 'T[]()' % (filename, lineno)) + if sys.version_info >= (3, 5): + name = 'notmuch' + else: + # On Python < 3.5, generators inherit the name of the code, not of + # the function. See: http://bugs.python.org/issue21205 + name = 'coro' + self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (name, filename, lineno)) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 17:12:52 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 16 Jun 2014 17:12:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Sync_asyncio_with_Tulip=3A_Fix_test=5F?= =?utf-8?q?tasks_for_Python_3=2E5?= Message-ID: <3gsbdJ1L4Lz7LjN@mail.python.org> http://hg.python.org/cpython/rev/0ce4ce919ccd changeset: 91215:0ce4ce919ccd parent: 91213:28b3b8b22654 parent: 91214:36ef32003a81 user: Victor Stinner date: Mon Jun 16 17:12:39 2014 +0200 summary: (Merge 3.4) Sync asyncio with Tulip: Fix test_tasks for Python 3.5 On Python 3.5, generator now gets their name from the function, no more from the code. So we get the expected "notmuch" name instead of the generic "coro" name. files: Lib/test/test_asyncio/test_tasks.py | 9 ++++++++- 1 files changed, 8 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 @@ -2,6 +2,7 @@ import gc import os.path +import sys import types import unittest import weakref @@ -154,7 +155,13 @@ t = MyTask(gen, loop=self.loop) filename = gen.gi_code.co_filename lineno = gen.gi_frame.f_lineno - self.assertEqual(repr(t), 'T[]()' % (filename, lineno)) + if sys.version_info >= (3, 5): + name = 'notmuch' + else: + # On Python < 3.5, generators inherit the name of the code, not of + # the function. See: http://bugs.python.org/issue21205 + name = 'coro' + self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (name, filename, lineno)) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 18:15:23 2014 From: python-checkins at python.org (zach.ware) Date: Mon, 16 Jun 2014 18:15:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Fix_typo_repor?= =?utf-8?q?ted_by_Jesse_W_on_docs=40?= Message-ID: <3gsd1R1mLHz7LjM@mail.python.org> http://hg.python.org/cpython/rev/d81500d13604 changeset: 91216:d81500d13604 branch: 2.7 parent: 91210:f254ceec0d45 user: Zachary Ware date: Mon Jun 16 11:13:01 2014 -0500 summary: Fix typo reported by Jesse W on docs@ files: Doc/howto/functional.rst | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -587,7 +587,8 @@ Because ``yield`` will often be returning ``None``, you should always check for this case. Don't just use its value in expressions unless you're sure that the -``send()`` method will be the only method used resume your generator function. +``send()`` method will be the only method used to resume your generator +function. In addition to ``send()``, there are two other new methods on generators: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 18:15:24 2014 From: python-checkins at python.org (zach.ware) Date: Mon, 16 Jun 2014 18:15:24 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fix_typo_repor?= =?utf-8?q?ted_by_Jesse_W_on_docs=40?= Message-ID: <3gsd1S3T4qz7LjM@mail.python.org> http://hg.python.org/cpython/rev/b7055979a851 changeset: 91217:b7055979a851 branch: 3.4 parent: 91214:36ef32003a81 user: Zachary Ware date: Mon Jun 16 11:13:01 2014 -0500 summary: Fix typo reported by Jesse W on docs@ files: Doc/howto/functional.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -583,7 +583,7 @@ Because ``yield`` will often be returning ``None``, you should always check for this case. Don't just use its value in expressions unless you're sure that the -:meth:`~generator.send` method will be the only method used resume your +:meth:`~generator.send` method will be the only method used to resume your generator function. In addition to :meth:`~generator.send`, there are two other methods on -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 18:15:25 2014 From: python-checkins at python.org (zach.ware) Date: Mon, 16 Jun 2014 18:15:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_typo_fix_from_3=2E4?= Message-ID: <3gsd1T5NKkz7LjM@mail.python.org> http://hg.python.org/cpython/rev/34881ee3eec5 changeset: 91218:34881ee3eec5 parent: 91215:0ce4ce919ccd parent: 91217:b7055979a851 user: Zachary Ware date: Mon Jun 16 11:14:47 2014 -0500 summary: Merge typo fix from 3.4 files: Doc/howto/functional.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -583,7 +583,7 @@ Because ``yield`` will often be returning ``None``, you should always check for this case. Don't just use its value in expressions unless you're sure that the -:meth:`~generator.send` method will be the only method used resume your +:meth:`~generator.send` method will be the only method used to resume your generator function. In addition to :meth:`~generator.send`, there are two other methods on -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 22:51:54 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 16 Jun 2014 22:51:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzcz?= =?utf-8?q?=3A_Fix_TestStdLib=2Etest=5Fpydoc=28=29_of_test=5Fenum=2E_Patch?= =?utf-8?q?_written_by?= Message-ID: <3gsl8V1Hy2z7Ljc@mail.python.org> http://hg.python.org/cpython/rev/1536085d4a94 changeset: 91219:1536085d4a94 branch: 3.4 parent: 91217:b7055979a851 user: Victor Stinner date: Mon Jun 16 22:48:43 2014 +0200 summary: Issue #21773: Fix TestStdLib.test_pydoc() of test_enum. Patch written by Claudiu Popa. The print_diffs() function was not defined, using the assertEqual() is more reliable. files: Lib/test/test_enum.py | 4 +--- 1 files changed, 1 insertions(+), 3 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 @@ -1528,9 +1528,7 @@ helper = pydoc.Helper(output=output) helper(self.Color) result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(result, expected_text) def test_inspect_getmembers(self): values = dict(( -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 16 22:51:55 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 16 Jun 2014 22:51:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321773=3A_Fix_TestStdLib=2Etes?= =?utf-8?q?t=5Fpydoc=28=29_of_test=5Fenum=2E_Patch?= Message-ID: <3gsl8W32zQz7Ljn@mail.python.org> http://hg.python.org/cpython/rev/e82ba67d7472 changeset: 91220:e82ba67d7472 parent: 91218:34881ee3eec5 parent: 91219:1536085d4a94 user: Victor Stinner date: Mon Jun 16 22:51:36 2014 +0200 summary: (Merge 3.4) Issue #21773: Fix TestStdLib.test_pydoc() of test_enum. Patch written by Claudiu Popa. The print_diffs() function was not defined, using the assertEqual() is more reliable. files: Lib/test/test_enum.py | 4 +--- 1 files changed, 1 insertions(+), 3 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 @@ -1528,9 +1528,7 @@ helper = pydoc.Helper(output=output) helper(self.Color) result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(result, expected_text) def test_inspect_getmembers(self): values = dict(( -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 00:27:48 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 17 Jun 2014 00:27:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogVGFz?= =?utf-8?q?k=2E=5F=5Frepr=5F=5F=28=29_now_also_handles_CoroWrapper?= Message-ID: <3gsnH806XPz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/38703604a7e4 changeset: 91221:38703604a7e4 branch: 3.4 parent: 91219:1536085d4a94 user: Victor Stinner date: Tue Jun 17 00:26:36 2014 +0200 summary: asyncio: Task.__repr__() now also handles CoroWrapper 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 @@ -190,7 +190,7 @@ i = len(res) text = self._coro.__name__ coro = self._coro - if inspect.isgenerator(coro): + if iscoroutine(coro): filename = coro.gi_code.co_filename if coro.gi_frame is not None: text += ' at %s:%s' % (filename, coro.gi_frame.f_lineno) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 00:27:49 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 17 Jun 2014 00:27:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogKE1lcmdlIDMuNCkgYXN5bmNpbzogVGFzay5fX3JlcHJfXygpIG5vdyBh?= =?utf-8?q?lso_handles_CoroWrapper?= Message-ID: <3gsnH91tCfz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/178cda97a504 changeset: 91222:178cda97a504 parent: 91220:e82ba67d7472 parent: 91221:38703604a7e4 user: Victor Stinner date: Tue Jun 17 00:27:02 2014 +0200 summary: (Merge 3.4) asyncio: Task.__repr__() now also handles CoroWrapper 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 @@ -190,7 +190,7 @@ i = len(res) text = self._coro.__name__ coro = self._coro - if inspect.isgenerator(coro): + if iscoroutine(coro): filename = coro.gi_code.co_filename if coro.gi_frame is not None: text += ' at %s:%s' % (filename, coro.gi_frame.f_lineno) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 01:01:36 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 17 Jun 2014 01:01:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNjg2?= =?utf-8?q?=3A_add_unittest_for_idlelib=2EHyperParser=2E__Original_patch_b?= =?utf-8?q?y_Saimadhav?= Message-ID: <3gsp281HlSz7LjN@mail.python.org> http://hg.python.org/cpython/rev/59730aecce9b changeset: 91223:59730aecce9b branch: 2.7 parent: 91216:d81500d13604 user: Terry Jan Reedy date: Mon Jun 16 19:00:54 2014 -0400 summary: Issue #21686: add unittest for idlelib.HyperParser. Original patch by Saimadhav Heblikar. files: Lib/idlelib/HyperParser.py | 11 +- Lib/idlelib/idle_test/test_hyperparser.py | 191 ++++++++++ 2 files changed, 199 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -1,4 +1,4 @@ -"""Provide advanced parsing abilities for the ParenMatch and other extensions. +"""Provide advanced parsing abilities for ParenMatch and other extensions. HyperParser uses PyParser. PyParser mostly gives information on the proper indentation of code. HyperParser gives additional information on @@ -88,7 +88,7 @@ self.indexbracket += 1 def is_in_string(self): - """Is the index given to the HyperParser is in a string?""" + """Is the index given to the HyperParser in a string?""" # The bracket to which we belong should be an opener. # If it's an opener, it has to have a character. return (self.isopener[self.indexbracket] and @@ -96,7 +96,7 @@ in ('"', "'")) def is_in_code(self): - """Is the index given to the HyperParser is in a normal code?""" + """Is the index given to the HyperParser in normal code?""" return (not self.isopener[self.indexbracket] or self.rawtext[self.bracketing[self.indexbracket][0]] not in ('#', '"', "'")) @@ -248,3 +248,8 @@ break return rawtext[last_identifier_pos:self.indexinrawtext] + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_hyperparser', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_hyperparser.py b/Lib/idlelib/idle_test/test_hyperparser.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_hyperparser.py @@ -0,0 +1,191 @@ +"""Unittest for idlelib.HyperParser""" +import unittest +from test.test_support import requires +from Tkinter import Tk, Text +from idlelib.EditorWindow import EditorWindow +from idlelib.HyperParser import HyperParser + +class DummyEditwin: + def __init__(self, text): + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + self.num_context_lines = 50, 500, 1000 + + _build_char_in_string_func = EditorWindow._build_char_in_string_func.im_func + is_char_in_string = EditorWindow.is_char_in_string.im_func + + +class HyperParserTest(unittest.TestCase): + code = ( + '"""This is a module docstring"""\n' + '# this line is a comment\n' + 'x = "this is a string"\n' + "y = 'this is also a string'\n" + 'l = [i for i in range(10)]\n' + 'm = [py*py for # comment\n' + ' py in l]\n' + 'x.__len__\n' + "z = ((r'asdf')+('a')))\n" + '[x for x in\n' + 'for = False\n' + ) + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.text = Text(cls.root) + cls.editwin = DummyEditwin(cls.text) + + @classmethod + def tearDownClass(cls): + del cls.text, cls.editwin + cls.root.destroy() + del cls.root + + def setUp(self): + self.text.insert('insert', self.code) + + def tearDown(self): + self.text.delete('1.0', 'end') + self.editwin.context_use_ps1 = True + + def get_parser(self, index): + """ + Return a parser object with index at 'index' + """ + return HyperParser(self.editwin, index) + + def test_init(self): + """ + test corner cases in the init method + """ + with self.assertRaises(ValueError) as ve: + self.text.tag_add('console', '1.0', '1.end') + p = self.get_parser('1.5') + self.assertIn('precedes', str(ve.exception)) + + # test without ps1 + self.editwin.context_use_ps1 = False + + # number of lines lesser than 50 + p = self.get_parser('end') + self.assertEqual(p.rawtext, self.text.get('1.0', 'end')) + + # number of lines greater than 50 + self.text.insert('end', self.text.get('1.0', 'end')*4) + p = self.get_parser('54.5') + + def test_is_in_string(self): + get = self.get_parser + + p = get('1.0') + self.assertFalse(p.is_in_string()) + p = get('1.4') + self.assertTrue(p.is_in_string()) + p = get('2.3') + self.assertFalse(p.is_in_string()) + p = get('3.3') + self.assertFalse(p.is_in_string()) + p = get('3.7') + self.assertTrue(p.is_in_string()) + p = get('4.6') + self.assertTrue(p.is_in_string()) + + def test_is_in_code(self): + get = self.get_parser + + p = get('1.0') + self.assertTrue(p.is_in_code()) + p = get('1.1') + self.assertFalse(p.is_in_code()) + p = get('2.5') + self.assertFalse(p.is_in_code()) + p = get('3.4') + self.assertTrue(p.is_in_code()) + p = get('3.6') + self.assertFalse(p.is_in_code()) + p = get('4.14') + self.assertFalse(p.is_in_code()) + + def test_get_surrounding_bracket(self): + get = self.get_parser + + def without_mustclose(parser): + # a utility function to get surrounding bracket + # with mustclose=False + return parser.get_surrounding_brackets(mustclose=False) + + def with_mustclose(parser): + # a utility function to get surrounding bracket + # with mustclose=True + return parser.get_surrounding_brackets(mustclose=True) + + p = get('3.2') + self.assertIsNone(with_mustclose(p)) + self.assertIsNone(without_mustclose(p)) + + p = get('5.6') + self.assertTupleEqual(without_mustclose(p), ('5.4', '5.25')) + self.assertTupleEqual(without_mustclose(p), with_mustclose(p)) + + p = get('5.23') + self.assertTupleEqual(without_mustclose(p), ('5.21', '5.24')) + self.assertTupleEqual(without_mustclose(p), with_mustclose(p)) + + p = get('6.15') + self.assertTupleEqual(without_mustclose(p), ('6.4', '6.end')) + self.assertIsNone(with_mustclose(p)) + + p = get('9.end') + self.assertIsNone(with_mustclose(p)) + self.assertIsNone(without_mustclose(p)) + + def test_get_expression(self): + get = self.get_parser + + p = get('4.2') + self.assertEqual(p.get_expression(), 'y ') + + p = get('4.7') + with self.assertRaises(ValueError) as ve: + p.get_expression() + self.assertIn('is inside a code', str(ve.exception)) + + p = get('5.25') + self.assertEqual(p.get_expression(), 'range(10)') + + p = get('6.7') + self.assertEqual(p.get_expression(), 'py') + + p = get('6.8') + self.assertEqual(p.get_expression(), '') + + p = get('7.9') + self.assertEqual(p.get_expression(), 'py') + + p = get('8.end') + self.assertEqual(p.get_expression(), 'x.__len__') + + p = get('9.13') + self.assertEqual(p.get_expression(), "r'asdf'") + + p = get('9.17') + with self.assertRaises(ValueError) as ve: + p.get_expression() + self.assertIn('is inside a code', str(ve.exception)) + + p = get('10.0') + self.assertEqual(p.get_expression(), '') + + p = get('11.3') + self.assertEqual(p.get_expression(), '') + + p = get('11.11') + self.assertEqual(p.get_expression(), 'False') + + +if __name__ == '__main__': + unittest.main(verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 01:01:37 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 17 Jun 2014 01:01:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjg2?= =?utf-8?q?=3A_add_unittest_for_idlelib=2EHyperParser=2E__Original_patch_b?= =?utf-8?q?y_Saimadhav?= Message-ID: <3gsp2949GVz7Lk0@mail.python.org> http://hg.python.org/cpython/rev/2fc89d2230a2 changeset: 91224:2fc89d2230a2 branch: 3.4 parent: 91221:38703604a7e4 user: Terry Jan Reedy date: Mon Jun 16 19:01:01 2014 -0400 summary: Issue #21686: add unittest for idlelib.HyperParser. Original patch by Saimadhav Heblikar. Correct a minor 3.x bug in HyperParser discovered by testing. files: Lib/idlelib/HyperParser.py | 14 +- Lib/idlelib/idle_test/test_hyperparser.py | 191 ++++++++++ 2 files changed, 201 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -1,4 +1,4 @@ -"""Provide advanced parsing abilities for the ParenMatch and other extensions. +"""Provide advanced parsing abilities for ParenMatch and other extensions. HyperParser uses PyParser. PyParser mostly gives information on the proper indentation of code. HyperParser gives additional information on @@ -88,7 +88,7 @@ self.indexbracket += 1 def is_in_string(self): - """Is the index given to the HyperParser is in a string?""" + """Is the index given to the HyperParser in a string?""" # The bracket to which we belong should be an opener. # If it's an opener, it has to have a character. return (self.isopener[self.indexbracket] and @@ -96,7 +96,7 @@ in ('"', "'")) def is_in_code(self): - """Is the index given to the HyperParser is in a normal code?""" + """Is the index given to the HyperParser in normal code?""" return (not self.isopener[self.indexbracket] or self.rawtext[self.bracketing[self.indexbracket][0]] not in ('#', '"', "'")) @@ -158,7 +158,8 @@ while i > limit and str[i-1] in self._id_chars: i -= 1 if (i < pos and (str[i] not in self._id_first_chars or - keyword.iskeyword(str[i:pos]))): + (keyword.iskeyword(str[i:pos]) and + str[i:pos] not in {'None', 'False', 'True'}))): i = pos return pos - i @@ -248,3 +249,8 @@ break return rawtext[last_identifier_pos:self.indexinrawtext] + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_hyperparser', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_hyperparser.py b/Lib/idlelib/idle_test/test_hyperparser.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_hyperparser.py @@ -0,0 +1,191 @@ +"""Unittest for idlelib.HyperParser""" +import unittest +from test.support import requires +from tkinter import Tk, Text +from idlelib.EditorWindow import EditorWindow +from idlelib.HyperParser import HyperParser + +class DummyEditwin: + def __init__(self, text): + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + self.num_context_lines = 50, 500, 1000 + + _build_char_in_string_func = EditorWindow._build_char_in_string_func + is_char_in_string = EditorWindow.is_char_in_string + + +class HyperParserTest(unittest.TestCase): + code = ( + '"""This is a module docstring"""\n' + '# this line is a comment\n' + 'x = "this is a string"\n' + "y = 'this is also a string'\n" + 'l = [i for i in range(10)]\n' + 'm = [py*py for # comment\n' + ' py in l]\n' + 'x.__len__\n' + "z = ((r'asdf')+('a')))\n" + '[x for x in\n' + 'for = False\n' + ) + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.text = Text(cls.root) + cls.editwin = DummyEditwin(cls.text) + + @classmethod + def tearDownClass(cls): + del cls.text, cls.editwin + cls.root.destroy() + del cls.root + + def setUp(self): + self.text.insert('insert', self.code) + + def tearDown(self): + self.text.delete('1.0', 'end') + self.editwin.context_use_ps1 = True + + def get_parser(self, index): + """ + Return a parser object with index at 'index' + """ + return HyperParser(self.editwin, index) + + def test_init(self): + """ + test corner cases in the init method + """ + with self.assertRaises(ValueError) as ve: + self.text.tag_add('console', '1.0', '1.end') + p = self.get_parser('1.5') + self.assertIn('precedes', str(ve.exception)) + + # test without ps1 + self.editwin.context_use_ps1 = False + + # number of lines lesser than 50 + p = self.get_parser('end') + self.assertEqual(p.rawtext, self.text.get('1.0', 'end')) + + # number of lines greater than 50 + self.text.insert('end', self.text.get('1.0', 'end')*4) + p = self.get_parser('54.5') + + def test_is_in_string(self): + get = self.get_parser + + p = get('1.0') + self.assertFalse(p.is_in_string()) + p = get('1.4') + self.assertTrue(p.is_in_string()) + p = get('2.3') + self.assertFalse(p.is_in_string()) + p = get('3.3') + self.assertFalse(p.is_in_string()) + p = get('3.7') + self.assertTrue(p.is_in_string()) + p = get('4.6') + self.assertTrue(p.is_in_string()) + + def test_is_in_code(self): + get = self.get_parser + + p = get('1.0') + self.assertTrue(p.is_in_code()) + p = get('1.1') + self.assertFalse(p.is_in_code()) + p = get('2.5') + self.assertFalse(p.is_in_code()) + p = get('3.4') + self.assertTrue(p.is_in_code()) + p = get('3.6') + self.assertFalse(p.is_in_code()) + p = get('4.14') + self.assertFalse(p.is_in_code()) + + def test_get_surrounding_bracket(self): + get = self.get_parser + + def without_mustclose(parser): + # a utility function to get surrounding bracket + # with mustclose=False + return parser.get_surrounding_brackets(mustclose=False) + + def with_mustclose(parser): + # a utility function to get surrounding bracket + # with mustclose=True + return parser.get_surrounding_brackets(mustclose=True) + + p = get('3.2') + self.assertIsNone(with_mustclose(p)) + self.assertIsNone(without_mustclose(p)) + + p = get('5.6') + self.assertTupleEqual(without_mustclose(p), ('5.4', '5.25')) + self.assertTupleEqual(without_mustclose(p), with_mustclose(p)) + + p = get('5.23') + self.assertTupleEqual(without_mustclose(p), ('5.21', '5.24')) + self.assertTupleEqual(without_mustclose(p), with_mustclose(p)) + + p = get('6.15') + self.assertTupleEqual(without_mustclose(p), ('6.4', '6.end')) + self.assertIsNone(with_mustclose(p)) + + p = get('9.end') + self.assertIsNone(with_mustclose(p)) + self.assertIsNone(without_mustclose(p)) + + def test_get_expression(self): + get = self.get_parser + + p = get('4.2') + self.assertEqual(p.get_expression(), 'y ') + + p = get('4.7') + with self.assertRaises(ValueError) as ve: + p.get_expression() + self.assertIn('is inside a code', str(ve.exception)) + + p = get('5.25') + self.assertEqual(p.get_expression(), 'range(10)') + + p = get('6.7') + self.assertEqual(p.get_expression(), 'py') + + p = get('6.8') + self.assertEqual(p.get_expression(), '') + + p = get('7.9') + self.assertEqual(p.get_expression(), 'py') + + p = get('8.end') + self.assertEqual(p.get_expression(), 'x.__len__') + + p = get('9.13') + self.assertEqual(p.get_expression(), "r'asdf'") + + p = get('9.17') + with self.assertRaises(ValueError) as ve: + p.get_expression() + self.assertIn('is inside a code', str(ve.exception)) + + p = get('10.0') + self.assertEqual(p.get_expression(), '') + + p = get('11.3') + self.assertEqual(p.get_expression(), '') + + p = get('11.11') + self.assertEqual(p.get_expression(), 'False') + + +if __name__ == '__main__': + unittest.main(verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 01:01:38 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 17 Jun 2014 01:01:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gsp2B6znPz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/1b3a5d63521c changeset: 91225:1b3a5d63521c parent: 91222:178cda97a504 parent: 91224:2fc89d2230a2 user: Terry Jan Reedy date: Mon Jun 16 19:01:14 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/HyperParser.py | 14 +- Lib/idlelib/idle_test/test_hyperparser.py | 191 ++++++++++ 2 files changed, 201 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -1,4 +1,4 @@ -"""Provide advanced parsing abilities for the ParenMatch and other extensions. +"""Provide advanced parsing abilities for ParenMatch and other extensions. HyperParser uses PyParser. PyParser mostly gives information on the proper indentation of code. HyperParser gives additional information on @@ -88,7 +88,7 @@ self.indexbracket += 1 def is_in_string(self): - """Is the index given to the HyperParser is in a string?""" + """Is the index given to the HyperParser in a string?""" # The bracket to which we belong should be an opener. # If it's an opener, it has to have a character. return (self.isopener[self.indexbracket] and @@ -96,7 +96,7 @@ in ('"', "'")) def is_in_code(self): - """Is the index given to the HyperParser is in a normal code?""" + """Is the index given to the HyperParser in normal code?""" return (not self.isopener[self.indexbracket] or self.rawtext[self.bracketing[self.indexbracket][0]] not in ('#', '"', "'")) @@ -158,7 +158,8 @@ while i > limit and str[i-1] in self._id_chars: i -= 1 if (i < pos and (str[i] not in self._id_first_chars or - keyword.iskeyword(str[i:pos]))): + (keyword.iskeyword(str[i:pos]) and + str[i:pos] not in {'None', 'False', 'True'}))): i = pos return pos - i @@ -248,3 +249,8 @@ break return rawtext[last_identifier_pos:self.indexinrawtext] + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_hyperparser', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_hyperparser.py b/Lib/idlelib/idle_test/test_hyperparser.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_hyperparser.py @@ -0,0 +1,191 @@ +"""Unittest for idlelib.HyperParser""" +import unittest +from test.support import requires +from tkinter import Tk, Text +from idlelib.EditorWindow import EditorWindow +from idlelib.HyperParser import HyperParser + +class DummyEditwin: + def __init__(self, text): + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + self.num_context_lines = 50, 500, 1000 + + _build_char_in_string_func = EditorWindow._build_char_in_string_func + is_char_in_string = EditorWindow.is_char_in_string + + +class HyperParserTest(unittest.TestCase): + code = ( + '"""This is a module docstring"""\n' + '# this line is a comment\n' + 'x = "this is a string"\n' + "y = 'this is also a string'\n" + 'l = [i for i in range(10)]\n' + 'm = [py*py for # comment\n' + ' py in l]\n' + 'x.__len__\n' + "z = ((r'asdf')+('a')))\n" + '[x for x in\n' + 'for = False\n' + ) + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.text = Text(cls.root) + cls.editwin = DummyEditwin(cls.text) + + @classmethod + def tearDownClass(cls): + del cls.text, cls.editwin + cls.root.destroy() + del cls.root + + def setUp(self): + self.text.insert('insert', self.code) + + def tearDown(self): + self.text.delete('1.0', 'end') + self.editwin.context_use_ps1 = True + + def get_parser(self, index): + """ + Return a parser object with index at 'index' + """ + return HyperParser(self.editwin, index) + + def test_init(self): + """ + test corner cases in the init method + """ + with self.assertRaises(ValueError) as ve: + self.text.tag_add('console', '1.0', '1.end') + p = self.get_parser('1.5') + self.assertIn('precedes', str(ve.exception)) + + # test without ps1 + self.editwin.context_use_ps1 = False + + # number of lines lesser than 50 + p = self.get_parser('end') + self.assertEqual(p.rawtext, self.text.get('1.0', 'end')) + + # number of lines greater than 50 + self.text.insert('end', self.text.get('1.0', 'end')*4) + p = self.get_parser('54.5') + + def test_is_in_string(self): + get = self.get_parser + + p = get('1.0') + self.assertFalse(p.is_in_string()) + p = get('1.4') + self.assertTrue(p.is_in_string()) + p = get('2.3') + self.assertFalse(p.is_in_string()) + p = get('3.3') + self.assertFalse(p.is_in_string()) + p = get('3.7') + self.assertTrue(p.is_in_string()) + p = get('4.6') + self.assertTrue(p.is_in_string()) + + def test_is_in_code(self): + get = self.get_parser + + p = get('1.0') + self.assertTrue(p.is_in_code()) + p = get('1.1') + self.assertFalse(p.is_in_code()) + p = get('2.5') + self.assertFalse(p.is_in_code()) + p = get('3.4') + self.assertTrue(p.is_in_code()) + p = get('3.6') + self.assertFalse(p.is_in_code()) + p = get('4.14') + self.assertFalse(p.is_in_code()) + + def test_get_surrounding_bracket(self): + get = self.get_parser + + def without_mustclose(parser): + # a utility function to get surrounding bracket + # with mustclose=False + return parser.get_surrounding_brackets(mustclose=False) + + def with_mustclose(parser): + # a utility function to get surrounding bracket + # with mustclose=True + return parser.get_surrounding_brackets(mustclose=True) + + p = get('3.2') + self.assertIsNone(with_mustclose(p)) + self.assertIsNone(without_mustclose(p)) + + p = get('5.6') + self.assertTupleEqual(without_mustclose(p), ('5.4', '5.25')) + self.assertTupleEqual(without_mustclose(p), with_mustclose(p)) + + p = get('5.23') + self.assertTupleEqual(without_mustclose(p), ('5.21', '5.24')) + self.assertTupleEqual(without_mustclose(p), with_mustclose(p)) + + p = get('6.15') + self.assertTupleEqual(without_mustclose(p), ('6.4', '6.end')) + self.assertIsNone(with_mustclose(p)) + + p = get('9.end') + self.assertIsNone(with_mustclose(p)) + self.assertIsNone(without_mustclose(p)) + + def test_get_expression(self): + get = self.get_parser + + p = get('4.2') + self.assertEqual(p.get_expression(), 'y ') + + p = get('4.7') + with self.assertRaises(ValueError) as ve: + p.get_expression() + self.assertIn('is inside a code', str(ve.exception)) + + p = get('5.25') + self.assertEqual(p.get_expression(), 'range(10)') + + p = get('6.7') + self.assertEqual(p.get_expression(), 'py') + + p = get('6.8') + self.assertEqual(p.get_expression(), '') + + p = get('7.9') + self.assertEqual(p.get_expression(), 'py') + + p = get('8.end') + self.assertEqual(p.get_expression(), 'x.__len__') + + p = get('9.13') + self.assertEqual(p.get_expression(), "r'asdf'") + + p = get('9.17') + with self.assertRaises(ValueError) as ve: + p.get_expression() + self.assertIn('is inside a code', str(ve.exception)) + + p = get('10.0') + self.assertEqual(p.get_expression(), '') + + p = get('11.3') + self.assertEqual(p.get_expression(), '') + + p = get('11.11') + self.assertEqual(p.get_expression(), 'False') + + +if __name__ == '__main__': + unittest.main(verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 01:27:27 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 17 Jun 2014 01:27:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Add_Idle_news_?= =?utf-8?q?entries_=28plus_whitespace_fix_from_other_entries=29=2E?= Message-ID: <3gspbz0DvpzRxT@mail.python.org> http://hg.python.org/cpython/rev/6d86e8afc2a5 changeset: 91226:6d86e8afc2a5 branch: 2.7 parent: 91223:59730aecce9b user: Terry Jan Reedy date: Mon Jun 16 19:24:22 2014 -0400 summary: Add Idle news entries (plus whitespace fix from other entries). files: Misc/NEWS | 13 +++++++++++-- 1 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -55,12 +55,21 @@ - Issue #8743: Fix interoperability between set objects and the collections.Set() abstract base class. -- Issue #21481: Argparse equality and inequality tests now return +- Issue #21481: Argparse equality and inequality tests now return NotImplemented when comparing to an unknown type. - + IDLE ---- +- Issue #21686: add unittest for HyperParser. Original patch by Saimadhav + Heblikar. + +- Issue #12387: Add missing upper(lower)case versions of default Windows key + bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy. + +- Issue #21695: Closing a Find-in-files output window while the search is + still in progress no longer closes Idle. + - Issue #18910: Add unittest for textView. Patch by Phil Webster. - Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 01:27:28 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 17 Jun 2014 01:27:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Add_Idle_news_?= =?utf-8?q?entries=2E?= Message-ID: <3gspc02063z7Ljl@mail.python.org> http://hg.python.org/cpython/rev/b58a80a44909 changeset: 91227:b58a80a44909 branch: 3.4 parent: 91224:2fc89d2230a2 user: Terry Jan Reedy date: Mon Jun 16 19:24:29 2014 -0400 summary: Add Idle news entries. files: Misc/NEWS | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -98,6 +98,15 @@ IDLE ---- +- Issue #21686: add unittest for HyperParser. Original patch by Saimadhav + Heblikar. + +- Issue #12387: Add missing upper(lower)case versions of default Windows key + bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy. + +- Issue #21695: Closing a Find-in-files output window while the search is + still in progress no longer closes Idle. + - Issue #18910: Add unittest for textView. Patch by Phil Webster. - Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 01:27:29 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 17 Jun 2014 01:27:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Add_Idle_news_entries=2E?= Message-ID: <3gspc13xLBz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/a82d7e028458 changeset: 91228:a82d7e028458 parent: 91225:1b3a5d63521c parent: 91227:b58a80a44909 user: Terry Jan Reedy date: Mon Jun 16 19:26:56 2014 -0400 summary: Add Idle news entries. files: Misc/NEWS | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -499,6 +499,15 @@ IDLE ---- +- Issue #21686: add unittest for HyperParser. Original patch by Saimadhav + Heblikar. + +- Issue #12387: Add missing upper(lower)case versions of default Windows key + bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy. + +- Issue #21695: Closing a Find-in-files output window while the search is + still in progress no longer closes Idle. + - Issue #18910: Add unittest for textView. Patch by Phil Webster. - Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. -- Repository URL: http://hg.python.org/cpython From tjreedy at udel.edu Tue Jun 17 03:21:39 2014 From: tjreedy at udel.edu (Terry Reedy) Date: Mon, 16 Jun 2014 21:21:39 -0400 Subject: [Python-checkins] cpython (merge 3.4 -> default): Add Idle news entries. In-Reply-To: <3gspc13xLBz7Ljm@mail.python.org> References: <3gspc13xLBz7Ljm@mail.python.org> Message-ID: <539F9823.502@udel.edu> On 6/16/2014 7:27 PM, terry.reedy wrote: > http://hg.python.org/cpython/rev/a82d7e028458 > changeset: 91228:a82d7e028458 > parent: 91225:1b3a5d63521c > parent: 91227:b58a80a44909 > user: Terry Jan Reedy > date: Mon Jun 16 19:26:56 2014 -0400 > summary: > Add Idle news entries. > > files: > Misc/NEWS | 9 +++++++++ > 1 files changed, 9 insertions(+), 0 deletions(-) > > > diff --git a/Misc/NEWS b/Misc/NEWS > --- a/Misc/NEWS > +++ b/Misc/NEWS > @@ -499,6 +499,15 @@ > IDLE > ---- > > +- Issue #21686: add unittest for HyperParser. Original patch by Saimadhav > + Heblikar. > + > +- Issue #12387: Add missing upper(lower)case versions of default Windows key > + bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy. > + > +- Issue #21695: Closing a Find-in-files output window while the search is > + still in progress no longer closes Idle. > + > - Issue #18910: Add unittest for textView. Patch by Phil Webster. > > - Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. I am pushing Idle news items separately, in batches, because hg (3.0+3 on Windows) cannot currently merge them from 3.4 to default. I suspect the line offset is greater than its normal fuzz factor. In any case, hg creates phantom conflicts and inserts merge markers in seemingly random locations including chunks of other code. So this is really a null merge (revert to local) followed by a manual copy, paste, and save. tjr From python-checkins at python.org Tue Jun 17 04:39:45 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 04:39:45 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_avoid_crashes_?= =?utf-8?q?and_lockups_from_daemon_threads_during_interpreter_shutdown?= Message-ID: <3gstss1T87z7Ljb@mail.python.org> http://hg.python.org/cpython/rev/7741d0dd66ca changeset: 91229:7741d0dd66ca branch: 2.7 parent: 91226:6d86e8afc2a5 user: Benjamin Peterson date: Mon Jun 16 19:39:18 2014 -0700 summary: avoid crashes and lockups from daemon threads during interpreter shutdown (#1856) files: Include/pythonrun.h | 2 + Lib/test/test_threading.py | 43 ++++++++++++++++++++++++++ Misc/NEWS | 4 ++ Python/ceval.c | 6 +++ Python/pythonrun.c | 9 ++++- Python/thread_pthread.h | 4 +- 6 files changed, 65 insertions(+), 3 deletions(-) diff --git a/Include/pythonrun.h b/Include/pythonrun.h --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -146,6 +146,8 @@ PyAPI_FUNC(void) PyOS_FiniInterrupts(void); PyAPI_FUNC(void) PyByteArray_Fini(void); +PyAPI_DATA(PyThreadState *) _Py_Finalizing; + /* Stuff with no proper home (yet) */ PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *); PyAPI_DATA(int) (*PyOS_InputHook)(void); 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 @@ -700,6 +700,49 @@ output = "end of worker thread\nend of main thread\n" self.assertScriptHasOutput(script, output) + @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") + def test_6_daemon_threads(self): + # Check that a daemon thread cannot crash the interpreter on shutdown + # by manipulating internal structures that are being disposed of in + # the main thread. + script = """if True: + import os + import random + import sys + import time + import threading + + thread_has_run = set() + + def random_io(): + '''Loop for a while sleeping random tiny amounts and doing some I/O.''' + while True: + in_f = open(os.__file__, 'rb') + stuff = in_f.read(200) + null_f = open(os.devnull, 'wb') + null_f.write(stuff) + time.sleep(random.random() / 1995) + null_f.close() + in_f.close() + thread_has_run.add(threading.current_thread()) + + def main(): + count = 0 + for _ in range(40): + new_thread = threading.Thread(target=random_io) + new_thread.daemon = True + new_thread.start() + count += 1 + while len(thread_has_run) < count: + time.sleep(0.001) + # Trigger process shutdown + sys.exit(0) + + main() + """ + rc, out, err = assert_python_ok('-c', script) + self.assertFalse(err) + @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") def test_reinit_tls_after_fork(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #1856: Avoid crashes and lockups when daemon threads run while the + interpreter is shutting down; instead, these threads are now killed when they + try to take the GIL. + - Issue #19656: Running Python with the -3 option now also warns about non-ascii bytes literals. diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -355,6 +355,12 @@ if (interpreter_lock) { int err = errno; PyThread_acquire_lock(interpreter_lock, 1); + /* _Py_Finalizing is protected by the GIL */ + if (_Py_Finalizing && tstate != _Py_Finalizing) { + PyThread_release_lock(interpreter_lock); + PyThread_exit_thread(); + assert(0); /* unreachable */ + } errno = err; } #endif diff --git a/Python/pythonrun.c b/Python/pythonrun.c --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -91,6 +91,8 @@ int Py_NoUserSiteDirectory = 0; /* for -s and site.py */ int Py_HashRandomizationFlag = 0; /* for -R and PYTHONHASHSEED */ +PyThreadState *_Py_Finalizing = NULL; + /* Hack to force loading of object files */ int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) = \ @@ -163,6 +165,7 @@ if (initialized) return; initialized = 1; + _Py_Finalizing = NULL; if ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0') Py_DebugFlag = add_flag(Py_DebugFlag, p); @@ -422,12 +425,16 @@ * the threads created via Threading. */ call_sys_exitfunc(); - initialized = 0; /* Get current thread state and interpreter pointer */ tstate = PyThreadState_GET(); interp = tstate->interp; + /* Remaining threads (e.g. daemon threads) will automatically exit + after taking the GIL (in PyEval_RestoreThread()). */ + _Py_Finalizing = tstate; + initialized = 0; + /* Disable signal handling */ PyOS_FiniInterrupts(); diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -242,9 +242,9 @@ PyThread_exit_thread(void) { dprintf(("PyThread_exit_thread called\n")); - if (!initialized) { + if (!initialized) exit(0); - } + pthread_exit(0); } #ifdef USE_SEMAPHORES -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:08:49 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:08:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_check_if_the_t?= =?utf-8?q?hread_is_finalizing_after_retaking_the_GIL?= Message-ID: <3gszW54dSsz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/0432b0d99175 changeset: 91230:0432b0d99175 branch: 2.7 user: Benjamin Peterson date: Mon Jun 16 22:59:07 2014 -0700 summary: check if the thread is finalizing after retaking the GIL files: Python/ceval.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1024,6 +1024,13 @@ /* Other threads may run now */ PyThread_acquire_lock(interpreter_lock, 1); + + /* Check if we should make a quick exit. */ + if (_Py_Finalizing && _Py_Finalizing != tstate) { + PyThread_release_lock(interpreter_lock); + PyThread_exit_thread(); + } + if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:08:50 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:08:50 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_check_if_the_t?= =?utf-8?q?hread_is_finalizing_after_retaking_the_GIL?= Message-ID: <3gszW66PJfz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/d1d1ed421717 changeset: 91231:d1d1ed421717 branch: 3.4 parent: 91227:b58a80a44909 user: Benjamin Peterson date: Mon Jun 16 22:59:07 2014 -0700 summary: check if the thread is finalizing after retaking the GIL files: Python/ceval.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1267,6 +1267,13 @@ /* Other threads may run now */ take_gil(tstate); + + /* Check if we should make a quick exit. */ + if (_Py_Finalizing && _Py_Finalizing != tstate) { + drop_gil(tstate); + PyThread_exit_thread(); + } + if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:08:52 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:08:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <3gszW80nnnz7LkH@mail.python.org> http://hg.python.org/cpython/rev/556b37984622 changeset: 91232:556b37984622 parent: 91228:a82d7e028458 parent: 91231:d1d1ed421717 user: Benjamin Peterson date: Mon Jun 16 23:07:15 2014 -0700 summary: merge 3.4 files: Python/ceval.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1267,6 +1267,13 @@ /* Other threads may run now */ take_gil(tstate); + + /* Check if we should make a quick exit. */ + if (_Py_Finalizing && _Py_Finalizing != tstate) { + drop_gil(tstate); + PyThread_exit_thread(); + } + if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:08:53 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:08:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_avoid_a_deadlo?= =?utf-8?q?ck_with_the_interpreter_head_lock_and_the_GIL_during_finalizati?= =?utf-8?q?on?= Message-ID: <3gszW94Fhsz7Lk0@mail.python.org> http://hg.python.org/cpython/rev/58c638e86e30 changeset: 91233:58c638e86e30 branch: 2.7 parent: 91230:0432b0d99175 user: Benjamin Peterson date: Mon Jun 16 23:07:49 2014 -0700 summary: avoid a deadlock with the interpreter head lock and the GIL during finalization files: Python/pystate.c | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -315,7 +315,14 @@ Py_FatalError( "PyThreadState_DeleteCurrent: no current tstate"); _PyThreadState_Current = NULL; - tstate_delete_common(tstate); + /* + Only call tstate_delete_common to have the tstate if we're not finalizing + or we're the main thread. The main thread will do this for us. Not calling + tstate_delete_common means we won't lock the interpreter head lock, + avoiding a possible deadlock with the GIL. + */ + if (!_Py_Finalizing || _Py_Finalizing == tstate) + tstate_delete_common(tstate); if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); PyEval_ReleaseLock(); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:08:54 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:08:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_avoid_a_deadlo?= =?utf-8?q?ck_with_the_interpreter_head_lock_and_the_GIL_during_finalizati?= =?utf-8?q?on?= Message-ID: <3gszWB5xsqz7Lky@mail.python.org> http://hg.python.org/cpython/rev/5ccb6901cf95 changeset: 91234:5ccb6901cf95 branch: 3.4 parent: 91231:d1d1ed421717 user: Benjamin Peterson date: Mon Jun 16 23:07:49 2014 -0700 summary: avoid a deadlock with the interpreter head lock and the GIL during finalization files: Python/pystate.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -423,6 +423,14 @@ Py_FatalError( "PyThreadState_DeleteCurrent: no current tstate"); _Py_atomic_store_relaxed(&_PyThreadState_Current, NULL); + /* + Only call tstate_delete_common to have the tstate if we're not finalizing + or we're the main thread. The main thread will do this for us. Not calling + tstate_delete_common means we won't lock the interpreter head lock, + avoiding a possible deadlock with the GIL. + */ + if (!_Py_Finalizing || _Py_Finalizing == tstate) + tstate_delete_common(tstate); if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); tstate_delete_common(tstate); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:08:56 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:08:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <3gszWD0Tp7z7Ljs@mail.python.org> http://hg.python.org/cpython/rev/2ed64ea19d81 changeset: 91235:2ed64ea19d81 parent: 91232:556b37984622 parent: 91234:5ccb6901cf95 user: Benjamin Peterson date: Mon Jun 16 23:08:29 2014 -0700 summary: merge 3.4 files: Python/pystate.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -423,6 +423,14 @@ Py_FatalError( "PyThreadState_DeleteCurrent: no current tstate"); _Py_atomic_store_relaxed(&_PyThreadState_Current, NULL); + /* + Only call tstate_delete_common to have the tstate if we're not finalizing + or we're the main thread. The main thread will do this for us. Not calling + tstate_delete_common means we won't lock the interpreter head lock, + avoiding a possible deadlock with the GIL. + */ + if (!_Py_Finalizing || _Py_Finalizing == tstate) + tstate_delete_common(tstate); if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); tstate_delete_common(tstate); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:16:52 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:16:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E2=29=3A_expect_the_cor?= =?utf-8?q?rect_platform-dependent_linesep?= Message-ID: <3gszhN0Xlbz7LjT@mail.python.org> http://hg.python.org/cpython/rev/77f227624cff changeset: 91236:77f227624cff branch: 3.2 parent: 91178:e47422855841 user: Benjamin Peterson date: Mon Jun 16 23:15:50 2014 -0700 summary: expect the correct platform-dependent linesep files: Lib/test/test_httpservers.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 @@ -463,7 +463,7 @@ def test_urlquote_decoding_in_cgi_check(self): res = self.request('/cgi-bin%2ffile1.py') - self.assertEqual((b'Hello World\n', 'text/html', 200), + self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), (res.read(), res.getheader('Content-type'), res.status)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:16:53 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:16:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy4yIC0+IDMuMyk6?= =?utf-8?q?_merge_3=2E2?= Message-ID: <3gszhP1yqlz7Ljs@mail.python.org> http://hg.python.org/cpython/rev/7417d8854f93 changeset: 91237:7417d8854f93 branch: 3.3 parent: 91179:5676797f3a3e parent: 91236:77f227624cff user: Benjamin Peterson date: Mon Jun 16 23:16:06 2014 -0700 summary: merge 3.2 files: Lib/test/test_httpservers.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 @@ -466,7 +466,7 @@ def test_urlquote_decoding_in_cgi_check(self): res = self.request('/cgi-bin%2ffile1.py') - self.assertEqual((b'Hello World\n', 'text/html', 200), + self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), (res.read(), res.getheader('Content-type'), res.status)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:16:54 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:16:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy4zIC0+IDMuNCk6?= =?utf-8?q?_merge_3=2E3?= Message-ID: <3gszhQ3d7wz7Ljs@mail.python.org> http://hg.python.org/cpython/rev/fceb3a907260 changeset: 91238:fceb3a907260 branch: 3.4 parent: 91234:5ccb6901cf95 parent: 91237:7417d8854f93 user: Benjamin Peterson date: Mon Jun 16 23:16:31 2014 -0700 summary: merge 3.3 files: Lib/test/test_httpservers.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 @@ -487,7 +487,7 @@ def test_urlquote_decoding_in_cgi_check(self): res = self.request('/cgi-bin%2ffile1.py') - self.assertEqual((b'Hello World\n', 'text/html', 200), + self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), (res.read(), res.getheader('Content-type'), res.status)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:16:55 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:16:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <3gszhR5GVwz7LkD@mail.python.org> http://hg.python.org/cpython/rev/6fc543e32a5b changeset: 91239:6fc543e32a5b parent: 91235:2ed64ea19d81 parent: 91238:fceb3a907260 user: Benjamin Peterson date: Mon Jun 16 23:16:37 2014 -0700 summary: merge 3.4 files: Lib/test/test_httpservers.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 @@ -487,7 +487,7 @@ def test_urlquote_decoding_in_cgi_check(self): res = self.request('/cgi-bin%2ffile1.py') - self.assertEqual((b'Hello World\n', 'text/html', 200), + self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), (res.read(), res.getheader('Content-type'), res.status)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:49:24 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:49:24 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_remove_extra_c?= =?utf-8?q?all_to_tstate=5Fdelete=5Fcommon_=28merge_artifact=29?= Message-ID: <3gt0Pw54GKz7LjM@mail.python.org> http://hg.python.org/cpython/rev/488daf4691f6 changeset: 91240:488daf4691f6 branch: 3.4 parent: 91238:fceb3a907260 user: Benjamin Peterson date: Mon Jun 16 23:49:02 2014 -0700 summary: remove extra call to tstate_delete_common (merge artifact) files: Python/pystate.c | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -423,6 +423,8 @@ Py_FatalError( "PyThreadState_DeleteCurrent: no current tstate"); _Py_atomic_store_relaxed(&_PyThreadState_Current, NULL); + if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) + PyThread_delete_key_value(autoTLSkey); /* Only call tstate_delete_common to have the tstate if we're not finalizing or we're the main thread. The main thread will do this for us. Not calling @@ -431,9 +433,6 @@ */ if (!_Py_Finalizing || _Py_Finalizing == tstate) tstate_delete_common(tstate); - if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) - PyThread_delete_key_value(autoTLSkey); - tstate_delete_common(tstate); PyEval_ReleaseLock(); } #endif /* WITH_THREAD */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 08:49:25 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 08:49:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <3gt0Px6VRWz7Ljd@mail.python.org> http://hg.python.org/cpython/rev/eed8538d5940 changeset: 91241:eed8538d5940 parent: 91239:6fc543e32a5b parent: 91240:488daf4691f6 user: Benjamin Peterson date: Mon Jun 16 23:49:09 2014 -0700 summary: merge 3.4 files: Python/pystate.c | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -423,6 +423,8 @@ Py_FatalError( "PyThreadState_DeleteCurrent: no current tstate"); _Py_atomic_store_relaxed(&_PyThreadState_Current, NULL); + if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) + PyThread_delete_key_value(autoTLSkey); /* Only call tstate_delete_common to have the tstate if we're not finalizing or we're the main thread. The main thread will do this for us. Not calling @@ -431,9 +433,6 @@ */ if (!_Py_Finalizing || _Py_Finalizing == tstate) tstate_delete_common(tstate); - if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) - PyThread_delete_key_value(autoTLSkey); - tstate_delete_common(tstate); PyEval_ReleaseLock(); } #endif /* WITH_THREAD */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 09:35:10 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 09:35:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_revert_tstate?= =?utf-8?q?=5Fdelete=5Fcommon=2C_since_it=27s_pretty_much_wrong?= Message-ID: <3gt1Qk13Xpz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/b97557699db3 changeset: 91242:b97557699db3 branch: 2.7 parent: 91233:58c638e86e30 user: Benjamin Peterson date: Tue Jun 17 00:34:14 2014 -0700 summary: revert tstate_delete_common, since it's pretty much wrong files: Python/pystate.c | 9 +-------- 1 files changed, 1 insertions(+), 8 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -315,16 +315,9 @@ Py_FatalError( "PyThreadState_DeleteCurrent: no current tstate"); _PyThreadState_Current = NULL; - /* - Only call tstate_delete_common to have the tstate if we're not finalizing - or we're the main thread. The main thread will do this for us. Not calling - tstate_delete_common means we won't lock the interpreter head lock, - avoiding a possible deadlock with the GIL. - */ - if (!_Py_Finalizing || _Py_Finalizing == tstate) - tstate_delete_common(tstate); if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); + tstate_delete_common(tstate); PyEval_ReleaseLock(); } #endif /* WITH_THREAD */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 09:35:11 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 09:35:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_revert_tstate?= =?utf-8?q?=5Fdelete=5Fcommon=2C_since_it=27s_pretty_much_wrong?= Message-ID: <3gt1Ql2Yz5z7Ljp@mail.python.org> http://hg.python.org/cpython/rev/3ce746735b99 changeset: 91243:3ce746735b99 branch: 3.4 parent: 91240:488daf4691f6 user: Benjamin Peterson date: Tue Jun 17 00:34:46 2014 -0700 summary: revert tstate_delete_common, since it's pretty much wrong files: Python/pystate.c | 9 +-------- 1 files changed, 1 insertions(+), 8 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -425,14 +425,7 @@ _Py_atomic_store_relaxed(&_PyThreadState_Current, NULL); if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); - /* - Only call tstate_delete_common to have the tstate if we're not finalizing - or we're the main thread. The main thread will do this for us. Not calling - tstate_delete_common means we won't lock the interpreter head lock, - avoiding a possible deadlock with the GIL. - */ - if (!_Py_Finalizing || _Py_Finalizing == tstate) - tstate_delete_common(tstate); + tstate_delete_common(tstate); PyEval_ReleaseLock(); } #endif /* WITH_THREAD */ -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 09:35:12 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 09:35:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <3gt1Qm4Gjnz7Lk4@mail.python.org> http://hg.python.org/cpython/rev/abcf17bc5eae changeset: 91244:abcf17bc5eae parent: 91241:eed8538d5940 parent: 91243:3ce746735b99 user: Benjamin Peterson date: Tue Jun 17 00:34:56 2014 -0700 summary: merge 3.4 files: Python/pystate.c | 9 +-------- 1 files changed, 1 insertions(+), 8 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -425,14 +425,7 @@ _Py_atomic_store_relaxed(&_PyThreadState_Current, NULL); if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); - /* - Only call tstate_delete_common to have the tstate if we're not finalizing - or we're the main thread. The main thread will do this for us. Not calling - tstate_delete_common means we won't lock the interpreter head lock, - avoiding a possible deadlock with the GIL. - */ - if (!_Py_Finalizing || _Py_Finalizing == tstate) - tstate_delete_common(tstate); + tstate_delete_common(tstate); PyEval_ReleaseLock(); } #endif /* WITH_THREAD */ -- Repository URL: http://hg.python.org/cpython From root at python.org Tue Jun 17 10:10:24 2014 From: root at python.org (Cron Daemon) Date: Tue, 17 Jun 2014 10:10:24 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From solipsis at pitrou.net Tue Jun 17 11:03:01 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 17 Jun 2014 11:03:01 +0200 Subject: [Python-checkins] Daily reference leaks (a82d7e028458): sum=9 Message-ID: results for a82d7e028458 on branch "default" -------------------------------------------- test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [0, -2, 2] references, sum=0 test_site leaked [0, -2, 2] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogIy8j4U', '-x'] From python-checkins at python.org Tue Jun 17 18:45:32 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 18:45:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_add_Ammar_Broh?= =?utf-8?q?i_for_running_ssllab=27s_test_on_python=2Eorg_and_reporting_a_p?= =?utf-8?q?roblem?= Message-ID: <3gtFdm2sl4z7Ljh@mail.python.org> http://hg.python.org/cpython/rev/23c77732a6a0 changeset: 91245:23c77732a6a0 branch: 2.7 parent: 91242:b97557699db3 user: Benjamin Peterson date: Tue Jun 17 09:44:55 2014 -0700 summary: add Ammar Brohi for running ssllab's test on python.org and reporting a problem files: Misc/ACKS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -171,6 +171,7 @@ Tobias Brink Richard Brodie Michael Broghton +Ammar Brohi Daniel Brotsky Jean Brouwers Gary S. Brown -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 18:45:33 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 18:45:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_add_Ammar_Broh?= =?utf-8?q?i_for_running_ssllab=27s_test_on_python=2Eorg_and_reporting_a_p?= =?utf-8?q?roblem?= Message-ID: <3gtFdn4JDdz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/6ea2894fb983 changeset: 91246:6ea2894fb983 branch: 3.4 parent: 91243:3ce746735b99 user: Benjamin Peterson date: Tue Jun 17 09:44:55 2014 -0700 summary: add Ammar Brohi for running ssllab's test on python.org and reporting a problem files: Misc/ACKS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -173,6 +173,7 @@ Tobias Brink Richard Brodie Michael Broghton +Ammar Brohi Daniel Brotsky Jean Brouwers Gary S. Brown -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 18:45:34 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 17 Jun 2014 18:45:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <3gtFdp5g9dz7Ljn@mail.python.org> http://hg.python.org/cpython/rev/4fc571a04c9c changeset: 91247:4fc571a04c9c parent: 91244:abcf17bc5eae parent: 91246:6ea2894fb983 user: Benjamin Peterson date: Tue Jun 17 09:45:20 2014 -0700 summary: merge 3.4 files: Misc/ACKS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -174,6 +174,7 @@ Tobias Brink Richard Brodie Michael Broghton +Ammar Brohi Daniel Brotsky Jean Brouwers Gary S. Brown -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 21:25:31 2014 From: python-checkins at python.org (ned.deily) Date: Tue, 17 Jun 2014 21:25:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNzg5?= =?utf-8?q?=3A_fix_broken_link_=28reported_by_Jan_Varho=29?= Message-ID: <3gtKBM26ctz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/f80199b009ef changeset: 91248:f80199b009ef branch: 2.7 parent: 91245:23c77732a6a0 user: Ned Deily date: Tue Jun 17 12:24:53 2014 -0700 summary: Issue #21789: fix broken link (reported by Jan Varho) files: Parser/tokenizer.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -534,7 +534,7 @@ "Non-ASCII character '\\x%.2x' " "in file %.200s on line %i, " "but no encoding declared; " - "see http://www.python.org/peps/pep-0263.html for details", + "see http://python.org/dev/peps/pep-0263/ for details", badchar, tok->filename, tok->lineno + 1); PyErr_SetString(PyExc_SyntaxError, buf); return error_ret(tok); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 22:35:55 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 17 Jun 2014 22:35:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNjk0?= =?utf-8?q?=3A_Add_unittest_for_ParenMatch=2E_Patch_by_Saimadhav_Heblikar?= =?utf-8?q?=2E?= Message-ID: <3gtLlb54Zjz7Lkw@mail.python.org> http://hg.python.org/cpython/rev/8a64509b0bd5 changeset: 91249:8a64509b0bd5 branch: 2.7 user: Terry Jan Reedy date: Tue Jun 17 16:35:14 2014 -0400 summary: Issue #21694: Add unittest for ParenMatch. Patch by Saimadhav Heblikar. files: Lib/idlelib/ParenMatch.py | 14 +- Lib/idlelib/idle_test/test_parenmatch.py | 121 +++++++++++ 2 files changed, 131 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/ParenMatch.py b/Lib/idlelib/ParenMatch.py --- a/Lib/idlelib/ParenMatch.py +++ b/Lib/idlelib/ParenMatch.py @@ -90,7 +90,8 @@ self.set_timeout = self.set_timeout_none def flash_paren_event(self, event): - indices = HyperParser(self.editwin, "insert").get_surrounding_brackets() + indices = (HyperParser(self.editwin, "insert") + .get_surrounding_brackets()) if indices is None: self.warn_mismatched() return @@ -167,6 +168,11 @@ # associate a counter with an event; only disable the "paren" # tag if the event is for the most recent timer. self.counter += 1 - self.editwin.text_frame.after(self.FLASH_DELAY, - lambda self=self, c=self.counter: \ - self.handle_restore_timer(c)) + self.editwin.text_frame.after( + self.FLASH_DELAY, + lambda self=self, c=self.counter: self.handle_restore_timer(c)) + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_parenmatch.py b/Lib/idlelib/idle_test/test_parenmatch.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_parenmatch.py @@ -0,0 +1,121 @@ +"""Test idlelib.ParenMatch.""" +# This must currently be a gui test because ParenMatch methods use +# several text methods not defined on idlelib.idle_test.mock_tk.Text. + +import unittest +from test.test_support import requires +from Tkinter import Tk, Text +from idlelib.ParenMatch import ParenMatch + +class Mock: # 2.7 does not have unittest.mock + def __init__(self, *args, **kwargs): + self.called = False + + def __call__(self, *args, **kwargs): + self.called = True + + def reset_mock(self, *args, **kwargs): + self.called = False + + def after(self, *args, **kwargs): + pass + +class DummyEditwin: + def __init__(self, text): + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + + +class ParenMatchTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.text = Text(cls.root) + cls.editwin = DummyEditwin(cls.text) + cls.editwin.text_frame = Mock() + + @classmethod + def tearDownClass(cls): + del cls.text, cls.editwin + cls.root.destroy() + del cls.root + + def tearDown(self): + self.text.delete('1.0', 'end') + + def test_paren_expression(self): + """ + Test ParenMatch with 'expression' style. + """ + text = self.text + pm = ParenMatch(self.editwin) + pm.set_style('expression') + + text.insert('insert', 'def foobar(a, b') + pm.flash_paren_event('event') + self.assertIn('<>', text.event_info()) + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.15')) + text.insert('insert', ')') + pm.restore_event() + self.assertNotIn('<>', text.event_info()) + self.assertEqual(text.tag_prevrange('paren', 'end'), ()) + + # paren_closed_event can only be tested as below + pm.paren_closed_event('event') + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.16')) + + def test_paren_default(self): + """ + Test ParenMatch with 'default' style. + """ + text = self.text + pm = ParenMatch(self.editwin) + pm.set_style('default') + + text.insert('insert', 'def foobar(a, b') + pm.flash_paren_event('event') + self.assertIn('<>', text.event_info()) + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.11')) + text.insert('insert', ')') + pm.restore_event() + self.assertNotIn('<>', text.event_info()) + self.assertEqual(text.tag_prevrange('paren', 'end'), ()) + + def test_paren_corner(self): + """ + Test corner cases in flash_paren_event and paren_closed_event. + + These cases force conditional expression and alternate paths. + """ + text = self.text + pm = ParenMatch(self.editwin) + + text.insert('insert', '# this is a commen)') + self.assertIsNone(pm.paren_closed_event('event')) + + text.insert('insert', '\ndef') + self.assertIsNone(pm.flash_paren_event('event')) + self.assertIsNone(pm.paren_closed_event('event')) + + text.insert('insert', ' a, *arg)') + self.assertIsNone(pm.paren_closed_event('event')) + + def test_handle_restore_timer(self): + pm = ParenMatch(self.editwin) + pm.restore_event = Mock() + pm.handle_restore_timer(0) + self.assertTrue(pm.restore_event.called) + pm.restore_event.reset_mock() + pm.handle_restore_timer(1) + self.assertFalse(pm.restore_event.called) + + +if __name__ == '__main__': + unittest.main(verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 22:35:57 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 17 Jun 2014 22:35:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjk0?= =?utf-8?q?=3A_Add_unittest_for_ParenMatch=2E_Patch_by_Saimadhav_Heblikar?= =?utf-8?q?=2E?= Message-ID: <3gtLld0xqVz7LkS@mail.python.org> http://hg.python.org/cpython/rev/385d4fea9f13 changeset: 91250:385d4fea9f13 branch: 3.4 parent: 91246:6ea2894fb983 user: Terry Jan Reedy date: Tue Jun 17 16:35:20 2014 -0400 summary: Issue #21694: Add unittest for ParenMatch. Patch by Saimadhav Heblikar. files: Lib/idlelib/ParenMatch.py | 14 +- Lib/idlelib/idle_test/test_parenmatch.py | 109 +++++++++++ 2 files changed, 119 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/ParenMatch.py b/Lib/idlelib/ParenMatch.py --- a/Lib/idlelib/ParenMatch.py +++ b/Lib/idlelib/ParenMatch.py @@ -90,7 +90,8 @@ self.set_timeout = self.set_timeout_none def flash_paren_event(self, event): - indices = HyperParser(self.editwin, "insert").get_surrounding_brackets() + indices = (HyperParser(self.editwin, "insert") + .get_surrounding_brackets()) if indices is None: self.warn_mismatched() return @@ -167,6 +168,11 @@ # associate a counter with an event; only disable the "paren" # tag if the event is for the most recent timer. self.counter += 1 - self.editwin.text_frame.after(self.FLASH_DELAY, - lambda self=self, c=self.counter: \ - self.handle_restore_timer(c)) + self.editwin.text_frame.after( + self.FLASH_DELAY, + lambda self=self, c=self.counter: self.handle_restore_timer(c)) + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_parenmatch.py b/Lib/idlelib/idle_test/test_parenmatch.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_parenmatch.py @@ -0,0 +1,109 @@ +"""Test idlelib.ParenMatch.""" +# This must currently be a gui test because ParenMatch methods use +# several text methods not defined on idlelib.idle_test.mock_tk.Text. + +import unittest +from unittest.mock import Mock +from test.support import requires +from tkinter import Tk, Text +from idlelib.ParenMatch import ParenMatch + +class DummyEditwin: + def __init__(self, text): + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + + +class ParenMatchTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.text = Text(cls.root) + cls.editwin = DummyEditwin(cls.text) + cls.editwin.text_frame = Mock() + + @classmethod + def tearDownClass(cls): + del cls.text, cls.editwin + cls.root.destroy() + del cls.root + + def tearDown(self): + self.text.delete('1.0', 'end') + + def test_paren_expression(self): + """ + Test ParenMatch with 'expression' style. + """ + text = self.text + pm = ParenMatch(self.editwin) + pm.set_style('expression') + + text.insert('insert', 'def foobar(a, b') + pm.flash_paren_event('event') + self.assertIn('<>', text.event_info()) + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.15')) + text.insert('insert', ')') + pm.restore_event() + self.assertNotIn('<>', text.event_info()) + self.assertEqual(text.tag_prevrange('paren', 'end'), ()) + + # paren_closed_event can only be tested as below + pm.paren_closed_event('event') + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.16')) + + def test_paren_default(self): + """ + Test ParenMatch with 'default' style. + """ + text = self.text + pm = ParenMatch(self.editwin) + pm.set_style('default') + + text.insert('insert', 'def foobar(a, b') + pm.flash_paren_event('event') + self.assertIn('<>', text.event_info()) + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.11')) + text.insert('insert', ')') + pm.restore_event() + self.assertNotIn('<>', text.event_info()) + self.assertEqual(text.tag_prevrange('paren', 'end'), ()) + + def test_paren_corner(self): + """ + Test corner cases in flash_paren_event and paren_closed_event. + + These cases force conditional expression and alternate paths. + """ + text = self.text + pm = ParenMatch(self.editwin) + + text.insert('insert', '# this is a commen)') + self.assertIsNone(pm.paren_closed_event('event')) + + text.insert('insert', '\ndef') + self.assertIsNone(pm.flash_paren_event('event')) + self.assertIsNone(pm.paren_closed_event('event')) + + text.insert('insert', ' a, *arg)') + self.assertIsNone(pm.paren_closed_event('event')) + + def test_handle_restore_timer(self): + pm = ParenMatch(self.editwin) + pm.restore_event = Mock() + pm.handle_restore_timer(0) + self.assertTrue(pm.restore_event.called) + pm.restore_event.reset_mock() + pm.handle_restore_timer(1) + self.assertFalse(pm.restore_event.called) + + +if __name__ == '__main__': + unittest.main(verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 22:35:58 2014 From: python-checkins at python.org (terry.reedy) Date: Tue, 17 Jun 2014 22:35:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gtLlf3xJ4z7LkH@mail.python.org> http://hg.python.org/cpython/rev/c11d19e8d753 changeset: 91251:c11d19e8d753 parent: 91247:4fc571a04c9c parent: 91250:385d4fea9f13 user: Terry Jan Reedy date: Tue Jun 17 16:35:33 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/ParenMatch.py | 14 +- Lib/idlelib/idle_test/test_parenmatch.py | 109 +++++++++++ 2 files changed, 119 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/ParenMatch.py b/Lib/idlelib/ParenMatch.py --- a/Lib/idlelib/ParenMatch.py +++ b/Lib/idlelib/ParenMatch.py @@ -90,7 +90,8 @@ self.set_timeout = self.set_timeout_none def flash_paren_event(self, event): - indices = HyperParser(self.editwin, "insert").get_surrounding_brackets() + indices = (HyperParser(self.editwin, "insert") + .get_surrounding_brackets()) if indices is None: self.warn_mismatched() return @@ -167,6 +168,11 @@ # associate a counter with an event; only disable the "paren" # tag if the event is for the most recent timer. self.counter += 1 - self.editwin.text_frame.after(self.FLASH_DELAY, - lambda self=self, c=self.counter: \ - self.handle_restore_timer(c)) + self.editwin.text_frame.after( + self.FLASH_DELAY, + lambda self=self, c=self.counter: self.handle_restore_timer(c)) + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_parenmatch.py b/Lib/idlelib/idle_test/test_parenmatch.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_parenmatch.py @@ -0,0 +1,109 @@ +"""Test idlelib.ParenMatch.""" +# This must currently be a gui test because ParenMatch methods use +# several text methods not defined on idlelib.idle_test.mock_tk.Text. + +import unittest +from unittest.mock import Mock +from test.support import requires +from tkinter import Tk, Text +from idlelib.ParenMatch import ParenMatch + +class DummyEditwin: + def __init__(self, text): + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + + +class ParenMatchTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.text = Text(cls.root) + cls.editwin = DummyEditwin(cls.text) + cls.editwin.text_frame = Mock() + + @classmethod + def tearDownClass(cls): + del cls.text, cls.editwin + cls.root.destroy() + del cls.root + + def tearDown(self): + self.text.delete('1.0', 'end') + + def test_paren_expression(self): + """ + Test ParenMatch with 'expression' style. + """ + text = self.text + pm = ParenMatch(self.editwin) + pm.set_style('expression') + + text.insert('insert', 'def foobar(a, b') + pm.flash_paren_event('event') + self.assertIn('<>', text.event_info()) + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.15')) + text.insert('insert', ')') + pm.restore_event() + self.assertNotIn('<>', text.event_info()) + self.assertEqual(text.tag_prevrange('paren', 'end'), ()) + + # paren_closed_event can only be tested as below + pm.paren_closed_event('event') + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.16')) + + def test_paren_default(self): + """ + Test ParenMatch with 'default' style. + """ + text = self.text + pm = ParenMatch(self.editwin) + pm.set_style('default') + + text.insert('insert', 'def foobar(a, b') + pm.flash_paren_event('event') + self.assertIn('<>', text.event_info()) + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.11')) + text.insert('insert', ')') + pm.restore_event() + self.assertNotIn('<>', text.event_info()) + self.assertEqual(text.tag_prevrange('paren', 'end'), ()) + + def test_paren_corner(self): + """ + Test corner cases in flash_paren_event and paren_closed_event. + + These cases force conditional expression and alternate paths. + """ + text = self.text + pm = ParenMatch(self.editwin) + + text.insert('insert', '# this is a commen)') + self.assertIsNone(pm.paren_closed_event('event')) + + text.insert('insert', '\ndef') + self.assertIsNone(pm.flash_paren_event('event')) + self.assertIsNone(pm.paren_closed_event('event')) + + text.insert('insert', ' a, *arg)') + self.assertIsNone(pm.paren_closed_event('event')) + + def test_handle_restore_timer(self): + pm = ParenMatch(self.editwin) + pm.restore_event = Mock() + pm.handle_restore_timer(0) + self.assertTrue(pm.restore_event.called) + pm.restore_event.reset_mock() + pm.handle_restore_timer(1) + self.assertFalse(pm.restore_event.called) + + +if __name__ == '__main__': + unittest.main(verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 23:32:24 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 17 Jun 2014 23:32:24 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2310310=3A_Use_=22u?= =?utf-8?q?nsigned_int_field=3A1=22_instead_of_=22signed_int_field=3A1=22_?= =?utf-8?q?in_a?= Message-ID: <3gtN0m72vRz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/3aeca1fd4c0e changeset: 91252:3aeca1fd4c0e user: Victor Stinner date: Tue Jun 17 23:31:25 2014 +0200 summary: Issue #10310: Use "unsigned int field:1" instead of "signed int field:1" in a private structure of the _io module to fix a compiler warning (overflow when assigning the value 1). Fix also a cast in incrementalnewlinedecoder_setstate(). Patch written by Hallvard B Furuseth. files: Modules/_io/textio.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -224,8 +224,8 @@ PyObject_HEAD PyObject *decoder; PyObject *errors; - signed int pendingcr: 1; - signed int translate: 1; + unsigned int pendingcr: 1; + unsigned int translate: 1; unsigned int seennl: 3; } nldecoder_object; @@ -546,7 +546,7 @@ if (!PyArg_Parse(state, "(OK)", &buffer, &flag)) return NULL; - self->pendingcr = (int) flag & 1; + self->pendingcr = (int) (flag & 1); flag >>= 1; if (self->decoder != Py_None) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 23:41:00 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 17 Jun 2014 23:41:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzIz?= =?utf-8?q?=3A_asyncio=2EQueue=3A_support_any_type_of_number_=28ex=3A_floa?= =?utf-8?q?t=29_for_the?= Message-ID: <3gtNBh1R9Rz7LjR@mail.python.org> http://hg.python.org/cpython/rev/ccfc13183fea changeset: 91253:ccfc13183fea branch: 3.4 parent: 91250:385d4fea9f13 user: Victor Stinner date: Tue Jun 17 23:36:21 2014 +0200 summary: Issue #21723: asyncio.Queue: support any type of number (ex: float) for the maximum size. Patch written by Vajrasky Kok. files: Lib/asyncio/queues.py | 6 +++--- Lib/test/test_asyncio/test_queues.py | 15 +++++++++++++++ Misc/NEWS | 3 +++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Lib/asyncio/queues.py b/Lib/asyncio/queues.py --- a/Lib/asyncio/queues.py +++ b/Lib/asyncio/queues.py @@ -105,7 +105,7 @@ if self._maxsize <= 0: return False else: - return self.qsize() == self._maxsize + return self.qsize() >= self._maxsize @coroutine def put(self, item): @@ -126,7 +126,7 @@ self._put(item) getter.set_result(self._get()) - elif self._maxsize > 0 and self._maxsize == self.qsize(): + elif self._maxsize > 0 and self._maxsize <= self.qsize(): waiter = futures.Future(loop=self._loop) self._putters.append((item, waiter)) @@ -152,7 +152,7 @@ self._put(item) getter.set_result(self._get()) - elif self._maxsize > 0 and self._maxsize == self.qsize(): + elif self._maxsize > 0 and self._maxsize <= self.qsize(): raise QueueFull else: self._put(item) diff --git a/Lib/test/test_asyncio/test_queues.py b/Lib/test/test_asyncio/test_queues.py --- a/Lib/test/test_asyncio/test_queues.py +++ b/Lib/test/test_asyncio/test_queues.py @@ -339,6 +339,21 @@ q.put_nowait(1) self.assertRaises(asyncio.QueueFull, q.put_nowait, 2) + def test_float_maxsize(self): + q = asyncio.Queue(maxsize=1.3, loop=self.loop) + q.put_nowait(1) + q.put_nowait(2) + self.assertTrue(q.full()) + self.assertRaises(asyncio.QueueFull, q.put_nowait, 3) + + q = asyncio.Queue(maxsize=1.3, loop=self.loop) + @asyncio.coroutine + def queue_put(): + yield from q.put(1) + yield from q.put(2) + self.assertTrue(q.full()) + self.loop.run_until_complete(queue_put()) + def test_put_cancelled(self): q = asyncio.Queue(loop=self.loop) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,9 @@ Library ------- +- Issue #21723: asyncio.Queue: support any type of number (ex: float) for the + maximum size. Patch written by Vajrasky Kok. + - Issue #21326: Add a new is_closed() method to asyncio.BaseEventLoop. run_forever() and run_until_complete() methods of asyncio.BaseEventLoop now raise an exception if the event loop was closed. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 17 23:41:01 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 17 Jun 2014 23:41:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogKE1lcmdlIDMuNCkgSXNzdWUgIzIxNzIzOiBhc3luY2lvLlF1ZXVlOiBz?= =?utf-8?q?upport_any_type_of_number_=28ex=3A_float=29?= Message-ID: <3gtNBj3G0Sz7LkJ@mail.python.org> http://hg.python.org/cpython/rev/a2f115bfa513 changeset: 91254:a2f115bfa513 parent: 91252:3aeca1fd4c0e parent: 91253:ccfc13183fea user: Victor Stinner date: Tue Jun 17 23:37:35 2014 +0200 summary: (Merge 3.4) Issue #21723: asyncio.Queue: support any type of number (ex: float) for the maximum size. Patch written by Vajrasky Kok. files: Lib/asyncio/queues.py | 6 +++--- Lib/test/test_asyncio/test_queues.py | 15 +++++++++++++++ Misc/NEWS | 3 +++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Lib/asyncio/queues.py b/Lib/asyncio/queues.py --- a/Lib/asyncio/queues.py +++ b/Lib/asyncio/queues.py @@ -105,7 +105,7 @@ if self._maxsize <= 0: return False else: - return self.qsize() == self._maxsize + return self.qsize() >= self._maxsize @coroutine def put(self, item): @@ -126,7 +126,7 @@ self._put(item) getter.set_result(self._get()) - elif self._maxsize > 0 and self._maxsize == self.qsize(): + elif self._maxsize > 0 and self._maxsize <= self.qsize(): waiter = futures.Future(loop=self._loop) self._putters.append((item, waiter)) @@ -152,7 +152,7 @@ self._put(item) getter.set_result(self._get()) - elif self._maxsize > 0 and self._maxsize == self.qsize(): + elif self._maxsize > 0 and self._maxsize <= self.qsize(): raise QueueFull else: self._put(item) diff --git a/Lib/test/test_asyncio/test_queues.py b/Lib/test/test_asyncio/test_queues.py --- a/Lib/test/test_asyncio/test_queues.py +++ b/Lib/test/test_asyncio/test_queues.py @@ -339,6 +339,21 @@ q.put_nowait(1) self.assertRaises(asyncio.QueueFull, q.put_nowait, 2) + def test_float_maxsize(self): + q = asyncio.Queue(maxsize=1.3, loop=self.loop) + q.put_nowait(1) + q.put_nowait(2) + self.assertTrue(q.full()) + self.assertRaises(asyncio.QueueFull, q.put_nowait, 3) + + q = asyncio.Queue(maxsize=1.3, loop=self.loop) + @asyncio.coroutine + def queue_put(): + yield from q.put(1) + yield from q.put(2) + self.assertTrue(q.full()) + self.loop.run_until_complete(queue_put()) + def test_put_cancelled(self): q = asyncio.Queue(loop=self.loop) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,9 @@ Library ------- +- Issue #21723: asyncio.Queue: support any type of number (ex: float) for the + maximum size. Patch written by Vajrasky Kok. + - Issue #21711: support for "site-python" directories has now been removed from the site module (it was deprecated in 3.4). -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 18 01:16:36 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 18 Jun 2014 01:16:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogU2V0?= =?utf-8?q?_=5F=5Fqualname=5F=5F_attribute_of_CoroWrapper_in_=40coroutine_?= =?utf-8?q?decorator_on?= Message-ID: <3gtQK059Mlz7LjS@mail.python.org> http://hg.python.org/cpython/rev/2a8ad880f7bf changeset: 91255:2a8ad880f7bf branch: 3.4 parent: 91253:ccfc13183fea user: Victor Stinner date: Wed Jun 18 01:14:59 2014 +0200 summary: asyncio: Set __qualname__ attribute of CoroWrapper in @coroutine decorator on Python 3.5 - Drop __slots__ optimization of CoroWrapper to be able to set the __qualname__ attribute. - Add tests on __name__, __qualname__ and __module__ of a coroutine function and coroutine object. - Fix test_tasks when run in debug mode (PYTHONASYNCIODEBUG env var set) on Python 3.3 or 3.4 files: Lib/asyncio/tasks.py | 10 ++- Lib/test/test_asyncio/test_tasks.py | 48 ++++++++++++++-- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -32,12 +32,12 @@ _DEBUG = (not sys.flags.ignore_environment and bool(os.environ.get('PYTHONASYNCIODEBUG'))) +_PY35 = (sys.version_info >= (3, 5)) + class CoroWrapper: # Wrapper for coroutine in _DEBUG mode. - __slots__ = ['gen', 'func', '__name__', '__doc__', '__weakref__'] - def __init__(self, gen, func): assert inspect.isgenerator(gen), gen self.gen = gen @@ -111,8 +111,10 @@ @functools.wraps(func) def wrapper(*args, **kwds): w = CoroWrapper(coro(*args, **kwds), func) - w.__name__ = coro.__name__ - w.__doc__ = coro.__doc__ + w.__name__ = func.__name__ + if _PY35: + w.__qualname__ = func.__qualname__ + w.__doc__ = func.__doc__ return w wrapper._is_coroutine = True # For iscoroutinefunction(). 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 @@ -9,9 +9,13 @@ from test.script_helper import assert_python_ok import asyncio +from asyncio import tasks from asyncio import test_utils +PY35 = (sys.version_info >= (3, 5)) + + @asyncio.coroutine def coroutine_function(): pass @@ -117,10 +121,22 @@ yield from [] return 'abc' + self.assertEqual(notmuch.__name__, 'notmuch') + if PY35: + self.assertEqual(notmuch.__qualname__, + 'TaskTests.test_task_repr..notmuch') + self.assertEqual(notmuch.__module__, __name__) + filename, lineno = test_utils.get_function_source(notmuch) src = "%s:%s" % (filename, lineno) - t = asyncio.Task(notmuch(), loop=self.loop) + gen = notmuch() + self.assertEqual(gen.__name__, 'notmuch') + if PY35: + self.assertEqual(gen.__qualname__, + 'TaskTests.test_task_repr..notmuch') + + t = asyncio.Task(gen, loop=self.loop) t.add_done_callback(Dummy()) self.assertEqual(repr(t), 'Task()' % src) @@ -143,6 +159,12 @@ def notmuch(): pass + self.assertEqual(notmuch.__name__, 'notmuch') + self.assertEqual(notmuch.__module__, __name__) + if PY35: + self.assertEqual(notmuch.__qualname__, + 'TaskTests.test_task_repr_custom..notmuch') + class T(asyncio.Future): def __repr__(self): return 'T[]' @@ -152,16 +174,26 @@ return super().__repr__() gen = notmuch() + if PY35 or tasks._DEBUG: + # On Python >= 3.5, generators now inherit the name of the + # function, as expected, and have a qualified name (__qualname__ + # attribute). In debug mode, @coroutine decorator uses CoroWrapper + # which gets its name (__name__ attribute) from the wrapped + # coroutine function. + coro_name = 'notmuch' + else: + # On Python < 3.5, generators inherit the name of the code, not of + # the function. See: http://bugs.python.org/issue21205 + coro_name = 'coro' + self.assertEqual(gen.__name__, coro_name) + if PY35: + self.assertEqual(gen.__qualname__, + 'TaskTests.test_task_repr_custom..notmuch') + t = MyTask(gen, loop=self.loop) filename = gen.gi_code.co_filename lineno = gen.gi_frame.f_lineno - if sys.version_info >= (3, 5): - name = 'notmuch' - else: - # On Python < 3.5, generators inherit the name of the code, not of - # the function. See: http://bugs.python.org/issue21205 - name = 'coro' - self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (name, filename, lineno)) + self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (coro_name, filename, lineno)) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 18 01:16:38 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 18 Jun 2014 01:16:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogKE1lcmdlIDMuNCkgYXN5bmNpbzogU2V0IF9fcXVhbG5hbWVfXyBhdHRy?= =?utf-8?q?ibute_of_CoroWrapper_in_=40coroutine?= Message-ID: <3gtQK20WWkz7LjS@mail.python.org> http://hg.python.org/cpython/rev/0773c3c69844 changeset: 91256:0773c3c69844 parent: 91254:a2f115bfa513 parent: 91255:2a8ad880f7bf user: Victor Stinner date: Wed Jun 18 01:15:27 2014 +0200 summary: (Merge 3.4) asyncio: Set __qualname__ attribute of CoroWrapper in @coroutine decorator on Python 3.5. - Drop __slots__ optimization of CoroWrapper to be able to set the __qualname__ attribute. - Add tests on __name__, __qualname__ and __module__ of a coroutine function and coroutine object. - Fix test_tasks when run in debug mode (PYTHONASYNCIODEBUG env var set) on Python 3.3 or 3.4 files: Lib/asyncio/tasks.py | 10 ++- Lib/test/test_asyncio/test_tasks.py | 48 ++++++++++++++-- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -32,12 +32,12 @@ _DEBUG = (not sys.flags.ignore_environment and bool(os.environ.get('PYTHONASYNCIODEBUG'))) +_PY35 = (sys.version_info >= (3, 5)) + class CoroWrapper: # Wrapper for coroutine in _DEBUG mode. - __slots__ = ['gen', 'func', '__name__', '__doc__', '__weakref__'] - def __init__(self, gen, func): assert inspect.isgenerator(gen), gen self.gen = gen @@ -111,8 +111,10 @@ @functools.wraps(func) def wrapper(*args, **kwds): w = CoroWrapper(coro(*args, **kwds), func) - w.__name__ = coro.__name__ - w.__doc__ = coro.__doc__ + w.__name__ = func.__name__ + if _PY35: + w.__qualname__ = func.__qualname__ + w.__doc__ = func.__doc__ return w wrapper._is_coroutine = True # For iscoroutinefunction(). 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 @@ -9,9 +9,13 @@ from test.script_helper import assert_python_ok import asyncio +from asyncio import tasks from asyncio import test_utils +PY35 = (sys.version_info >= (3, 5)) + + @asyncio.coroutine def coroutine_function(): pass @@ -117,10 +121,22 @@ yield from [] return 'abc' + self.assertEqual(notmuch.__name__, 'notmuch') + if PY35: + self.assertEqual(notmuch.__qualname__, + 'TaskTests.test_task_repr..notmuch') + self.assertEqual(notmuch.__module__, __name__) + filename, lineno = test_utils.get_function_source(notmuch) src = "%s:%s" % (filename, lineno) - t = asyncio.Task(notmuch(), loop=self.loop) + gen = notmuch() + self.assertEqual(gen.__name__, 'notmuch') + if PY35: + self.assertEqual(gen.__qualname__, + 'TaskTests.test_task_repr..notmuch') + + t = asyncio.Task(gen, loop=self.loop) t.add_done_callback(Dummy()) self.assertEqual(repr(t), 'Task()' % src) @@ -143,6 +159,12 @@ def notmuch(): pass + self.assertEqual(notmuch.__name__, 'notmuch') + self.assertEqual(notmuch.__module__, __name__) + if PY35: + self.assertEqual(notmuch.__qualname__, + 'TaskTests.test_task_repr_custom..notmuch') + class T(asyncio.Future): def __repr__(self): return 'T[]' @@ -152,16 +174,26 @@ return super().__repr__() gen = notmuch() + if PY35 or tasks._DEBUG: + # On Python >= 3.5, generators now inherit the name of the + # function, as expected, and have a qualified name (__qualname__ + # attribute). In debug mode, @coroutine decorator uses CoroWrapper + # which gets its name (__name__ attribute) from the wrapped + # coroutine function. + coro_name = 'notmuch' + else: + # On Python < 3.5, generators inherit the name of the code, not of + # the function. See: http://bugs.python.org/issue21205 + coro_name = 'coro' + self.assertEqual(gen.__name__, coro_name) + if PY35: + self.assertEqual(gen.__qualname__, + 'TaskTests.test_task_repr_custom..notmuch') + t = MyTask(gen, loop=self.loop) filename = gen.gi_code.co_filename lineno = gen.gi_frame.f_lineno - if sys.version_info >= (3, 5): - name = 'notmuch' - else: - # On Python < 3.5, generators inherit the name of the code, not of - # the function. See: http://bugs.python.org/issue21205 - name = 'coro' - self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (name, filename, lineno)) + self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (coro_name, filename, lineno)) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 18 01:23:11 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 18 Jun 2014 01:23:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogUmVm?= =?utf-8?q?actor_test=5F=5Frun=5Fonce=5Flogging=28=29_to_not_rely_on_the_e?= =?utf-8?q?xact_number_of?= Message-ID: <3gtQSb6msjz7LjM@mail.python.org> http://hg.python.org/cpython/rev/86f4a3a13fa7 changeset: 91257:86f4a3a13fa7 branch: 3.4 parent: 91255:2a8ad880f7bf user: Victor Stinner date: Wed Jun 18 01:22:15 2014 +0200 summary: asyncio: Refactor test__run_once_logging() to not rely on the exact number of calls to time.monotonic(). Use a "fast select" and a "slow select" instead. files: Lib/test/test_asyncio/test_base_events.py | 29 ++++------ 1 files changed, 11 insertions(+), 18 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 @@ -240,30 +240,23 @@ self.loop.set_debug(False) self.assertFalse(self.loop.get_debug()) - @mock.patch('asyncio.base_events.time') @mock.patch('asyncio.base_events.logger') - def test__run_once_logging(self, m_logger, m_time): + def test__run_once_logging(self, m_logger): + def slow_select(timeout): + time.sleep(1.0) + return [] + # Log to INFO level if timeout > 1.0 sec. - idx = -1 - data = [10.0, 10.0, 12.0, 13.0] - - def monotonic(): - nonlocal data, idx - idx += 1 - return data[idx] - - m_time.monotonic = monotonic - - self.loop._scheduled.append( - asyncio.TimerHandle(11.0, lambda: True, (), self.loop)) + self.loop._selector.select = slow_select self.loop._process_events = mock.Mock() self.loop._run_once() 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)] + def fast_select(timeout): + time.sleep(0.001) + return [] + + self.loop._selector.select = fast_select self.loop._run_once() self.assertEqual(logging.DEBUG, m_logger.log.call_args[0][0]) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 18 01:23:13 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 18 Jun 2014 01:23:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogKE1lcmdlIDMuNCkgYXN5bmNpbzogUmVmYWN0b3IgdGVzdF9fcnVuX29u?= =?utf-8?q?ce=5Flogging=28=29_to_not_rely_on_the_exact?= Message-ID: <3gtQSd13JLz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/538b5538eac2 changeset: 91258:538b5538eac2 parent: 91256:0773c3c69844 parent: 91257:86f4a3a13fa7 user: Victor Stinner date: Wed Jun 18 01:22:31 2014 +0200 summary: (Merge 3.4) asyncio: Refactor test__run_once_logging() to not rely on the exact number of calls to time.monotonic(). Use a "fast select" and a "slow select" instead. files: Lib/test/test_asyncio/test_base_events.py | 29 ++++------ 1 files changed, 11 insertions(+), 18 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 @@ -240,30 +240,23 @@ self.loop.set_debug(False) self.assertFalse(self.loop.get_debug()) - @mock.patch('asyncio.base_events.time') @mock.patch('asyncio.base_events.logger') - def test__run_once_logging(self, m_logger, m_time): + def test__run_once_logging(self, m_logger): + def slow_select(timeout): + time.sleep(1.0) + return [] + # Log to INFO level if timeout > 1.0 sec. - idx = -1 - data = [10.0, 10.0, 12.0, 13.0] - - def monotonic(): - nonlocal data, idx - idx += 1 - return data[idx] - - m_time.monotonic = monotonic - - self.loop._scheduled.append( - asyncio.TimerHandle(11.0, lambda: True, (), self.loop)) + self.loop._selector.select = slow_select self.loop._process_events = mock.Mock() self.loop._run_once() 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)] + def fast_select(timeout): + time.sleep(0.001) + return [] + + self.loop._selector.select = fast_select self.loop._run_once() self.assertEqual(logging.DEBUG, m_logger.log.call_args[0][0]) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 18 01:38:13 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 18 Jun 2014 01:38:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogUmVm?= =?utf-8?q?actor_tests=3A_add_a_base_TestCase_class?= Message-ID: <3gtQnx3fh6z7LjM@mail.python.org> http://hg.python.org/cpython/rev/4d335cf631da changeset: 91259:4d335cf631da branch: 3.4 parent: 91257:86f4a3a13fa7 user: Victor Stinner date: Wed Jun 18 01:36:32 2014 +0200 summary: asyncio: Refactor tests: add a base TestCase class files: Lib/asyncio/test_utils.py | 18 + Lib/test/test_asyncio/test_base_events.py | 11 +- Lib/test/test_asyncio/test_events.py | 14 +- Lib/test/test_asyncio/test_futures.py | 25 +- Lib/test/test_asyncio/test_locks.py | 68 +---- Lib/test/test_asyncio/test_proactor_events.py | 7 +- Lib/test/test_asyncio/test_queues.py | 32 +-- Lib/test/test_asyncio/test_selector_events.py | 21 +- Lib/test/test_asyncio/test_streams.py | 5 +- Lib/test/test_asyncio/test_subprocess.py | 10 +- Lib/test/test_asyncio/test_tasks.py | 105 +++------ Lib/test/test_asyncio/test_unix_events.py | 40 +-- Lib/test/test_asyncio/test_windows_events.py | 8 +- 13 files changed, 145 insertions(+), 219 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 @@ -11,6 +11,7 @@ import tempfile import threading import time +import unittest from unittest import mock from http.server import HTTPServer @@ -379,3 +380,20 @@ if source is None: raise ValueError("unable to get the source of %r" % (func,)) return source + + +class TestCase(unittest.TestCase): + def set_event_loop(self, loop, *, cleanup=True): + assert loop is not None + # ensure that the event loop is passed explicitly in asyncio + events.set_event_loop(None) + if cleanup: + self.addCleanup(loop.close) + + def new_test_loop(self, gen=None): + loop = TestLoop(gen) + self.set_event_loop(loop) + return loop + + def tearDown(self): + events.set_event_loop(None) 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 @@ -19,12 +19,12 @@ PY34 = sys.version_info >= (3, 4) -class BaseEventLoopTests(unittest.TestCase): +class BaseEventLoopTests(test_utils.TestCase): def setUp(self): self.loop = base_events.BaseEventLoop() self.loop._selector = mock.Mock() - asyncio.set_event_loop(None) + self.set_event_loop(self.loop) def test_not_implemented(self): m = mock.Mock() @@ -548,14 +548,11 @@ self.done.set_result(None) -class BaseEventLoopWithSelectorTests(unittest.TestCase): +class BaseEventLoopWithSelectorTests(test_utils.TestCase): def setUp(self): self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.set_event_loop(self.loop) @mock.patch('asyncio.base_events.socket') def test_create_connection_multiple_errors(self, m_socket): 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 @@ -224,7 +224,7 @@ def setUp(self): super().setUp() self.loop = self.create_event_loop() - asyncio.set_event_loop(None) + self.set_event_loop(self.loop) def tearDown(self): # just in case if we have transport close callbacks @@ -1629,14 +1629,14 @@ if sys.platform == 'win32': - class SelectEventLoopTests(EventLoopTestsMixin, unittest.TestCase): + class SelectEventLoopTests(EventLoopTestsMixin, test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop() class ProactorEventLoopTests(EventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.ProactorEventLoop() @@ -1691,7 +1691,7 @@ if hasattr(selectors, 'KqueueSelector'): class KqueueEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop( @@ -1716,7 +1716,7 @@ if hasattr(selectors, 'EpollSelector'): class EPollEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop(selectors.EpollSelector()) @@ -1724,7 +1724,7 @@ if hasattr(selectors, 'PollSelector'): class PollEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop(selectors.PollSelector()) @@ -1732,7 +1732,7 @@ # Should always exist. class SelectEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop(selectors.SelectSelector()) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -13,14 +13,10 @@ return f -class FutureTests(unittest.TestCase): +class FutureTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_initial_state(self): f = asyncio.Future(loop=self.loop) @@ -30,12 +26,9 @@ self.assertTrue(f.cancelled()) def test_init_constructor_default_loop(self): - try: - asyncio.set_event_loop(self.loop) - f = asyncio.Future() - self.assertIs(f._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + f = asyncio.Future() + self.assertIs(f._loop, self.loop) def test_constructor_positional(self): # Make sure Future doesn't accept a positional argument @@ -264,14 +257,10 @@ self.assertTrue(f2.cancelled()) -class FutureDoneCallbackTests(unittest.TestCase): +class FutureDoneCallbackTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def run_briefly(self): test_utils.run_briefly(self.loop) diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -17,14 +17,10 @@ RGX_REPR = re.compile(STR_RGX_REPR) -class LockTests(unittest.TestCase): +class LockTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -35,12 +31,9 @@ self.assertIs(lock._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - lock = asyncio.Lock() - self.assertIs(lock._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + lock = asyncio.Lock() + self.assertIs(lock._loop, self.loop) def test_repr(self): lock = asyncio.Lock(loop=self.loop) @@ -240,14 +233,10 @@ self.assertFalse(lock.locked()) -class EventTests(unittest.TestCase): +class EventTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -258,12 +247,9 @@ self.assertIs(ev._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - ev = asyncio.Event() - self.assertIs(ev._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + ev = asyncio.Event() + self.assertIs(ev._loop, self.loop) def test_repr(self): ev = asyncio.Event(loop=self.loop) @@ -376,14 +362,10 @@ self.assertTrue(t.result()) -class ConditionTests(unittest.TestCase): +class ConditionTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -394,12 +376,9 @@ self.assertIs(cond._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - cond = asyncio.Condition() - self.assertIs(cond._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + cond = asyncio.Condition() + self.assertIs(cond._loop, self.loop) def test_wait(self): cond = asyncio.Condition(loop=self.loop) @@ -678,14 +657,10 @@ self.assertFalse(cond.locked()) -class SemaphoreTests(unittest.TestCase): +class SemaphoreTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -696,12 +671,9 @@ self.assertIs(sem._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - sem = asyncio.Semaphore() - self.assertIs(sem._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + sem = asyncio.Semaphore() + self.assertIs(sem._loop, self.loop) def test_initial_value_zero(self): sem = asyncio.Semaphore(0, loop=self.loop) 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 @@ -12,10 +12,10 @@ from asyncio import test_utils -class ProactorSocketTransportTests(unittest.TestCase): +class ProactorSocketTransportTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() + self.loop = self.new_test_loop() self.proactor = mock.Mock() self.loop._proactor = self.proactor self.protocol = test_utils.make_test_protocol(asyncio.Protocol) @@ -343,7 +343,7 @@ tr.close() -class BaseProactorEventLoopTests(unittest.TestCase): +class BaseProactorEventLoopTests(test_utils.TestCase): def setUp(self): self.sock = mock.Mock(socket.socket) @@ -356,6 +356,7 @@ return (self.ssock, self.csock) self.loop = EventLoop(self.proactor) + self.set_event_loop(self.loop, cleanup=False) @mock.patch.object(BaseProactorEventLoop, 'call_soon') @mock.patch.object(BaseProactorEventLoop, '_socketpair') diff --git a/Lib/test/test_asyncio/test_queues.py b/Lib/test/test_asyncio/test_queues.py --- a/Lib/test/test_asyncio/test_queues.py +++ b/Lib/test/test_asyncio/test_queues.py @@ -7,14 +7,10 @@ from asyncio import test_utils -class _QueueTestBase(unittest.TestCase): +class _QueueTestBase(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() class QueueBasicTests(_QueueTestBase): @@ -32,8 +28,7 @@ self.assertAlmostEqual(0.2, when) yield 0.1 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) q = asyncio.Queue(loop=loop) self.assertTrue(fn(q).startswith(' http://hg.python.org/cpython/rev/f574c7b18279 changeset: 91260:f574c7b18279 parent: 91258:538b5538eac2 parent: 91259:4d335cf631da user: Victor Stinner date: Wed Jun 18 01:37:31 2014 +0200 summary: (Merge 3.4) asyncio: Refactor tests: add a base TestCase class files: Lib/asyncio/test_utils.py | 18 + Lib/test/test_asyncio/test_base_events.py | 11 +- Lib/test/test_asyncio/test_events.py | 14 +- Lib/test/test_asyncio/test_futures.py | 25 +- Lib/test/test_asyncio/test_locks.py | 68 +---- Lib/test/test_asyncio/test_proactor_events.py | 7 +- Lib/test/test_asyncio/test_queues.py | 32 +-- Lib/test/test_asyncio/test_selector_events.py | 21 +- Lib/test/test_asyncio/test_streams.py | 5 +- Lib/test/test_asyncio/test_subprocess.py | 10 +- Lib/test/test_asyncio/test_tasks.py | 105 +++------ Lib/test/test_asyncio/test_unix_events.py | 40 +-- Lib/test/test_asyncio/test_windows_events.py | 8 +- 13 files changed, 145 insertions(+), 219 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 @@ -11,6 +11,7 @@ import tempfile import threading import time +import unittest from unittest import mock from http.server import HTTPServer @@ -379,3 +380,20 @@ if source is None: raise ValueError("unable to get the source of %r" % (func,)) return source + + +class TestCase(unittest.TestCase): + def set_event_loop(self, loop, *, cleanup=True): + assert loop is not None + # ensure that the event loop is passed explicitly in asyncio + events.set_event_loop(None) + if cleanup: + self.addCleanup(loop.close) + + def new_test_loop(self, gen=None): + loop = TestLoop(gen) + self.set_event_loop(loop) + return loop + + def tearDown(self): + events.set_event_loop(None) 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 @@ -19,12 +19,12 @@ PY34 = sys.version_info >= (3, 4) -class BaseEventLoopTests(unittest.TestCase): +class BaseEventLoopTests(test_utils.TestCase): def setUp(self): self.loop = base_events.BaseEventLoop() self.loop._selector = mock.Mock() - asyncio.set_event_loop(None) + self.set_event_loop(self.loop) def test_not_implemented(self): m = mock.Mock() @@ -548,14 +548,11 @@ self.done.set_result(None) -class BaseEventLoopWithSelectorTests(unittest.TestCase): +class BaseEventLoopWithSelectorTests(test_utils.TestCase): def setUp(self): self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.set_event_loop(self.loop) @mock.patch('asyncio.base_events.socket') def test_create_connection_multiple_errors(self, m_socket): 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 @@ -224,7 +224,7 @@ def setUp(self): super().setUp() self.loop = self.create_event_loop() - asyncio.set_event_loop(None) + self.set_event_loop(self.loop) def tearDown(self): # just in case if we have transport close callbacks @@ -1629,14 +1629,14 @@ if sys.platform == 'win32': - class SelectEventLoopTests(EventLoopTestsMixin, unittest.TestCase): + class SelectEventLoopTests(EventLoopTestsMixin, test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop() class ProactorEventLoopTests(EventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.ProactorEventLoop() @@ -1691,7 +1691,7 @@ if hasattr(selectors, 'KqueueSelector'): class KqueueEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop( @@ -1716,7 +1716,7 @@ if hasattr(selectors, 'EpollSelector'): class EPollEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop(selectors.EpollSelector()) @@ -1724,7 +1724,7 @@ if hasattr(selectors, 'PollSelector'): class PollEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop(selectors.PollSelector()) @@ -1732,7 +1732,7 @@ # Should always exist. class SelectEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop(selectors.SelectSelector()) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -13,14 +13,10 @@ return f -class FutureTests(unittest.TestCase): +class FutureTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_initial_state(self): f = asyncio.Future(loop=self.loop) @@ -30,12 +26,9 @@ self.assertTrue(f.cancelled()) def test_init_constructor_default_loop(self): - try: - asyncio.set_event_loop(self.loop) - f = asyncio.Future() - self.assertIs(f._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + f = asyncio.Future() + self.assertIs(f._loop, self.loop) def test_constructor_positional(self): # Make sure Future doesn't accept a positional argument @@ -264,14 +257,10 @@ self.assertTrue(f2.cancelled()) -class FutureDoneCallbackTests(unittest.TestCase): +class FutureDoneCallbackTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def run_briefly(self): test_utils.run_briefly(self.loop) diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -17,14 +17,10 @@ RGX_REPR = re.compile(STR_RGX_REPR) -class LockTests(unittest.TestCase): +class LockTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -35,12 +31,9 @@ self.assertIs(lock._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - lock = asyncio.Lock() - self.assertIs(lock._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + lock = asyncio.Lock() + self.assertIs(lock._loop, self.loop) def test_repr(self): lock = asyncio.Lock(loop=self.loop) @@ -240,14 +233,10 @@ self.assertFalse(lock.locked()) -class EventTests(unittest.TestCase): +class EventTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -258,12 +247,9 @@ self.assertIs(ev._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - ev = asyncio.Event() - self.assertIs(ev._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + ev = asyncio.Event() + self.assertIs(ev._loop, self.loop) def test_repr(self): ev = asyncio.Event(loop=self.loop) @@ -376,14 +362,10 @@ self.assertTrue(t.result()) -class ConditionTests(unittest.TestCase): +class ConditionTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -394,12 +376,9 @@ self.assertIs(cond._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - cond = asyncio.Condition() - self.assertIs(cond._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + cond = asyncio.Condition() + self.assertIs(cond._loop, self.loop) def test_wait(self): cond = asyncio.Condition(loop=self.loop) @@ -678,14 +657,10 @@ self.assertFalse(cond.locked()) -class SemaphoreTests(unittest.TestCase): +class SemaphoreTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -696,12 +671,9 @@ self.assertIs(sem._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - sem = asyncio.Semaphore() - self.assertIs(sem._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + sem = asyncio.Semaphore() + self.assertIs(sem._loop, self.loop) def test_initial_value_zero(self): sem = asyncio.Semaphore(0, loop=self.loop) 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 @@ -12,10 +12,10 @@ from asyncio import test_utils -class ProactorSocketTransportTests(unittest.TestCase): +class ProactorSocketTransportTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() + self.loop = self.new_test_loop() self.proactor = mock.Mock() self.loop._proactor = self.proactor self.protocol = test_utils.make_test_protocol(asyncio.Protocol) @@ -343,7 +343,7 @@ tr.close() -class BaseProactorEventLoopTests(unittest.TestCase): +class BaseProactorEventLoopTests(test_utils.TestCase): def setUp(self): self.sock = mock.Mock(socket.socket) @@ -356,6 +356,7 @@ return (self.ssock, self.csock) self.loop = EventLoop(self.proactor) + self.set_event_loop(self.loop, cleanup=False) @mock.patch.object(BaseProactorEventLoop, 'call_soon') @mock.patch.object(BaseProactorEventLoop, '_socketpair') diff --git a/Lib/test/test_asyncio/test_queues.py b/Lib/test/test_asyncio/test_queues.py --- a/Lib/test/test_asyncio/test_queues.py +++ b/Lib/test/test_asyncio/test_queues.py @@ -7,14 +7,10 @@ from asyncio import test_utils -class _QueueTestBase(unittest.TestCase): +class _QueueTestBase(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() class QueueBasicTests(_QueueTestBase): @@ -32,8 +28,7 @@ self.assertAlmostEqual(0.2, when) yield 0.1 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) q = asyncio.Queue(loop=loop) self.assertTrue(fn(q).startswith(' http://hg.python.org/cpython/rev/a1e09a563ac4 changeset: 91261:a1e09a563ac4 branch: 3.4 parent: 91259:4d335cf631da user: Victor Stinner date: Wed Jun 18 03:25:23 2014 +0200 summary: asyncio: Fix pyflakes errors - Add a missing import - Remove an unused import - Remove unused variables files: Doc/library/asyncio-eventloop.rst | 4 ++++ Lib/test/test_asyncio/test_selector_events.py | 5 +---- Lib/test/test_asyncio/test_tasks.py | 1 - Lib/test/test_asyncio/test_windows_events.py | 1 + 4 files changed, 6 insertions(+), 5 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 @@ -588,10 +588,14 @@ Get the debug mode (:class:`bool`) of the event loop, ``False`` by default. + .. versionadded:: 3.4.2 + .. method:: BaseEventLoop.set_debug(enabled: bool) Set the debug mode of the event loop. + .. versionadded:: 3.4.2 + .. seealso:: The :ref:`Develop with asyncio ` section. 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 @@ -108,10 +108,7 @@ self.assertRaises(RuntimeError, self.loop.add_writer, fd, callback) def test_close_no_selector(self): - ssock = self.loop._ssock - csock = self.loop._csock - remove_reader = self.loop.remove_reader = mock.Mock() - + self.loop.remove_reader = mock.Mock() self.loop._selector.close() self.loop._selector = None self.loop.close() 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 @@ -1,6 +1,5 @@ """Tests for tasks.py.""" -import gc import os.path import sys import types 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 @@ -9,6 +9,7 @@ import asyncio from asyncio import _overlapped +from asyncio import test_utils from asyncio import windows_events -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 18 03:28:27 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 18 Jun 2014 03:28:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Fix_pyflakes_errors?= Message-ID: <3gtTF7085dz7LjS@mail.python.org> http://hg.python.org/cpython/rev/c9bee963557b changeset: 91262:c9bee963557b parent: 91260:f574c7b18279 parent: 91261:a1e09a563ac4 user: Victor Stinner date: Wed Jun 18 03:27:28 2014 +0200 summary: (Merge 3.4) asyncio: Fix pyflakes errors - Add a missing import - Remove an unused import - Remove unused variables files: Doc/library/asyncio-eventloop.rst | 4 ++++ Lib/test/test_asyncio/test_selector_events.py | 5 +---- Lib/test/test_asyncio/test_tasks.py | 1 - Lib/test/test_asyncio/test_windows_events.py | 1 + 4 files changed, 6 insertions(+), 5 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 @@ -588,10 +588,14 @@ Get the debug mode (:class:`bool`) of the event loop, ``False`` by default. + .. versionadded:: 3.4.2 + .. method:: BaseEventLoop.set_debug(enabled: bool) Set the debug mode of the event loop. + .. versionadded:: 3.4.2 + .. seealso:: The :ref:`Develop with asyncio ` section. 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 @@ -108,10 +108,7 @@ self.assertRaises(RuntimeError, self.loop.add_writer, fd, callback) def test_close_no_selector(self): - ssock = self.loop._ssock - csock = self.loop._csock - remove_reader = self.loop.remove_reader = mock.Mock() - + self.loop.remove_reader = mock.Mock() self.loop._selector.close() self.loop._selector = None self.loop.close() 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 @@ -1,6 +1,5 @@ """Tests for tasks.py.""" -import gc import os.path import sys import types 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 @@ -9,6 +9,7 @@ import asyncio from asyncio import _overlapped +from asyncio import test_utils from asyncio import windows_events -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 18 05:27:56 2014 From: python-checkins at python.org (zach.ware) Date: Wed, 18 Jun 2014 05:27:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fix_typo_point?= =?utf-8?q?ed_out_by_cocoatomo_on_docs=40?= Message-ID: <3gtWv06Jtrz7LjS@mail.python.org> http://hg.python.org/cpython/rev/4cf322536328 changeset: 91263:4cf322536328 branch: 3.4 parent: 91261:a1e09a563ac4 user: Zachary Ware date: Tue Jun 17 22:26:59 2014 -0500 summary: Fix typo pointed out by cocoatomo on docs@ files: Doc/library/ossaudiodev.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/ossaudiodev.rst b/Doc/library/ossaudiodev.rst --- a/Doc/library/ossaudiodev.rst +++ b/Doc/library/ossaudiodev.rst @@ -407,7 +407,7 @@ (silent) to 100 (full volume). If the control is monophonic, a 2-tuple is still returned, but both volumes are the same. - Raises :exc:`OSSAudioError` if an invalid control was is specified, or + Raises :exc:`OSSAudioError` if an invalid control is specified, or :exc:`OSError` if an unsupported control is specified. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 18 05:27:58 2014 From: python-checkins at python.org (zach.ware) Date: Wed, 18 Jun 2014 05:27:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_typo_fix?= Message-ID: <3gtWv20pczz7LjS@mail.python.org> http://hg.python.org/cpython/rev/1e74350dd056 changeset: 91264:1e74350dd056 parent: 91262:c9bee963557b parent: 91263:4cf322536328 user: Zachary Ware date: Tue Jun 17 22:27:46 2014 -0500 summary: Merge typo fix files: Doc/library/ossaudiodev.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/ossaudiodev.rst b/Doc/library/ossaudiodev.rst --- a/Doc/library/ossaudiodev.rst +++ b/Doc/library/ossaudiodev.rst @@ -407,7 +407,7 @@ (silent) to 100 (full volume). If the control is monophonic, a 2-tuple is still returned, but both volumes are the same. - Raises :exc:`OSSAudioError` if an invalid control was is specified, or + Raises :exc:`OSSAudioError` if an invalid control is specified, or :exc:`OSError` if an unsupported control is specified. -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Wed Jun 18 10:58:05 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 18 Jun 2014 10:58:05 +0200 Subject: [Python-checkins] Daily reference leaks (c9bee963557b): sum=61 Message-ID: results for c9bee963557b on branch "default" -------------------------------------------- test_collections leaked [-2, 2, 0] references, sum=0 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_multiprocessing_forkserver leaked [0, 38, 0] references, sum=38 test_multiprocessing_forkserver leaked [0, 18, 0] memory blocks, sum=18 test_site leaked [-2, 0, 0] references, sum=-2 test_site leaked [-2, 0, 0] memory blocks, sum=-2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogamBZnY', '-x'] From root at python.org Wed Jun 18 13:15:23 2014 From: root at python.org (Cron Daemon) Date: Wed, 18 Jun 2014 13:15:23 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From python-checkins at python.org Wed Jun 18 22:11:48 2014 From: python-checkins at python.org (ned.deily) Date: Wed, 18 Jun 2014 22:11:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzM0ODU6?= =?utf-8?q?_remove_misleading_comment?= Message-ID: <3gty9J6y8nz7LjP@mail.python.org> http://hg.python.org/cpython/rev/a854d23305de changeset: 91265:a854d23305de branch: 3.4 parent: 91263:4cf322536328 user: Ned Deily date: Wed Jun 18 13:09:40 2014 -0700 summary: Issue #3485: remove misleading comment files: Lib/posixpath.py | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Lib/posixpath.py b/Lib/posixpath.py --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -48,7 +48,6 @@ def normcase(s): """Normalize case of pathname. Has no effect under Posix""" - # TODO: on Mac OS X, this should really return s.lower(). if not isinstance(s, (bytes, str)): raise TypeError("normcase() argument must be str or bytes, " "not '{}'".format(s.__class__.__name__)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 18 22:11:50 2014 From: python-checkins at python.org (ned.deily) Date: Wed, 18 Jun 2014 22:11:50 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=233485=3A_merge_from_3=2E4?= Message-ID: <3gty9L1SW4z7Lkb@mail.python.org> http://hg.python.org/cpython/rev/3edda677119e changeset: 91266:3edda677119e parent: 91264:1e74350dd056 parent: 91265:a854d23305de user: Ned Deily date: Wed Jun 18 13:10:44 2014 -0700 summary: Issue #3485: merge from 3.4 files: Lib/posixpath.py | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Lib/posixpath.py b/Lib/posixpath.py --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -48,7 +48,6 @@ def normcase(s): """Normalize case of pathname. Has no effect under Posix""" - # TODO: on Mac OS X, this should really return s.lower(). if not isinstance(s, (bytes, str)): raise TypeError("normcase() argument must be str or bytes, " "not '{}'".format(s.__class__.__name__)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 05:09:27 2014 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 19 Jun 2014 05:09:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzIy?= =?utf-8?q?=3A_The_distutils_=22upload=22_command_now_exits_with_a_non-zer?= =?utf-8?q?o_return?= Message-ID: <3gv7RC1xswz7Ljv@mail.python.org> http://hg.python.org/cpython/rev/d86214c98a9c changeset: 91267:d86214c98a9c branch: 3.4 parent: 91265:a854d23305de user: Antoine Pitrou date: Wed Jun 18 23:07:46 2014 -0400 summary: Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. files: Lib/distutils/command/upload.py | 15 ++++++++------- Lib/distutils/tests/test_upload.py | 16 ++++++++++++---- Misc/ACKS | 1 + Misc/NEWS | 3 +++ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py --- a/Lib/distutils/command/upload.py +++ b/Lib/distutils/command/upload.py @@ -2,10 +2,6 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" -from distutils.errors import * -from distutils.core import PyPIRCCommand -from distutils.spawn import spawn -from distutils import log import sys import os, io import socket @@ -13,6 +9,10 @@ from base64 import standard_b64encode from urllib.request import urlopen, Request, HTTPError from urllib.parse import urlparse +from distutils.errors import DistutilsError, DistutilsOptionError +from distutils.core import PyPIRCCommand +from distutils.spawn import spawn +from distutils import log # this keeps compatibility for 2.3 and 2.4 if sys.version < "2.5": @@ -184,7 +184,7 @@ reason = result.msg except OSError as e: self.announce(str(e), log.ERROR) - return + raise except HTTPError as e: status = e.code reason = e.msg @@ -193,8 +193,9 @@ self.announce('Server response (%s): %s' % (status, reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (status, reason), - log.ERROR) + msg = 'Upload failed (%s): %s' % (status, reason) + self.announce(msg, log.ERROR) + raise DistutilsError(msg) if self.show_response: text = self._read_pypi_response(result) msg = '\n'.join(('-' * 75, text, '-' * 75)) diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py --- a/Lib/distutils/tests/test_upload.py +++ b/Lib/distutils/tests/test_upload.py @@ -6,6 +6,7 @@ from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution +from distutils.errors import DistutilsError from distutils.log import INFO from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase @@ -41,13 +42,14 @@ class FakeOpen(object): - def __init__(self, url): + def __init__(self, url, msg=None, code=None): self.url = url if not isinstance(url, str): self.req = url else: self.req = None - self.msg = 'OK' + self.msg = msg or 'OK' + self.code = code or 200 def getheader(self, name, default=None): return { @@ -58,7 +60,7 @@ return b'xyzzy' def getcode(self): - return 200 + return self.code class uploadTestCase(PyPIRCCommandTestCase): @@ -68,13 +70,15 @@ self.old_open = upload_mod.urlopen upload_mod.urlopen = self._urlopen self.last_open = None + self.next_msg = None + self.next_code = None def tearDown(self): upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() def _urlopen(self, url): - self.last_open = FakeOpen(url) + self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) return self.last_open def test_finalize_options(self): @@ -135,6 +139,10 @@ results = self.get_logs(INFO) self.assertIn('xyzzy\n', results[-1]) + def test_upload_fails(self): + self.next_msg = "Not Found" + self.next_code = 404 + self.assertRaises(DistutilsError, self.test_upload) def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -308,6 +308,7 @@ Arnaud Delobelle Konrad Delong Erik Demaine +Martin Dengler John Dennis L. Peter Deutsch Roger Dev diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,9 @@ Library ------- +- Issue #21722: The distutils "upload" command now exits with a non-zero + return code when uploading fails. Patch by Martin Dengler. + - Issue #21723: asyncio.Queue: support any type of number (ex: float) for the maximum size. Patch written by Vajrasky Kok. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 05:09:28 2014 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 19 Jun 2014 05:09:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321722=3A_The_distutils_=22upload=22_command_now?= =?utf-8?q?_exits_with_a_non-zero_return?= Message-ID: <3gv7RD4t6zz7LkG@mail.python.org> http://hg.python.org/cpython/rev/979aaa08c067 changeset: 91268:979aaa08c067 parent: 91266:3edda677119e parent: 91267:d86214c98a9c user: Antoine Pitrou date: Wed Jun 18 23:09:11 2014 -0400 summary: Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. files: Lib/distutils/command/upload.py | 9 +++++---- Lib/distutils/tests/test_upload.py | 16 ++++++++++++---- Misc/ACKS | 1 + Misc/NEWS | 3 +++ 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py --- a/Lib/distutils/command/upload.py +++ b/Lib/distutils/command/upload.py @@ -12,7 +12,7 @@ from base64 import standard_b64encode from urllib.request import urlopen, Request, HTTPError from urllib.parse import urlparse -from distutils.errors import DistutilsOptionError +from distutils.errors import DistutilsError, DistutilsOptionError from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log @@ -184,7 +184,7 @@ reason = result.msg except OSError as e: self.announce(str(e), log.ERROR) - return + raise except HTTPError as e: status = e.code reason = e.msg @@ -193,8 +193,9 @@ self.announce('Server response (%s): %s' % (status, reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (status, reason), - log.ERROR) + msg = 'Upload failed (%s): %s' % (status, reason) + self.announce(msg, log.ERROR) + raise DistutilsError(msg) if self.show_response: text = self._read_pypi_response(result) msg = '\n'.join(('-' * 75, text, '-' * 75)) diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py --- a/Lib/distutils/tests/test_upload.py +++ b/Lib/distutils/tests/test_upload.py @@ -6,6 +6,7 @@ from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution +from distutils.errors import DistutilsError from distutils.log import INFO from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase @@ -41,13 +42,14 @@ class FakeOpen(object): - def __init__(self, url): + def __init__(self, url, msg=None, code=None): self.url = url if not isinstance(url, str): self.req = url else: self.req = None - self.msg = 'OK' + self.msg = msg or 'OK' + self.code = code or 200 def getheader(self, name, default=None): return { @@ -58,7 +60,7 @@ return b'xyzzy' def getcode(self): - return 200 + return self.code class uploadTestCase(PyPIRCCommandTestCase): @@ -68,13 +70,15 @@ self.old_open = upload_mod.urlopen upload_mod.urlopen = self._urlopen self.last_open = None + self.next_msg = None + self.next_code = None def tearDown(self): upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() def _urlopen(self, url): - self.last_open = FakeOpen(url) + self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) return self.last_open def test_finalize_options(self): @@ -135,6 +139,10 @@ results = self.get_logs(INFO) self.assertIn('xyzzy\n', results[-1]) + def test_upload_fails(self): + self.next_msg = "Not Found" + self.next_code = 404 + self.assertRaises(DistutilsError, self.test_upload) def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -309,6 +309,7 @@ Arnaud Delobelle Konrad Delong Erik Demaine +Martin Dengler John Dennis L. Peter Deutsch Roger Dev diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,9 @@ Library ------- +- Issue #21722: The distutils "upload" command now exits with a non-zero + return code when uploading fails. Patch by Martin Dengler. + - Issue #21723: asyncio.Queue: support any type of number (ex: float) for the maximum size. Patch written by Vajrasky Kok. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 05:15:07 2014 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 19 Jun 2014 05:15:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNzIy?= =?utf-8?q?=3A_The_distutils_=22upload=22_command_now_exits_with_a_non-zer?= =?utf-8?q?o_return?= Message-ID: <3gv7Yl0RJRz7LkB@mail.python.org> http://hg.python.org/cpython/rev/cf70f030a744 changeset: 91269:cf70f030a744 branch: 2.7 parent: 91249:8a64509b0bd5 user: Antoine Pitrou date: Wed Jun 18 23:07:46 2014 -0400 summary: Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. files: Lib/distutils/command/upload.py | 9 +++++---- Lib/distutils/tests/test_upload.py | 17 +++++++++++++---- Misc/ACKS | 1 + Misc/NEWS | 3 +++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py --- a/Lib/distutils/command/upload.py +++ b/Lib/distutils/command/upload.py @@ -10,7 +10,7 @@ import cStringIO as StringIO from hashlib import md5 -from distutils.errors import DistutilsOptionError +from distutils.errors import DistutilsError, DistutilsOptionError from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log @@ -181,7 +181,7 @@ self.announce(msg, log.INFO) except socket.error, e: self.announce(str(e), log.ERROR) - return + raise except HTTPError, e: status = e.code reason = e.msg @@ -190,5 +190,6 @@ self.announce('Server response (%s): %s' % (status, reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (status, reason), - log.ERROR) + msg = 'Upload failed (%s): %s' % (status, reason) + self.announce(msg, log.ERROR) + raise DistutilsError(msg) diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py --- a/Lib/distutils/tests/test_upload.py +++ b/Lib/distutils/tests/test_upload.py @@ -7,6 +7,7 @@ from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution +from distutils.errors import DistutilsError from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase @@ -41,16 +42,17 @@ class FakeOpen(object): - def __init__(self, url): + def __init__(self, url, msg=None, code=None): self.url = url if not isinstance(url, str): self.req = url else: self.req = None - self.msg = 'OK' + self.msg = msg or 'OK' + self.code = code or 200 def getcode(self): - return 200 + return self.code class uploadTestCase(PyPIRCCommandTestCase): @@ -60,13 +62,15 @@ self.old_open = upload_mod.urlopen upload_mod.urlopen = self._urlopen self.last_open = None + self.next_msg = None + self.next_code = None def tearDown(self): upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() def _urlopen(self, url): - self.last_open = FakeOpen(url) + self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) return self.last_open def test_finalize_options(self): @@ -124,6 +128,11 @@ auth = self.last_open.req.headers['Authorization'] self.assertNotIn('\n', auth) + def test_upload_fails(self): + self.next_msg = "Not Found" + self.next_code = 404 + self.assertRaises(DistutilsError, self.test_upload) + def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -306,6 +306,7 @@ Arnaud Delobelle Konrad Delong Erik Demaine +Martin Dengler John Dennis L. Peter Deutsch Roger Dev diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,9 @@ Library ------- +- Issue #21722: The distutils "upload" command now exits with a non-zero + return code when uploading fails. Patch by Martin Dengler. + - Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths before checking for a CGI script at that path. -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Thu Jun 19 10:58:39 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 19 Jun 2014 10:58:39 +0200 Subject: [Python-checkins] Daily reference leaks (3edda677119e): sum=22 Message-ID: results for 3edda677119e on branch "default" -------------------------------------------- test_collections leaked [2, 4, 0] references, sum=6 test_collections leaked [1, 2, 0] memory blocks, sum=3 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 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/reflogPspnBX', '-x'] From python-checkins at python.org Thu Jun 19 12:51:29 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 19 Jun 2014 12:51:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogQ2xvc2VzICMyMTc1?= =?utf-8?q?8=3A_asyncio_doc=3A_mention_explicitly_that_subprocess_paramete?= =?utf-8?q?rs_are?= Message-ID: <3gvKhK0kXYz7Ljv@mail.python.org> http://hg.python.org/cpython/rev/24c356168cc8 changeset: 91270:24c356168cc8 branch: 3.4 parent: 91267:d86214c98a9c user: Victor Stinner date: Thu Jun 19 12:50:27 2014 +0200 summary: Closes #21758: asyncio doc: mention explicitly that subprocess parameters are bytes or character strings files: Doc/library/asyncio-subprocess.rst | 16 ++++++++++------ Doc/library/os.rst | 1 + 2 files changed, 11 insertions(+), 6 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 @@ -22,8 +22,8 @@ .. function:: create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) - Run the shell command *cmd* given as a string. Return a :class:`~asyncio.subprocess.Process` - instance. + Run the shell command *cmd*. See :meth:`BaseEventLoop.subprocess_shell` for + parameters. Return a :class:`~asyncio.subprocess.Process` instance. The optional *limit* parameter sets the buffer limit passed to the :class:`StreamReader`. @@ -32,7 +32,8 @@ .. function:: create_subprocess_exec(\*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) - Create a subprocess. Return a :class:`~asyncio.subprocess.Process` instance. + Create a subprocess. See :meth:`BaseEventLoop.subprocess_exec` for + parameters. Return a :class:`~asyncio.subprocess.Process` instance. The optional *limit* parameter sets the buffer limit passed to the :class:`StreamReader`. @@ -50,7 +51,9 @@ .. method:: BaseEventLoop.subprocess_exec(protocol_factory, \*args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, \*\*kwargs) - Create a subprocess from one or more string arguments, where the first string + Create a subprocess from one or more string arguments (character strings or + bytes strings encoded to the :ref:`filesystem encoding + `), where the first string specifies the program to execute, and the remaining strings specify the program's arguments. (Thus, together the string arguments form the ``sys.argv`` value of the program, assuming it is a Python script.) This is @@ -94,8 +97,9 @@ .. method:: BaseEventLoop.subprocess_shell(protocol_factory, cmd, \*, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, \*\*kwargs) - Create a subprocess from *cmd*, which is a string using the platform's - "shell" syntax. This is similar to the standard library + Create a subprocess from *cmd*, which is a character string or a bytes + string encoded to the :ref:`filesystem encoding `, + using the platform's "shell" syntax. This is similar to the standard library :class:`subprocess.Popen` class called with ``shell=True``. See :meth:`~BaseEventLoop.subprocess_exec` for more details about diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -65,6 +65,7 @@ .. _os-filenames: +.. _filesystem-encoding: File Names, Command Line Arguments, and Environment Variables ------------------------------------------------------------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 12:51:30 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 19 Jun 2014 12:51:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Closes_=2321758=3A_asyncio_doc=3A_ment?= =?utf-8?q?ion_explicitly_that_subprocess?= Message-ID: <3gvKhL26K7z7Lk2@mail.python.org> http://hg.python.org/cpython/rev/b57cdb945bf9 changeset: 91271:b57cdb945bf9 parent: 91268:979aaa08c067 parent: 91270:24c356168cc8 user: Victor Stinner date: Thu Jun 19 12:51:17 2014 +0200 summary: (Merge 3.4) Closes #21758: asyncio doc: mention explicitly that subprocess parameters are bytes or character strings files: Doc/library/asyncio-subprocess.rst | 16 ++++++++++------ Doc/library/os.rst | 1 + 2 files changed, 11 insertions(+), 6 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 @@ -22,8 +22,8 @@ .. function:: create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) - Run the shell command *cmd* given as a string. Return a :class:`~asyncio.subprocess.Process` - instance. + Run the shell command *cmd*. See :meth:`BaseEventLoop.subprocess_shell` for + parameters. Return a :class:`~asyncio.subprocess.Process` instance. The optional *limit* parameter sets the buffer limit passed to the :class:`StreamReader`. @@ -32,7 +32,8 @@ .. function:: create_subprocess_exec(\*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) - Create a subprocess. Return a :class:`~asyncio.subprocess.Process` instance. + Create a subprocess. See :meth:`BaseEventLoop.subprocess_exec` for + parameters. Return a :class:`~asyncio.subprocess.Process` instance. The optional *limit* parameter sets the buffer limit passed to the :class:`StreamReader`. @@ -50,7 +51,9 @@ .. method:: BaseEventLoop.subprocess_exec(protocol_factory, \*args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, \*\*kwargs) - Create a subprocess from one or more string arguments, where the first string + Create a subprocess from one or more string arguments (character strings or + bytes strings encoded to the :ref:`filesystem encoding + `), where the first string specifies the program to execute, and the remaining strings specify the program's arguments. (Thus, together the string arguments form the ``sys.argv`` value of the program, assuming it is a Python script.) This is @@ -94,8 +97,9 @@ .. method:: BaseEventLoop.subprocess_shell(protocol_factory, cmd, \*, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, \*\*kwargs) - Create a subprocess from *cmd*, which is a string using the platform's - "shell" syntax. This is similar to the standard library + Create a subprocess from *cmd*, which is a character string or a bytes + string encoded to the :ref:`filesystem encoding `, + using the platform's "shell" syntax. This is similar to the standard library :class:`subprocess.Popen` class called with ``shell=True``. See :meth:`~BaseEventLoop.subprocess_exec` for more details about diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -65,6 +65,7 @@ .. _os-filenames: +.. _filesystem-encoding: File Names, Command Line Arguments, and Environment Variables ------------------------------------------------------------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 13:00:16 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 19 Jun 2014 13:00:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogQ2xvc2VzICMyMTU5?= =?utf-8?q?5=3A_asyncio=2EBaseSelectorEventLoop=2E=5Fread=5Ffrom=5Fself=28?= =?utf-8?q?=29_now_reads_all?= Message-ID: <3gvKtS6hTdz7LjQ@mail.python.org> http://hg.python.org/cpython/rev/46c251118799 changeset: 91272:46c251118799 branch: 3.4 parent: 91270:24c356168cc8 user: Victor Stinner date: Thu Jun 19 12:59:15 2014 +0200 summary: Closes #21595: asyncio.BaseSelectorEventLoop._read_from_self() now reads all available bytes from the "self pipe", not only a single byte. This change reduces the risk of having the pipe full and so getting the innocuous "BlockingIOError: [Errno 11] Resource temporarily unavailable" message. files: Lib/asyncio/selector_events.py | 13 +++++++++---- 1 files changed, 9 insertions(+), 4 deletions(-) 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 @@ -83,10 +83,15 @@ self.add_reader(self._ssock.fileno(), self._read_from_self) def _read_from_self(self): - try: - self._ssock.recv(1) - except (BlockingIOError, InterruptedError): - pass + while True: + try: + data = self._ssock.recv(4096) + if not data: + break + except InterruptedError: + continue + except BlockingIOError: + break def _write_to_self(self): # This may be called from a different thread, possibly after -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 13:00:18 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 19 Jun 2014 13:00:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Closes_=2321595=3A_asyncio=2EBaseSelec?= =?utf-8?q?torEventLoop=2E=5Fread=5Ffrom=5Fself=28=29_now?= Message-ID: <3gvKtV13bKz7Lk2@mail.python.org> http://hg.python.org/cpython/rev/513eea89b80a changeset: 91273:513eea89b80a parent: 91271:b57cdb945bf9 parent: 91272:46c251118799 user: Victor Stinner date: Thu Jun 19 12:59:32 2014 +0200 summary: (Merge 3.4) Closes #21595: asyncio.BaseSelectorEventLoop._read_from_self() now reads all available bytes from the "self pipe", not only a single byte. This change reduces the risk of having the pipe full and so getting the innocuous "BlockingIOError: [Errno 11] Resource temporarily unavailable" message. files: Lib/asyncio/selector_events.py | 13 +++++++++---- 1 files changed, 9 insertions(+), 4 deletions(-) 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 @@ -83,10 +83,15 @@ self.add_reader(self._ssock.fileno(), self._read_from_self) def _read_from_self(self): - try: - self._ssock.recv(1) - except (BlockingIOError, InterruptedError): - pass + while True: + try: + data = self._ssock.recv(4096) + if not data: + break + except InterruptedError: + continue + except BlockingIOError: + break def _write_to_self(self): # This may be called from a different thread, possibly after -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 16:58:01 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 19 Jun 2014 16:58:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321741=3A_Add_st?= =?utf-8?q?=5Ffile=5Fattributes_to_os=2Estat=5Fresult_on_Windows=2E?= Message-ID: <3gvR8n5gt0z7LlB@mail.python.org> http://hg.python.org/cpython/rev/706fab0213db changeset: 91274:706fab0213db user: Zachary Ware date: Thu Jun 19 09:46:37 2014 -0500 summary: Issue #21741: Add st_file_attributes to os.stat_result on Windows. Patch by Ben Hoyt. files: Doc/library/os.rst | 8 ++++++ Doc/library/stat.rst | 28 +++++++++++++++++++++++- Doc/whatsnew/3.5.rst | 9 +++++++ Lib/stat.py | 23 +++++++++++++++++++ Lib/test/test_os.py | 22 ++++++++++++++++++ Lib/test/test_stat.py | 29 ++++++++++++++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 3 ++ Modules/_stat.c | 36 +++++++++++++++++++++++++++++++ Modules/posixmodule.c | 16 +++++++++++++ 10 files changed, 174 insertions(+), 1 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1905,6 +1905,11 @@ * :attr:`st_creator` * :attr:`st_type` + On Windows systems, the following attribute is also available: + + * :attr:`st_file_attributes` - Windows file attribute bits (see the + ``FILE_ATTRIBUTE_*`` constants in the :mod:`stat` module) + .. note:: The exact meaning and resolution of the :attr:`st_atime`, @@ -1958,6 +1963,9 @@ and the :attr:`st_atime_ns`, :attr:`st_mtime_ns`, and :attr:`st_ctime_ns` members. + .. versionadded:: 3.5 + Added the :attr:`st_file_attributes` member on Windows. + .. function:: stat_float_times([newvalue]) diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst --- a/Doc/library/stat.rst +++ b/Doc/library/stat.rst @@ -126,7 +126,7 @@ if __name__ == '__main__': walktree(sys.argv[1], visitfile) -An additional utility function is provided to covert a file's mode in a human +An additional utility function is provided to convert a file's mode in a human readable string: .. function:: filemode(mode) @@ -399,3 +399,29 @@ The file is a snapshot file. See the \*BSD or Mac OS systems man page :manpage:`chflags(2)` for more information. + +On Windows, the following file attribute constants are available for use when +testing bits in the ``st_file_attributes`` member returned by :func:`os.stat`. +See the `Windows API documentation +`_ +for more detail on the meaning of these constants. + +.. data:: FILE_ATTRIBUTE_ARCHIVE + FILE_ATTRIBUTE_COMPRESSED + FILE_ATTRIBUTE_DEVICE + FILE_ATTRIBUTE_DIRECTORY + FILE_ATTRIBUTE_ENCRYPTED + FILE_ATTRIBUTE_HIDDEN + FILE_ATTRIBUTE_INTEGRITY_STREAM + FILE_ATTRIBUTE_NORMAL + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED + FILE_ATTRIBUTE_NO_SCRUB_DATA + FILE_ATTRIBUTE_OFFLINE + FILE_ATTRIBUTE_READONLY + FILE_ATTRIBUTE_REPARSE_POINT + FILE_ATTRIBUTE_SPARSE_FILE + FILE_ATTRIBUTE_SYSTEM + FILE_ATTRIBUTE_TEMPORARY + FILE_ATTRIBUTE_VIRTUAL + + .. versionadded:: 3.5 diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -176,6 +176,15 @@ network objects from existing addresses (contributed by Peter Moody and Antoine Pitrou in :issue:`16531`). +os +-- + +* :class:`os.stat_result` now has a ``st_file_attributes`` field on Windows, + containing the ``dwFileAttributes`` member of the + ``BY_HANDLE_FILE_INFORMATION`` structure returned by + ``GetFileInformationByHandle()`` (contributed by Ben Hoyt in + :issue:`21719`). + shutil ------ diff --git a/Lib/stat.py b/Lib/stat.py --- a/Lib/stat.py +++ b/Lib/stat.py @@ -148,6 +148,29 @@ perm.append("-") return "".join(perm) + +# Windows FILE_ATTRIBUTE constants for interpreting os.stat()'s +# "st_file_attributes" member + +FILE_ATTRIBUTE_ARCHIVE = 32 +FILE_ATTRIBUTE_COMPRESSED = 2048 +FILE_ATTRIBUTE_DEVICE = 64 +FILE_ATTRIBUTE_DIRECTORY = 16 +FILE_ATTRIBUTE_ENCRYPTED = 16384 +FILE_ATTRIBUTE_HIDDEN = 2 +FILE_ATTRIBUTE_INTEGRITY_STREAM = 32768 +FILE_ATTRIBUTE_NORMAL = 128 +FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192 +FILE_ATTRIBUTE_NO_SCRUB_DATA = 131072 +FILE_ATTRIBUTE_OFFLINE = 4096 +FILE_ATTRIBUTE_READONLY = 1 +FILE_ATTRIBUTE_REPARSE_POINT = 1024 +FILE_ATTRIBUTE_SPARSE_FILE = 512 +FILE_ATTRIBUTE_SYSTEM = 4 +FILE_ATTRIBUTE_TEMPORARY = 256 +FILE_ATTRIBUTE_VIRTUAL = 65536 + + # If available, use C implementation try: from _stat import * diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -530,6 +530,28 @@ os.stat(r) self.assertEqual(ctx.exception.errno, errno.EBADF) + def check_file_attributes(self, result): + self.assertTrue(hasattr(result, 'st_file_attributes')) + self.assertTrue(isinstance(result.st_file_attributes, int)) + self.assertTrue(0 <= result.st_file_attributes <= 0xFFFFFFFF) + + @unittest.skipUnless(sys.platform == "win32", + "st_file_attributes is Win32 specific") + def test_file_attributes(self): + # test file st_file_attributes (FILE_ATTRIBUTE_DIRECTORY not set) + result = os.stat(self.fname) + self.check_file_attributes(result) + self.assertEqual( + result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY, + 0) + + # test directory st_file_attributes (FILE_ATTRIBUTE_DIRECTORY set) + result = os.stat(support.TESTFN) + self.check_file_attributes(result) + self.assertEqual( + result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY, + stat.FILE_ATTRIBUTE_DIRECTORY) + from test import mapping_tests class EnvironTests(mapping_tests.BasicTestMappingProtocol): diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py --- a/Lib/test/test_stat.py +++ b/Lib/test/test_stat.py @@ -1,5 +1,6 @@ import unittest import os +import sys from test.support import TESTFN, import_fresh_module c_stat = import_fresh_module('stat', fresh=['_stat']) @@ -52,6 +53,26 @@ 'S_IWOTH': 0o002, 'S_IXOTH': 0o001} + # defined by the Windows API documentation + file_attributes = { + 'FILE_ATTRIBUTE_ARCHIVE': 32, + 'FILE_ATTRIBUTE_COMPRESSED': 2048, + 'FILE_ATTRIBUTE_DEVICE': 64, + 'FILE_ATTRIBUTE_DIRECTORY': 16, + 'FILE_ATTRIBUTE_ENCRYPTED': 16384, + 'FILE_ATTRIBUTE_HIDDEN': 2, + 'FILE_ATTRIBUTE_INTEGRITY_STREAM': 32768, + 'FILE_ATTRIBUTE_NORMAL': 128, + 'FILE_ATTRIBUTE_NOT_CONTENT_INDEXED': 8192, + 'FILE_ATTRIBUTE_NO_SCRUB_DATA': 131072, + 'FILE_ATTRIBUTE_OFFLINE': 4096, + 'FILE_ATTRIBUTE_READONLY': 1, + 'FILE_ATTRIBUTE_REPARSE_POINT': 1024, + 'FILE_ATTRIBUTE_SPARSE_FILE': 512, + 'FILE_ATTRIBUTE_SYSTEM': 4, + 'FILE_ATTRIBUTE_TEMPORARY': 256, + 'FILE_ATTRIBUTE_VIRTUAL': 65536} + def setUp(self): try: os.remove(TESTFN) @@ -185,6 +206,14 @@ self.assertTrue(callable(func)) self.assertEqual(func(0), 0) + @unittest.skipUnless(sys.platform == "win32", + "FILE_ATTRIBUTE_* constants are Win32 specific") + def test_file_attribute_constants(self): + for key, value in sorted(self.file_attributes.items()): + self.assertTrue(hasattr(self.statmod, key), key) + modvalue = getattr(self.statmod, key) + self.assertEqual(value, modvalue, key) + class TestFilemodeCStat(TestFilemode, unittest.TestCase): statmod = c_stat diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -580,6 +580,7 @@ Ken Howard Brad Howes Mike Hoy +Ben Hoyt Chih-Hao Huang Christian Hudon Lawrence Hudson diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,9 @@ Library ------- +- Issue #21719: Added the ``st_file_attributes`` field to os.stat_result on + Windows. + - Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. diff --git a/Modules/_stat.c b/Modules/_stat.c --- a/Modules/_stat.c +++ b/Modules/_stat.c @@ -27,9 +27,21 @@ #endif /* HAVE_SYS_STAT_H */ #ifdef MS_WINDOWS +#include typedef unsigned short mode_t; + +/* FILE_ATTRIBUTE_INTEGRITY_STREAM and FILE_ATTRIBUTE_NO_SCRUB_DATA + are not present in VC2010, so define them manually */ +#ifndef FILE_ATTRIBUTE_INTEGRITY_STREAM +# define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x8000 #endif +#ifndef FILE_ATTRIBUTE_NO_SCRUB_DATA +# define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x20000 +#endif + +#endif /* MS_WINDOWS */ + /* From Python's stat.py */ #ifndef S_IMODE # define S_IMODE 07777 @@ -473,6 +485,10 @@ ST_ATIME\n\ ST_MTIME\n\ ST_CTIME\n\ +\n" + +"FILE_ATTRIBUTE_*: Windows file attribute constants\n\ + (only present on Windows)\n\ "); @@ -555,6 +571,26 @@ if (PyModule_AddIntConstant(m, "ST_MTIME", 8)) return NULL; if (PyModule_AddIntConstant(m, "ST_CTIME", 9)) return NULL; +#ifdef MS_WINDOWS + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_ARCHIVE)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_COMPRESSED)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_DEVICE)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_DIRECTORY)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_ENCRYPTED)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_HIDDEN)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_INTEGRITY_STREAM)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_NORMAL)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_NO_SCRUB_DATA)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_OFFLINE)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_READONLY)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_REPARSE_POINT)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_SPARSE_FILE)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_SYSTEM)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_TEMPORARY)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_VIRTUAL)) return NULL; +#endif + return m; } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1417,6 +1417,7 @@ Therefore, we implement our own stat, based on the Win32 API directly. */ #define HAVE_STAT_NSEC 1 +#define HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES 1 struct win32_stat{ unsigned long st_dev; @@ -1433,6 +1434,7 @@ int st_mtime_nsec; time_t st_ctime; int st_ctime_nsec; + unsigned long st_file_attributes; }; static __int64 secs_between_epochs = 11644473600; /* Seconds between 1.1.1601 and 1.1.1970 */ @@ -1497,6 +1499,7 @@ /* now set the bits that make this a symlink */ result->st_mode |= S_IFLNK; } + result->st_file_attributes = info->dwFileAttributes; return 0; } @@ -1961,6 +1964,9 @@ #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME {"st_birthtime", "time of creation"}, #endif +#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES + {"st_file_attributes", "Windows file attribute bits"}, +#endif {0} }; @@ -2000,6 +2006,12 @@ #define ST_BIRTHTIME_IDX ST_GEN_IDX #endif +#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES +#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_IDX+1) +#else +#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_IDX +#endif + static PyStructSequence_Desc stat_result_desc = { "stat_result", /* name */ stat_result__doc__, /* doc */ @@ -2267,6 +2279,10 @@ PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX, PyLong_FromLong((long)st->st_flags)); #endif +#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES + PyStructSequence_SET_ITEM(v, ST_FILE_ATTRIBUTES_IDX, + PyLong_FromUnsignedLong(st->st_file_attributes)); +#endif if (PyErr_Occurred()) { Py_DECREF(v); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 17:14:25 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 19 Jun 2014 17:14:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Tulip_issue_83?= =?utf-8?q?=3A_document_more_asyncio_functions_in_docstrings?= Message-ID: <3gvRWj3vvtz7Ljg@mail.python.org> http://hg.python.org/cpython/rev/aaee7df990a2 changeset: 91275:aaee7df990a2 branch: 3.4 parent: 91272:46c251118799 user: Victor Stinner date: Thu Jun 19 17:11:49 2014 +0200 summary: Tulip issue 83: document more asyncio functions in docstrings files: Doc/library/asyncio-eventloop.rst | 7 +- Lib/asyncio/base_events.py | 21 ++++++++- Lib/asyncio/selector_events.py | 41 +++++++++++++++++- 3 files changed, 58 insertions(+), 11 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 @@ -311,11 +311,10 @@ .. method:: BaseEventLoop.create_server(protocol_factory, host=None, port=None, \*, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None) - A :ref:`coroutine ` method which creates a TCP server bound to - host and port. + Create a TCP server bound to host and port. Return an + :class:`AbstractServer` object which can be used to stop the service. - The return value is a :class:`AbstractServer` object which can be used to stop - the service. + This method is a :ref:`coroutine `. If *host* is an empty string or None all interfaces are assumed and a list of multiple sockets will be returned (most likely 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 @@ -320,7 +320,7 @@ "than the current one") def call_soon_threadsafe(self, callback, *args): - """XXX""" + """Like call_soon(), but thread safe.""" handle = self._call_soon(callback, args, check_loop=False) self._write_to_self() return handle @@ -358,7 +358,17 @@ def create_connection(self, protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None): - """XXX""" + """Connect to a TCP server. + + Create a streaming transport connection to a given Internet host and + port: socket family AF_INET or socket.AF_INET6 depending on host (or + family if specified), socket type SOCK_STREAM. protocol_factory must be + a callable returning a protocol instance. + + This method is a coroutine which will try to establish the connection + in the background. When successful, the coroutine returns a + (transport, protocol) pair. + """ if server_hostname is not None and not ssl: raise ValueError('server_hostname is only meaningful with ssl') @@ -557,7 +567,12 @@ backlog=100, ssl=None, reuse_address=None): - """XXX""" + """Create a TCP server bound to host and port. + + Return an AbstractServer object which can be used to stop the service. + + This method is a coroutine. + """ if isinstance(ssl, bool): raise TypeError('ssl argument must be an SSLContext or None') if host is not None or port is not None: 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 @@ -226,7 +226,14 @@ return False def sock_recv(self, sock, n): - """XXX""" + """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 is a coroutine. + """ fut = futures.Future(loop=self) self._sock_recv(fut, False, sock, n) return fut @@ -253,7 +260,16 @@ fut.set_result(data) def sock_sendall(self, sock, data): - """XXX""" + """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 processed by the receiving end of the connection. + + This method is a coroutine. + """ fut = futures.Future(loop=self) if data: self._sock_sendall(fut, False, sock, data) @@ -285,7 +301,16 @@ self.add_writer(fd, self._sock_sendall, fut, True, sock, data) def sock_connect(self, sock, address): - """XXX""" + """Connect to a remote socket at address. + + The address must be already resolved to avoid the trap of hanging the + entire event loop when the address requires doing a DNS lookup. For + example, it must be an IP address, not an hostname, for AF_INET and + AF_INET6 address families. Use getaddrinfo() to resolve the hostname + asynchronously. + + This method is a coroutine. + """ fut = futures.Future(loop=self) try: base_events._check_resolved_address(sock, address) @@ -318,7 +343,15 @@ fut.set_result(None) def sock_accept(self, sock): - """XXX""" + """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 is a coroutine. + """ fut = futures.Future(loop=self) self._sock_accept(fut, False, sock) return fut -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 17:14:26 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 19 Jun 2014 17:14:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Tulip_issue_83=3A_document_more_asynci?= =?utf-8?q?o_functions_in_docstrings?= Message-ID: <3gvRWk6LlVz7Lk2@mail.python.org> http://hg.python.org/cpython/rev/493d1ae3227b changeset: 91276:493d1ae3227b parent: 91274:706fab0213db parent: 91275:aaee7df990a2 user: Victor Stinner date: Thu Jun 19 17:14:05 2014 +0200 summary: (Merge 3.4) Tulip issue 83: document more asyncio functions in docstrings files: Doc/library/asyncio-eventloop.rst | 7 +- Lib/asyncio/base_events.py | 21 ++++++++- Lib/asyncio/selector_events.py | 41 +++++++++++++++++- 3 files changed, 58 insertions(+), 11 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 @@ -311,11 +311,10 @@ .. method:: BaseEventLoop.create_server(protocol_factory, host=None, port=None, \*, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None) - A :ref:`coroutine ` method which creates a TCP server bound to - host and port. + Create a TCP server bound to host and port. Return an + :class:`AbstractServer` object which can be used to stop the service. - The return value is a :class:`AbstractServer` object which can be used to stop - the service. + This method is a :ref:`coroutine `. If *host* is an empty string or None all interfaces are assumed and a list of multiple sockets will be returned (most likely 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 @@ -320,7 +320,7 @@ "than the current one") def call_soon_threadsafe(self, callback, *args): - """XXX""" + """Like call_soon(), but thread safe.""" handle = self._call_soon(callback, args, check_loop=False) self._write_to_self() return handle @@ -358,7 +358,17 @@ def create_connection(self, protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None): - """XXX""" + """Connect to a TCP server. + + Create a streaming transport connection to a given Internet host and + port: socket family AF_INET or socket.AF_INET6 depending on host (or + family if specified), socket type SOCK_STREAM. protocol_factory must be + a callable returning a protocol instance. + + This method is a coroutine which will try to establish the connection + in the background. When successful, the coroutine returns a + (transport, protocol) pair. + """ if server_hostname is not None and not ssl: raise ValueError('server_hostname is only meaningful with ssl') @@ -557,7 +567,12 @@ backlog=100, ssl=None, reuse_address=None): - """XXX""" + """Create a TCP server bound to host and port. + + Return an AbstractServer object which can be used to stop the service. + + This method is a coroutine. + """ if isinstance(ssl, bool): raise TypeError('ssl argument must be an SSLContext or None') if host is not None or port is not None: 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 @@ -226,7 +226,14 @@ return False def sock_recv(self, sock, n): - """XXX""" + """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 is a coroutine. + """ fut = futures.Future(loop=self) self._sock_recv(fut, False, sock, n) return fut @@ -253,7 +260,16 @@ fut.set_result(data) def sock_sendall(self, sock, data): - """XXX""" + """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 processed by the receiving end of the connection. + + This method is a coroutine. + """ fut = futures.Future(loop=self) if data: self._sock_sendall(fut, False, sock, data) @@ -285,7 +301,16 @@ self.add_writer(fd, self._sock_sendall, fut, True, sock, data) def sock_connect(self, sock, address): - """XXX""" + """Connect to a remote socket at address. + + The address must be already resolved to avoid the trap of hanging the + entire event loop when the address requires doing a DNS lookup. For + example, it must be an IP address, not an hostname, for AF_INET and + AF_INET6 address families. Use getaddrinfo() to resolve the hostname + asynchronously. + + This method is a coroutine. + """ fut = futures.Future(loop=self) try: base_events._check_resolved_address(sock, address) @@ -318,7 +343,15 @@ fut.set_result(None) def sock_accept(self, sock): - """XXX""" + """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 is a coroutine. + """ fut = futures.Future(loop=self) self._sock_accept(fut, False, sock) return fut -- Repository URL: http://hg.python.org/cpython From root at python.org Thu Jun 19 20:00:23 2014 From: root at python.org (Cron Daemon) Date: Thu, 19 Jun 2014 20:00:23 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From python-checkins at python.org Thu Jun 19 23:45:56 2014 From: python-checkins at python.org (charles-francois.natali) Date: Thu, 19 Jun 2014 23:45:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxODEw?= =?utf-8?q?=3A_Backport_mmap-based_arena_allocation_failure_check=2E?= Message-ID: <3gvcCS5XKYz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/012b5c9c062d changeset: 91277:012b5c9c062d branch: 2.7 parent: 89052:d4f9efd4be7d user: Charles-Fran?ois Natali date: Thu Jun 19 22:42:51 2014 +0100 summary: Issue #21810: Backport mmap-based arena allocation failure check. files: Objects/obmalloc.c | 13 +++++++++---- 1 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -540,6 +540,8 @@ { struct arena_object* arenaobj; uint excess; /* number of bytes above pool alignment */ + void *address; + int err; #ifdef PYMALLOC_DEBUG if (Py_GETENV("PYTHONMALLOCSTATS")) @@ -593,12 +595,14 @@ 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); + address = mmap(NULL, ARENA_SIZE, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + err = (address == MAP_FAILED); #else - arenaobj->address = (uptr)malloc(ARENA_SIZE); + address = malloc(ARENA_SIZE); + err = (address == 0); #endif - if (arenaobj->address == 0) { + if (err) { /* The allocation failed: return NULL after putting the * arenaobj back. */ @@ -606,6 +610,7 @@ unused_arena_objects = arenaobj; return NULL; } + arenaobj->address = (uptr)address; ++narenas_currently_allocated; #ifdef PYMALLOC_DEBUG -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 23:46:03 2014 From: python-checkins at python.org (charles-francois.natali) Date: Thu, 19 Jun 2014 23:46:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMi43IC0+IDIuNyk6?= =?utf-8?q?_Merge=2E?= Message-ID: <3gvcCb35pjz7Lkb@mail.python.org> http://hg.python.org/cpython/rev/7b4dcc39c6db changeset: 91278:7b4dcc39c6db branch: 2.7 parent: 91277:012b5c9c062d parent: 91269:cf70f030a744 user: Charles-Fran??ois Natali date: Thu Jun 19 22:45:09 2014 +0100 summary: Merge. files: .hgtags | 2 + Doc/Makefile | 5 +- Doc/README.txt | 46 +- Doc/c-api/buffer.rst | 6 +- Doc/c-api/dict.rst | 7 +- Doc/c-api/init.rst | 1 + Doc/c-api/sequence.rst | 8 +- Doc/c-api/typeobj.rst | 3 +- Doc/distutils/apiref.rst | 17 +- Doc/distutils/configfile.rst | 2 + Doc/distutils/examples.rst | 3 - Doc/distutils/setupscript.rst | 9 +- Doc/faq/general.rst | 3 +- Doc/faq/gui.rst | 14 +- Doc/faq/programming.rst | 2 +- Doc/howto/functional.rst | 3 +- Doc/howto/logging-cookbook.rst | 2 +- Doc/howto/logging.rst | 14 + Doc/howto/pyporting.rst | 614 +++--- Doc/howto/sockets.rst | 12 +- Doc/howto/unicode.rst | 2 +- Doc/howto/webservers.rst | 2 +- Doc/install/index.rst | 4 +- Doc/library/2to3.rst | 2 +- Doc/library/codecs.rst | 14 +- Doc/library/csv.rst | 57 +- Doc/library/email.util.rst | 10 +- Doc/library/exceptions.rst | 14 +- Doc/library/functions.rst | 24 +- Doc/library/future_builtins.rst | 5 + Doc/library/getopt.rst | 1 + Doc/library/hashlib.rst | 40 + Doc/library/hmac.rst | 33 + Doc/library/io.rst | 7 + Doc/library/logging.config.rst | 8 +- Doc/library/logging.handlers.rst | 20 +- Doc/library/logging.rst | 4 +- Doc/library/marshal.rst | 5 +- Doc/library/multiprocessing.rst | 2 +- Doc/library/os.rst | 21 +- Doc/library/pyexpat.rst | 8 +- Doc/library/re.rst | 2 +- Doc/library/repr.rst | 9 +- Doc/library/site.rst | 6 +- Doc/library/smtplib.rst | 6 +- Doc/library/socket.rst | 1 + Doc/library/sqlite3.rst | 5 +- Doc/library/ssl.rst | 3 +- Doc/library/stdtypes.rst | 16 +- Doc/library/stringprep.rst | 1 - Doc/library/subprocess.rst | 63 +- Doc/library/tarfile.rst | 8 +- Doc/library/time.rst | 4 +- Doc/library/tkinter.rst | 2 +- Doc/library/tokenize.rst | 25 +- Doc/library/trace.rst | 4 +- Doc/library/traceback.rst | 2 +- Doc/library/unittest.rst | 28 +- Doc/library/urllib.rst | 5 +- Doc/library/xml.etree.elementtree.rst | 2 +- Doc/reference/compound_stmts.rst | 34 +- Doc/reference/datamodel.rst | 4 +- Doc/reference/lexical_analysis.rst | 4 +- Doc/reference/simple_stmts.rst | 14 + Doc/tools/dailybuild.py | 54 +- Doc/tools/sphinx-build.py | 8 +- Doc/tools/sphinxext/indexsidebar.html | 32 +- Doc/tools/sphinxext/layout.html | 8 + Doc/tools/sphinxext/pyspecific.py | 4 +- Doc/tools/sphinxext/static/version_switch.js | 3 +- Doc/tools/sphinxext/susp-ignored.csv | 5 +- Doc/tutorial/datastructures.rst | 14 +- Doc/using/cmdline.rst | 15 +- Doc/whatsnew/2.7.rst | 172 +- Include/abstract.h | 2 +- Include/listobject.h | 2 +- Include/patchlevel.h | 4 +- Include/pythonrun.h | 2 + Lib/CGIHTTPServer.py | 2 +- Lib/_abcoll.py | 23 +- Lib/_osx_support.py | 2 +- Lib/_pyio.py | 67 +- Lib/argparse.py | 4 + Lib/ctypes/test/__init__.py | 10 +- Lib/ctypes/test/test_arrays.py | 26 +- Lib/ctypes/test/test_as_parameter.py | 6 +- Lib/ctypes/test/test_bitfields.py | 21 +- Lib/ctypes/test/test_buffers.py | 60 +- Lib/ctypes/test/test_byteswap.py | 3 +- Lib/ctypes/test/test_callbacks.py | 47 +- Lib/ctypes/test/test_cast.py | 15 +- Lib/ctypes/test/test_cfuncs.py | 9 +- Lib/ctypes/test/test_checkretval.py | 15 +- Lib/ctypes/test/test_errcheck.py | 19 - Lib/ctypes/test/test_find.py | 69 +- Lib/ctypes/test/test_functions.py | 70 +- Lib/ctypes/test/test_integers.py | 5 - Lib/ctypes/test/test_keeprefs.py | 3 +- Lib/ctypes/test/test_loading.py | 138 +- Lib/ctypes/test/test_macholib.py | 21 +- Lib/ctypes/test/test_memfunctions.py | 44 +- Lib/ctypes/test/test_numbers.py | 39 +- Lib/ctypes/test/test_objects.py | 11 +- Lib/ctypes/test/test_parameters.py | 19 +- Lib/ctypes/test/test_pep3118.py | 15 +- Lib/ctypes/test/test_prototypes.py | 84 +- Lib/ctypes/test/test_python_api.py | 32 +- Lib/ctypes/test/test_random_things.py | 27 +- Lib/ctypes/test/test_slicing.py | 71 +- Lib/ctypes/test/test_strings.py | 114 +- Lib/ctypes/test/test_structures.py | 41 +- Lib/ctypes/test/test_unicode.py | 219 +- Lib/ctypes/test/test_values.py | 100 +- Lib/ctypes/test/test_win32.py | 106 +- Lib/ctypes/test/test_wintypes.py | 8 +- Lib/distutils/__init__.py | 2 +- Lib/distutils/command/bdist_rpm.py | 1 + Lib/distutils/command/upload.py | 9 +- Lib/distutils/core.py | 7 +- Lib/distutils/dir_util.py | 4 +- Lib/distutils/spawn.py | 63 +- Lib/distutils/tests/support.py | 2 +- Lib/distutils/tests/test_upload.py | 17 +- Lib/distutils/tests/test_util.py | 12 +- Lib/distutils/util.py | 23 +- 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 +- Lib/fileinput.py | 5 +- Lib/hashlib.py | 69 +- Lib/hmac.py | 3 + Lib/httplib.py | 54 +- Lib/idlelib/AutoComplete.py | 5 + Lib/idlelib/AutoExpand.py | 21 + Lib/idlelib/Bindings.py | 32 +- Lib/idlelib/CallTipWindow.py | 51 +- Lib/idlelib/CallTips.py | 16 +- Lib/idlelib/ClassBrowser.py | 20 +- Lib/idlelib/ColorDelegator.py | 17 +- Lib/idlelib/Debugger.py | 2 +- Lib/idlelib/EditorWindow.py | 30 +- Lib/idlelib/FormatParagraph.py | 14 +- Lib/idlelib/GrepDialog.py | 74 +- Lib/idlelib/HyperParser.py | 173 +- Lib/idlelib/IOBinding.py | 16 +- Lib/idlelib/IdleHistory.py | 2 - Lib/idlelib/MultiCall.py | 15 +- Lib/idlelib/MultiStatusBar.py | 35 +- Lib/idlelib/ObjectBrowser.py | 11 +- Lib/idlelib/ParenMatch.py | 14 +- Lib/idlelib/PathBrowser.py | 20 +- Lib/idlelib/Percolator.py | 50 +- Lib/idlelib/PyShell.py | 4 +- Lib/idlelib/ReplaceDialog.py | 31 + Lib/idlelib/ScriptBinding.py | 4 +- Lib/idlelib/ScrolledList.py | 19 +- Lib/idlelib/SearchDialog.py | 22 + Lib/idlelib/SearchEngine.py | 1 - Lib/idlelib/StackViewer.py | 39 +- Lib/idlelib/ToolTip.py | 22 +- Lib/idlelib/TreeWidget.py | 31 +- Lib/idlelib/UndoDelegator.py | 21 +- Lib/idlelib/WidgetRedirector.py | 15 +- Lib/idlelib/ZoomHeight.py | 2 +- Lib/idlelib/aboutDialog.py | 20 +- Lib/idlelib/config-keys.def | 28 +- Lib/idlelib/config-main.def | 2 +- Lib/idlelib/configDialog.py | 19 +- Lib/idlelib/configHandler.py | 12 +- Lib/idlelib/configHelpSourceEdit.py | 27 +- Lib/idlelib/configSectionNameDialog.py | 28 +- Lib/idlelib/dynOptionMenuWidget.py | 26 +- Lib/idlelib/idle_test/README.txt | 47 +- Lib/idlelib/idle_test/htest.py | 368 ++++ Lib/idlelib/idle_test/mock_idle.py | 25 + Lib/idlelib/idle_test/mock_tk.py | 27 +- Lib/idlelib/idle_test/test_autocomplete.py | 143 + Lib/idlelib/idle_test/test_autoexpand.py | 141 + Lib/idlelib/idle_test/test_calltips.py | 16 +- Lib/idlelib/idle_test/test_formatparagraph.py | 13 +- Lib/idlelib/idle_test/test_hyperparser.py | 191 ++ Lib/idlelib/idle_test/test_idlehistory.py | 1 + Lib/idlelib/idle_test/test_parenmatch.py | 121 + Lib/idlelib/idle_test/test_searchengine.py | 3 + Lib/idlelib/idle_test/test_text.py | 1 + Lib/idlelib/idle_test/test_textview.py | 98 + Lib/idlelib/idlever.py | 2 +- Lib/idlelib/keybindingDialog.py | 30 +- Lib/idlelib/macosxSupport.py | 126 +- Lib/idlelib/tabbedpages.py | 10 +- Lib/idlelib/textView.py | 35 +- Lib/io.py | 7 +- Lib/json/tests/test_decode.py | 5 + Lib/lib-tk/Tkinter.py | 52 +- Lib/lib-tk/test/runtktests.py | 46 +- Lib/lib-tk/test/test_tkinter/test_geometry_managers.py | 889 ++++++++++ Lib/lib-tk/test/test_tkinter/test_images.py | 339 +++ Lib/lib-tk/test/test_tkinter/test_widgets.py | 240 ++- Lib/lib-tk/test/test_ttk/test_functions.py | 53 +- Lib/lib-tk/test/test_ttk/test_widgets.py | 12 +- Lib/lib-tk/test/widget_tests.py | 21 + Lib/lib-tk/tkFont.py | 3 +- Lib/lib-tk/ttk.py | 40 +- Lib/lib-tk/turtle.py | 18 +- Lib/lib2to3/Grammar.txt | 7 +- Lib/lib2to3/pgen2/grammar.py | 1 + Lib/lib2to3/pgen2/token.py | 13 +- Lib/lib2to3/pgen2/tokenize.py | 2 +- Lib/lib2to3/tests/test_parser.py | 13 + Lib/logging/__init__.py | 32 +- Lib/logging/config.py | 57 +- Lib/logging/handlers.py | 3 +- Lib/mimetypes.py | 38 +- Lib/multiprocessing/connection.py | 2 +- Lib/ntpath.py | 25 +- Lib/os.py | 12 +- Lib/posixpath.py | 27 +- Lib/pydoc.py | 11 +- Lib/pydoc_data/topics.py | 20 +- Lib/random.py | 9 +- Lib/robotparser.py | 14 +- Lib/shutil.py | 3 +- Lib/site.py | 2 +- Lib/sqlite3/dbapi2.py | 2 + Lib/sqlite3/test/factory.py | 35 +- Lib/sqlite3/test/hooks.py | 2 +- Lib/subprocess.py | 11 +- Lib/tempfile.py | 8 +- Lib/test/pydoc_mod.py | 10 + Lib/test/regrtest.py | 11 +- Lib/test/test_argparse.py | 9 + Lib/test/test_codecmaps_hk.py | 1 - Lib/test/test_collections.py | 172 +- Lib/test/test_compile.py | 43 +- Lib/test/test_decimal.py | 4 +- Lib/test/test_epoll.py | 3 - Lib/test/test_exceptions.py | 6 + Lib/test/test_file.py | 1 + Lib/test/test_file2k.py | 7 + Lib/test/test_fileinput.py | 43 +- Lib/test/test_genericpath.py | 29 +- Lib/test/test_grammar.py | 6 + Lib/test/test_hashlib.py | 68 +- Lib/test/test_hmac.py | 114 +- Lib/test/test_httplib.py | 48 +- Lib/test/test_httpservers.py | 26 +- Lib/test/test_idle.py | 17 +- Lib/test/test_imaplib.py | 1 - Lib/test/test_io.py | 53 + Lib/test/test_itertools.py | 17 +- Lib/test/test_mimetypes.py | 50 +- Lib/test/test_ntpath.py | 71 +- Lib/test/test_optparse.py | 1 + Lib/test/test_pkgutil.py | 4 +- Lib/test/test_posix.py | 10 +- Lib/test/test_py3kwarn.py | 5 + Lib/test/test_pydoc.py | 52 + Lib/test/test_pyexpat.py | 11 + Lib/test/test_random.py | 2 +- Lib/test/test_re.py | 13 + Lib/test/test_robotparser.py | 7 + Lib/test/test_set.py | 28 +- Lib/test/test_shutil.py | 9 + Lib/test/test_socket.py | 10 +- Lib/test/test_spwd.py | 62 + Lib/test/test_ssl.py | 11 +- Lib/test/test_strop.py | 6 + Lib/test/test_support.py | 133 +- Lib/test/test_sys.py | 90 +- Lib/test/test_tcl.py | 100 +- Lib/test/test_tempfile.py | 18 + Lib/test/test_threading.py | 43 + Lib/test/test_tk.py | 16 +- Lib/test/test_tokenize.py | 42 +- Lib/test/test_ttk_guionly.py | 16 +- Lib/test/test_unicode.py | 21 + Lib/test/test_urllib2net.py | 16 +- Lib/test/test_urllibnet.py | 24 +- Lib/test/test_winreg.py | 9 + Lib/test/test_zipfile.py | 89 +- Lib/test/test_zipimport.py | 193 +- Lib/tokenize.py | 26 +- Lib/unittest/case.py | 4 +- Lib/unittest/test/test_case.py | 6 + Lib/zipfile.py | 2 +- Mac/BuildScript/README.txt | 58 +- Mac/BuildScript/build-installer.py | 132 +- Mac/BuildScript/resources/ReadMe.txt | 26 + Mac/BuildScript/resources/Welcome.rtf | 10 +- Mac/Modules/carbonevt/_CarbonEvtmodule.c | 3 +- Mac/Modules/list/_Listmodule.c | 3 +- Mac/README | 238 +- Makefile.pre.in | 1 + Misc/ACKS | 404 +++- Misc/NEWS | 312 +++- Misc/RPM/python-2.7.spec | 2 +- Modules/_bsddb.c | 21 +- Modules/_ctypes/_ctypes.c | 80 +- Modules/_ctypes/cfield.c | 6 +- Modules/_ctypes/ctypes.h | 3 + Modules/_ctypes/stgdict.c | 7 +- Modules/_elementtree.c | 8 +- Modules/_hashopenssl.c | 225 ++ Modules/_heapqmodule.c | 16 +- Modules/_io/_iomodule.c | 30 +- Modules/_io/bufferedio.c | 9 +- Modules/_io/fileio.c | 15 +- Modules/_io/iobase.c | 4 +- Modules/_json.c | 8 + Modules/_sqlite/connection.c | 3 +- Modules/_sqlite/cursor.c | 9 +- Modules/_sqlite/row.c | 26 +- Modules/_sre.c | 97 +- Modules/_tkinter.c | 36 +- Modules/binascii.c | 55 +- Modules/bz2module.c | 3 +- Modules/cPickle.c | 3 +- Modules/cdmodule.c | 18 +- Modules/flmodule.c | 12 +- Modules/itertoolsmodule.c | 13 +- Modules/operator.c | 128 + Modules/posixmodule.c | 50 +- Modules/pyexpat.c | 2 +- Modules/readline.c | 3 +- Modules/selectmodule.c | 3 +- Modules/stropmodule.c | 28 +- Modules/svmodule.c | 3 +- Modules/zipimport.c | 426 +--- Objects/bytearrayobject.c | 6 +- Objects/exceptions.c | 12 + Objects/fileobject.c | 14 +- Objects/moduleobject.c | 6 +- Objects/setobject.c | 8 +- Objects/stringlib/formatter.h | 18 +- Objects/stringlib/transmogrify.h | 38 +- Objects/stringobject.c | 74 +- Objects/tupleobject.c | 6 +- Objects/unicodeobject.c | 8 +- PC/_subprocess.c | 5 +- PC/_winreg.c | 15 +- PCbuild/build_ssl.py | 4 +- PCbuild/pyproject.vsprops | 2 +- PCbuild/readme.txt | 2 +- Parser/tokenizer.c | 21 +- Python/ast.c | 22 +- Python/bltinmodule.c | 2 +- Python/ceval.c | 16 +- Python/compile.c | 8 +- Python/pystate.c | 2 +- Python/pythonrun.c | 9 +- Python/sysmodule.c | 3 +- Python/thread_pthread.h | 4 +- README | 2 +- Tools/buildbot/external-amd64.bat | 18 +- Tools/buildbot/external-common.bat | 21 +- Tools/buildbot/external.bat | 20 +- Tools/msi/msi.py | 27 +- configure | 76 +- configure.ac | 40 +- setup.py | 6 +- 361 files changed, 9211 insertions(+), 3694 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -163,3 +163,5 @@ fcb3ec2842f99454322492dd0ec2cf01322df093 v2.6.9 4913d0e9be30666218cc4d713937e81c0e7f346a v2.7.6rc1 3a1db0d2747ec2d47a8693ed5650f3567161a200 v2.7.6 +e32e3a9f390212463c22509d0f9aead8051cee63 v2.7.7rc1 +f89216059edf77660ef1eb2a98e88352551da1d6 v2.7.7 diff --git a/Doc/Makefile b/Doc/Makefile --- a/Doc/Makefile +++ b/Doc/Makefile @@ -176,9 +176,10 @@ autobuild-html: make html SPHINXOPTS='-A daily=1 -A versionswitcher=1' -# for stable releases: only build if not in pre-release stage (alpha, beta, rc) +# for stable releases: only build if not in pre-release stage (alpha, beta) +# release candidate downloads are okay, since the stable tree can be in that stage autobuild-stable: - @case $(DISTVERSION) in *[abc]*) \ + @case $(DISTVERSION) in *[ab]*) \ echo "Not building; $(DISTVERSION) is not a release version."; \ exit 1;; \ esac diff --git a/Doc/README.txt b/Doc/README.txt --- a/Doc/README.txt +++ b/Doc/README.txt @@ -3,18 +3,17 @@ This directory contains the reStructuredText (reST) sources to the Python documentation. You don't need to build them yourself, prebuilt versions are -available at http://docs.python.org/download/. +available at https://docs.python.org/2/download.html Documentation on the authoring Python documentation, including information about both style and markup, is available in the "Documenting Python" chapter of the -documentation. There's also a chapter intended to point out differences to -those familiar with the previous docs written in LaTeX. +documentation. Building the docs ================= -You need to have Python 2.4 or higher installed; the toolset used to build the +You need to have Python 2 installed; the toolset used to build the docs is written in Python. It is called *Sphinx*, it is not included in this tree, but maintained separately. Also needed are the docutils, supplying the base markup that Sphinx uses, Jinja, a templating engine, and optionally @@ -33,6 +32,9 @@ HTML output files. To view the generated HTML, point your favorite browser at the top-level index `build/html/index.html` after running "make". +On Windows, we try to emulate the Makefile as closely as possible with a +``make.bat`` file. + Available make targets are: * "html", which builds standalone HTML files for offline viewing. @@ -65,43 +67,23 @@ `tools/sphinxext/pyspecific.py` -- pydoc needs these to show topic and keyword help. + * "suspicious", which checks the parsed markup for text that looks like + malformed and thus unconverted reST. + A "make update" updates the Subversion checkouts in `tools/`. Without make ------------ -You'll need to install the Sphinx package, either by checking it out via :: +Install the Sphinx package and its dependencies from PyPI. - svn co http://svn.python.org/projects/external/Sphinx-0.6.7/sphinx tools/sphinx +Then, from the ``Docs`` directory, run :: -or by installing it from PyPI. + sphinx-build -b . build/ -Then, you need to install Docutils, either by checking it out via :: - - svn co http://svn.python.org/projects/external/docutils-0.6/docutils tools/docutils - -or by installing it from http://docutils.sf.net/. - -You also need Jinja2, either by checking it out via :: - - svn co http://svn.python.org/projects/external/Jinja-2.3.1/jinja2 tools/jinja2 - -or by installing it from PyPI. - -You can optionally also install Pygments, either as a checkout via :: - - svn co http://svn.python.org/projects/external/Pygments-1.3.1/pygments tools/pygments - -or from PyPI at http://pypi.python.org/pypi/Pygments. - - -Then, make an output directory, e.g. under `build/`, and run :: - - python tools/sphinx-build.py -b . build/ - -where `` is one of html, text, latex, or htmlhelp (for explanations see -the make targets above). +where ```` is one of html, text, latex, or htmlhelp (for explanations +see the make targets above). Contributing diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -21,8 +21,10 @@ Two examples of objects that support the buffer interface are strings and arrays. The string object exposes the character contents in the buffer -interface's byte-oriented form. An array can also expose its contents, but it -should be noted that array elements may be multi-byte values. +interface's byte-oriented form. An array can only expose its contents via the +old-style buffer interface. This limitation does not apply to Python 3, +where :class:`memoryview` objects can be constructed from arrays, too. +Array elements may be multi-byte values. An example user of the buffer interface is the file object's :meth:`write` method. Any object that can export a series of bytes through the buffer diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -210,8 +210,11 @@ .. c:function:: int PyDict_Update(PyObject *a, PyObject *b) - This is the same as ``PyDict_Merge(a, b, 1)`` in C, or ``a.update(b)`` in - Python. Return ``0`` on success or ``-1`` if an exception was raised. + This is the same as ``PyDict_Merge(a, b, 1)`` in C, and is similar to + ``a.update(b)`` in Python except that :c:func:`PyDict_Update` doesn't fall + back to the iterating over a sequence of key value pairs if the second + argument has no "keys" attribute. Return ``0`` on success or ``-1`` if an + exception was raised. .. versionadded:: 2.2 diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -534,6 +534,7 @@ .. index:: module: thread .. note:: + When only the main thread exists, no GIL operations are needed. This is a common situation (most Python programs do not use threads), and the lock operations slow the interpreter down a bit. Therefore, the lock is not diff --git a/Doc/c-api/sequence.rst b/Doc/c-api/sequence.rst --- a/Doc/c-api/sequence.rst +++ b/Doc/c-api/sequence.rst @@ -167,10 +167,10 @@ .. c:function:: PyObject* PySequence_Fast(PyObject *o, const char *m) - Returns the sequence *o* as a tuple, unless it is already a tuple or list, in - which case *o* is returned. Use :c:func:`PySequence_Fast_GET_ITEM` to access the - members of the result. Returns *NULL* on failure. If the object is not a - sequence, raises :exc:`TypeError` with *m* as the message text. + Return the sequence *o* as a list, unless it is already a tuple or list, in + which case *o* is returned. Use :c:func:`PySequence_Fast_GET_ITEM` to access + the members of the result. Returns *NULL* on failure. If the object is not + a sequence, raises :exc:`TypeError` with *m* as the message text. .. c:function:: PyObject* PySequence_Fast_GET_ITEM(PyObject *o, Py_ssize_t i) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1295,7 +1295,8 @@ This function is used by :c:func:`PySequence_Repeat` and has the same signature. It is also used by the ``*`` operator, after trying numeric - multiplication via the :c:member:`~PyTypeObject.tp_as_number.nb_mul` slot. + multiplication via the :c:member:`~PyTypeObject.tp_as_number.nb_multiply` + slot. .. c:member:: ssizeargfunc PySequenceMethods.sq_item diff --git a/Doc/distutils/apiref.rst b/Doc/distutils/apiref.rst --- a/Doc/distutils/apiref.rst +++ b/Doc/distutils/apiref.rst @@ -1167,15 +1167,6 @@ underscore. No { } or ( ) style quoting is available. -.. function:: grok_environment_error(exc[, prefix='error: ']) - - Generate a useful error message from an :exc:`EnvironmentError` (:exc:`IOError` - or :exc:`OSError`) exception object. Handles Python 1.5.1 and later styles, - and does what it can to deal with exception objects that don't have a filename - (which happens when the error is due to a two-file operation, such as - :func:`~os.rename` or :func:`~os.link`). Returns the error message as a - string prefixed with *prefix*. - .. function:: split_quoted(s) @@ -1916,8 +1907,12 @@ .. module:: distutils.command.clean :synopsis: Clean a package build area - -.. % todo +This command removes the temporary files created by :command:`build` +and its subcommands, like intermediary compiled object files. With +the ``--all`` option, the complete build directory will be removed. + +Extension modules built :ref:`in place ` +will not be cleaned, as they are not in the build directory. :mod:`distutils.command.config` --- Perform package configuration diff --git a/Doc/distutils/configfile.rst b/Doc/distutils/configfile.rst --- a/Doc/distutils/configfile.rst +++ b/Doc/distutils/configfile.rst @@ -69,6 +69,8 @@ Note that an option spelled :option:`--foo-bar` on the command-line is spelled :option:`foo_bar` in configuration files. +.. _distutils-build-ext-inplace: + For example, say you want your extensions to be built "in-place"---that is, you have an extension :mod:`pkg.ext`, and you want the compiled extension file (:file:`ext.so` on Unix, say) to be put in the same source directory as your diff --git a/Doc/distutils/examples.rst b/Doc/distutils/examples.rst --- a/Doc/distutils/examples.rst +++ b/Doc/distutils/examples.rst @@ -193,9 +193,6 @@ packages=['foobar', 'foobar.subfoo'], ) -(Again, the empty string in :option:`package_dir` stands for the current -directory.) - .. _single-ext: diff --git a/Doc/distutils/setupscript.rst b/Doc/distutils/setupscript.rst --- a/Doc/distutils/setupscript.rst +++ b/Doc/distutils/setupscript.rst @@ -684,6 +684,8 @@ DistributionMetadata.download_url = None +.. _debug-setup-script: + Debugging the setup script ========================== @@ -699,7 +701,8 @@ and see that it's a permission problem. On the other hand, this doesn't help the developer to find the cause of the -failure. For this purpose, the DISTUTILS_DEBUG environment variable can be set +failure. For this purpose, the :envvar:`DISTUTILS_DEBUG` environment variable can be set to anything except an empty string, and distutils will now print detailed -information what it is doing, and prints the full traceback in case an exception -occurs. +information about what it is doing, dump the full traceback when an exception +occurs, and print the whole command line when an external program (like a C +compiler) fails. diff --git a/Doc/faq/general.rst b/Doc/faq/general.rst --- a/Doc/faq/general.rst +++ b/Doc/faq/general.rst @@ -471,7 +471,8 @@ Emacs users will be happy to know that there is a very good Python mode for Emacs. All of these programming environments provide syntax highlighting, auto-indenting, and access to the interactive interpreter while coding. Consult -http://www.python.org/editors/ for a full list of Python editing environments. +`the Python wiki `_ for a full list +of Python editing environments. If you want to discuss Python's use in education, you may be interested in joining `the edu-sig mailing list diff --git a/Doc/faq/gui.rst b/Doc/faq/gui.rst --- a/Doc/faq/gui.rst +++ b/Doc/faq/gui.rst @@ -21,15 +21,15 @@ Standard builds of Python include an object-oriented interface to the Tcl/Tk widget set, called Tkinter. This is probably the easiest to install and use. For more info about Tk, including pointers to the source, see the Tcl/Tk home -page at http://www.tcl.tk. Tcl/Tk is fully portable to the MacOS, Windows, and -Unix platforms. +page at http://www.tcl.tk. Tcl/Tk is fully portable to the Mac OS X, Windows, +and Unix platforms. wxWidgets --------- wxWidgets (http://www.wxwidgets.org) is a free, portable GUI class library written in C++ that provides a native look and feel on a -number of platforms, with Windows, MacOS X, GTK, X11, all listed as +number of platforms, with Windows, Mac OS X, GTK, X11, all listed as current stable targets. Language bindings are available for a number of languages including Python, Perl, Ruby, etc. @@ -88,13 +88,9 @@ What platform-specific GUI toolkits exist for Python? ======================================================== -`The Mac port `_ by Jack Jansen has a rich and -ever-growing set of modules that support the native Mac toolbox calls. The port -supports MacOS X's Carbon libraries. - By installing the `PyObjc Objective-C bridge -`_, Python programs can use MacOS X's -Cocoa libraries. See the documentation that comes with the Mac port. +`_, Python programs can use Mac OS X's +Cocoa libraries. :ref:`Pythonwin ` by Mark Hammond includes an interface to the Microsoft Foundation Classes and a Python programming environment diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -149,7 +149,7 @@ `_. Guido van Rossum has written up an anecdote related to optimization at -http://www.python.org/doc/essays/list2str.html. +http://www.python.org/doc/essays/list2str. One thing to notice is that function and (especially) method calls are rather expensive; if you have designed a purely OO interface with lots of tiny diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -587,7 +587,8 @@ Because ``yield`` will often be returning ``None``, you should always check for this case. Don't just use its value in expressions unless you're sure that the -``send()`` method will be the only method used resume your generator function. +``send()`` method will be the only method used to resume your generator +function. In addition to ``send()``, there are two other new methods on generators: 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 @@ -706,7 +706,7 @@ } For more information about this configuration, you can see the `relevant -section `_ +section `_ of the Django documentation. Inserting a BOM into messages sent to a SysLogHandler diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -996,6 +996,15 @@ so that if the logger's threshold is set above ``DEBUG``, the calls to :func:`expensive_func1` and :func:`expensive_func2` are never made. +.. note:: In some cases, :meth:`~Logger.isEnabledFor` can iself be more + expensive than you'd like (e.g. for deeply nested loggers where an explicit + level is only set high up in the logger hierarchy). In such cases (or if you + want to avoid calling a method in tight loops), you can cache the result of a + call to :meth:`~Logger.isEnabledFor` in a local or instance variable, and use + that instead of calling the method each time. Such a cached value would only + need to be recomputed when the logging configuration changes dynamically + while the application is running (which is not all that common). + There are other optimizations which can be made for specific applications which need more precise control over what logging information is collected. Here's a list of things you can do to avoid processing during logging which you don't @@ -1005,6 +1014,11 @@ | What you don't want to collect | How to avoid collecting it | +===============================================+========================================+ | Information about where calls were made from. | Set ``logging._srcfile`` to ``None``. | +| | This avoids calling | +| | :func:`sys._getframe`, which may help | +| | to speed up your code in environments | +| | like PyPy (which can't speed up code | +| | that uses :func:`sys._getframe`). | +-----------------------------------------------+----------------------------------------+ | Threading information. | Set ``logging.logThreads`` to ``0``. | +-----------------------------------------------+----------------------------------------+ diff --git a/Doc/howto/pyporting.rst b/Doc/howto/pyporting.rst --- a/Doc/howto/pyporting.rst +++ b/Doc/howto/pyporting.rst @@ -10,238 +10,211 @@ With Python 3 being the future of Python while Python 2 is still in active use, it is good to have your project available for both major releases of - Python. This guide is meant to help you choose which strategy works best - for your project to support both Python 2 & 3 along with how to execute - that strategy. + Python. This guide is meant to help you figure out how best to support both + Python 2 & 3 simultaneously. If you are looking to port an extension module instead of pure Python code, please see :ref:`cporting-howto`. + If you would like to read one core Python developer's take on why Python 3 + came into existence, you can read Nick Coghlan's `Python 3 Q & A`_. -Choosing a Strategy -=================== + If you prefer to read a (free) book on porting a project to Python 3, + consider reading `Porting to Python 3`_ by Lennart Regebro which should cover + much of what is discussed in this HOWTO. -When a project makes the decision that it's time to support both Python 2 & 3, -a decision needs to be made as to how to go about accomplishing that goal. -The chosen strategy will depend on how large the project's existing -codebase is and how much divergence you want from your Python 2 codebase from -your Python 3 one (e.g., starting a new version with Python 3). + For help with porting, you can email the python-porting_ mailing list with + questions. -If your project is brand-new or does not have a large codebase, then you may -want to consider writing/porting :ref:`all of your code for Python 3 -and use 3to2 ` to port your code for Python 2. +The Short Version +================= -If you would prefer to maintain a codebase which is semantically **and** -syntactically compatible with Python 2 & 3 simultaneously, you can write -:ref:`use_same_source`. While this tends to lead to somewhat non-idiomatic -code, it does mean you keep a rapid development process for you, the developer. +* Decide what's the oldest version of Python 2 you want to support (if at all) +* Make sure you have a thorough test suite and use continuous integration + testing to make sure you stay compatible with the versions of Python you care + about +* If you have dependencies, check their Python 3 status using caniusepython3 + (`command-line tool `__, + `web app `__) -Finally, you do have the option of :ref:`using 2to3 ` to translate -Python 2 code into Python 3 code (with some manual help). This can take the -form of branching your code and using 2to3 to start a Python 3 branch. You can -also have users perform the translation at installation time automatically so -that you only have to maintain a Python 2 codebase. +With that done, your options are: -Regardless of which approach you choose, porting is not as hard or -time-consuming as you might initially think. You can also tackle the problem -piece-meal as a good portion of porting is simply updating your code to follow -current best practices in a Python 2/3 compatible way. +* If you are dropping Python 2 support, use 2to3_ to port to Python 3 +* If you are keeping Python 2 support, then start writing Python 2/3-compatible + code starting **TODAY** + + If you have dependencies that have not been ported, reach out to them to port + their project while working to make your code compatible with Python 3 so + you're ready when your dependencies are all ported + + If all your dependencies have been ported (or you have none), go ahead and + port to Python 3 -Universal Bits of Advice ------------------------- +* If you are creating a new project that wants to have 2/3 compatibility, + code in Python 3 and then backport to Python 2 -Regardless of what strategy you pick, there are a few things you should -consider. -One is make sure you have a robust test suite. You need to make sure everything -continues to work, just like when you support a new minor version of Python. -This means making sure your test suite is thorough and is ported properly -between Python 2 & 3. You will also most likely want to use something like tox_ -to automate testing between both a Python 2 and Python 3 VM. +Before You Begin +================ -Two, once your project has Python 3 support, make sure to add the proper -classifier on the Cheeseshop_ (PyPI_). To have your project listed as Python 3 -compatible it must have the -`Python 3 classifier `_ -(from -http://techspot.zzzeek.org/2011/01/24/zzzeek-s-guide-to-python-3-porting/):: +If your project is on the Cheeseshop_/PyPI_, make sure it has the proper +`trove classifiers`_ to signify what versions of Python it **currently** +supports. At minimum you should specify the major version(s), e.g. +``Programming Language :: Python :: 2`` if your project currently only supports +Python 2. It is preferrable that you be as specific as possible by listing every +major/minor version of Python that you support, e.g. if your project supports +Python 2.6 and 2.7, then you want the classifiers of:: - setup( - name='Your Library', - version='1.0', - classifiers=[ - # make sure to use :: Python *and* :: Python :: 3 so - # that pypi can list the package on the python 3 page - 'Programming Language :: Python', - 'Programming Language :: Python :: 3' - ], - packages=['yourlibrary'], - # make sure to add custom_fixers to the MANIFEST.in - include_package_data=True, - # ... - ) + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.6 + Programming Language :: Python :: 2.7 +Once your project supports Python 3 you will want to go back and add the +appropriate classifiers for Python 3 as well. This is important as setting the +``Programming Language :: Python :: 3`` classifier will lead to your project +being listed under the `Python 3 Packages`_ section of PyPI. -Doing so will cause your project to show up in the -`Python 3 packages list -`_. You will know -you set the classifier properly as visiting your project page on the Cheeseshop -will show a Python 3 logo in the upper-left corner of the page. +Make sure you have a robust test suite. You need to +make sure everything continues to work, just like when you support a new +minor/feature release of Python. This means making sure your test suite is +thorough and is ported properly between Python 2 & 3 (consider using coverage_ +to measure that you have effective test coverage). You will also most likely +want to use something like tox_ to automate testing between all of your +supported versions of Python. You will also want to **port your tests first** so +that you can make sure that you detect breakage during the transition. Tests also +tend to be simpler than the code they are testing so it gives you an idea of how +easy it can be to port code. -Three, the six_ project provides a library which helps iron out differences -between Python 2 & 3. If you find there is a sticky point that is a continual -point of contention in your translation or maintenance of code, consider using -a source-compatible solution relying on six. If you have to create your own -Python 2/3 compatible solution, you can use ``sys.version_info[0] >= 3`` as a -guard. - -Four, read all the approaches. Just because some bit of advice applies to one -approach more than another doesn't mean that some advice doesn't apply to other -strategies. - -Five, drop support for older Python versions if possible. `Python 2.5`_ +Drop support for older Python versions if possible. `Python 2.5`_ introduced a lot of useful syntax and libraries which have become idiomatic in Python 3. `Python 2.6`_ introduced future statements which makes compatibility much easier if you are going from Python 2 to 3. -`Python 2.7`_ continues the trend in the stdlib. So choose the newest version +`Python 2.7`_ continues the trend in the stdlib. Choose the newest version of Python which you believe can be your minimum support version and work from there. +Target the newest version of Python 3 that you can. Beyond just the usual +bugfixes, compatibility has continued to improve between Python 2 and 3 as time +has passed. E.g. Python 3.3 added back the ``u`` prefix for +strings, making source-compatible Python code easier to write. -.. _tox: http://codespeak.net/tox/ -.. _Cheeseshop: -.. _PyPI: http://pypi.python.org/ -.. _six: http://packages.python.org/six -.. _Python 2.7: http://www.python.org/2.7.x -.. _Python 2.6: http://www.python.org/2.6.x -.. _Python 2.5: http://www.python.org/2.5.x -.. _Python 2.4: http://www.python.org/2.4.x -.. _Python 2.3: http://www.python.org/2.3.x -.. _Python 2.2: http://www.python.org/2.2.x +Writing Source-Compatible Python 2/3 Code +========================================= -.. _use_3to2: +Over the years the Python community has discovered that the easiest way to +support both Python 2 and 3 in parallel is to write Python code that works in +either version. While this might sound counter-intuitive at first, it actually +is not difficult and typically only requires following some select +(non-idiomatic) practices and using some key projects to help make bridging +between Python 2 and 3 easier. -Python 3 and 3to2 -================= +Projects to Consider +-------------------- -If you are starting a new project or your codebase is small enough, you may -want to consider writing your code for Python 3 and backporting to Python 2 -using 3to2_. Thanks to Python 3 being more strict about things than Python 2 -(e.g., bytes vs. strings), the source translation can be easier and more -straightforward than from Python 2 to 3. Plus it gives you more direct -experience developing in Python 3 which, since it is the future of Python, is a -good thing long-term. +The lowest level library for supporting Python 2 & 3 simultaneously is six_. +Reading through its documentation will give you an idea of where exactly the +Python language changed between versions 2 & 3 and thus what you will want the +library to help you continue to support. -A drawback of this approach is that 3to2 is a third-party project. This means -that the Python core developers (and thus this guide) can make no promises -about how well 3to2 works at any time. There is nothing to suggest, though, -that 3to2 is not a high-quality project. +To help automate porting your code over to using six, you can use +modernize_. This project will attempt to rewrite your code to be as modern as +possible while using six to smooth out any differences between Python 2 & 3. +If you want to write your compatible code to feel more like Python 3 there is +the future_ project. It tries to provide backports of objects from Python 3 so +that you can use them from Python 2-compatible code, e.g. replacing the +``bytes`` type from Python 2 with the one from Python 3. +It also provides a translation script like modernize (its translation code is +actually partially based on it) to help start working with a pre-existing code +base. It is also unique in that its translation script will also port Python 3 +code backwards as well as Python 2 code forwards. -.. _3to2: https://bitbucket.org/amentajo/lib3to2/overview +Tips & Tricks +------------- -.. _use_2to3: - -Python 2 and 2to3 -================= - -Included with Python since 2.6, the 2to3_ tool (and :mod:`lib2to3` module) -helps with porting Python 2 to Python 3 by performing various source -translations. This is a perfect solution for projects which wish to branch -their Python 3 code from their Python 2 codebase and maintain them as -independent codebases. You can even begin preparing to use this approach -today by writing future-compatible Python code which works cleanly in -Python 2 in conjunction with 2to3; all steps outlined below will work -with Python 2 code up to the point when the actual use of 2to3 occurs. - -Use of 2to3 as an on-demand translation step at install time is also possible, -preventing the need to maintain a separate Python 3 codebase, but this approach -does come with some drawbacks. While users will only have to pay the -translation cost once at installation, you as a developer will need to pay the -cost regularly during development. If your codebase is sufficiently large -enough then the translation step ends up acting like a compilation step, -robbing you of the rapid development process you are used to with Python. -Obviously the time required to translate a project will vary, so do an -experimental translation just to see how long it takes to evaluate whether you -prefer this approach compared to using :ref:`use_same_source` or simply keeping -a separate Python 3 codebase. - -Below are the typical steps taken by a project which uses a 2to3-based approach -to supporting Python 2 & 3. - +To help with writing source-compatible code using one of the projects mentioned +in `Projects to Consider`_, consider following the below suggestions. Some of +them are handled by the suggested projects, so if you do use one of them then +read their documentation first to see which suggestions below will taken care of +for you. Support Python 2.7 ------------------- +////////////////// As a first step, make sure that your project is compatible with `Python 2.7`_. This is just good to do as Python 2.7 is the last release of Python 2 and thus will be used for a rather long time. It also allows for use of the ``-3`` flag -to Python to help discover places in your code which 2to3 cannot handle but are -known to cause issues. +to Python to help discover places in your code where compatibility might be an +issue (the ``-3`` flag is in Python 2.6 but Python 2.7 adds more warnings). Try to Support `Python 2.6`_ and Newer Only -------------------------------------------- +/////////////////////////////////////////// While not possible for all projects, if you can support `Python 2.6`_ and newer **only**, your life will be much easier. Various future statements, stdlib additions, etc. exist only in Python 2.6 and later which greatly assist in -porting to Python 3. But if you project must keep support for `Python 2.5`_ (or -even `Python 2.4`_) then it is still possible to port to Python 3. +supporting Python 3. But if you project must keep support for `Python 2.5`_ then +it is still possible to simultaneously support Python 3. Below are the benefits you gain if you only have to support Python 2.6 and newer. Some of these options are personal choice while others are **strongly** recommended (the ones that are more for personal choice are labeled as such). If you continue to support older versions of Python then you -at least need to watch out for situations that these solutions fix. +at least need to watch out for situations that these solutions fix and handle +them appropriately (which is where library help from e.g. six_ comes in handy). ``from __future__ import print_function`` ''''''''''''''''''''''''''''''''''''''''' -This is a personal choice. 2to3 handles the translation from the print -statement to the print function rather well so this is an optional step. This -future statement does help, though, with getting used to typing -``print('Hello, World')`` instead of ``print 'Hello, World'``. +It will not only get you used to typing ``print()`` as a function instead of a +statement, but it will also give you the various benefits the function has over +the Python 2 statement (six_ provides a function if you support Python 2.5 or +older). ``from __future__ import unicode_literals`` ''''''''''''''''''''''''''''''''''''''''''' -Another personal choice. You can always mark what you want to be a (unicode) -string with a ``u`` prefix to get the same effect. But regardless of whether -you use this future statement or not, you **must** make sure you know exactly -which Python 2 strings you want to be bytes, and which are to be strings. This -means you should, **at minimum** mark all strings that are meant to be text -strings with a ``u`` prefix if you do not use this future statement. +If you choose to use this future statement then all string literals in +Python 2 will be assumed to be Unicode (as is already the case in Python 3). +If you choose not to use this future statement then you should mark all of your +text strings with a ``u`` prefix and only support Python 3.3 or newer. But you +are **strongly** advised to do one or the other (six_ provides a function in +case you don't want to use the future statement **and** you want to support +Python 3.2 or older). -Bytes literals -'''''''''''''' +Bytes/string literals +''''''''''''''''''''' -This is a **very** important one. The ability to prefix Python 2 strings that -are meant to contain bytes with a ``b`` prefix help to very clearly delineate -what is and is not a Python 3 string. When you run 2to3 on code, all Python 2 -strings become Python 3 strings **unless** they are prefixed with ``b``. +This is a **very** important one. Prefix Python 2 strings that +are meant to contain bytes with a ``b`` prefix to very clearly delineate +what is and is not a Python 3 text string (six_ provides a function to use for +Python 2.5 compatibility). + +This point cannot be stressed enough: make sure you know what all of your string +literals in Python 2 are meant to be in Python 3. Any string literal that +should be treated as bytes should have the ``b`` prefix. Any string literal +that should be Unicode/text in Python 2 should either have the ``u`` literal +(supported, but ignored, in Python 3.3 and later) or you should have +``from __future__ import unicode_literals`` at the top of the file. But the key +point is you should know how Python 3 will treat every one one of your string +literals and you should mark them as appropriate. There are some differences between byte literals in Python 2 and those in Python 3 thanks to the bytes type just being an alias to ``str`` in Python 2. -Probably the biggest "gotcha" is that indexing results in different values. In -Python 2, the value of ``b'py'[1]`` is ``'y'``, while in Python 3 it's ``121``. -You can avoid this disparity by always slicing at the size of a single element: -``b'py'[1:2]`` is ``'y'`` in Python 2 and ``b'y'`` in Python 3 (i.e., close -enough). +See the `Handle Common "Gotchas"`_ section for what to watch out for. -You cannot concatenate bytes and strings in Python 3. But since Python -2 has bytes aliased to ``str``, it will succeed: ``b'a' + u'b'`` works in -Python 2, but ``b'a' + 'b'`` in Python 3 is a :exc:`TypeError`. A similar issue -also comes about when doing comparisons between bytes and strings. +``from __future__ import absolute_import`` +'''''''''''''''''''''''''''''''''''''''''' +Discussed in more detail below, but you should use this future statement to +prevent yourself from accidentally using implicit relative imports. Supporting `Python 2.5`_ and Newer Only ---------------------------------------- +/////////////////////////////////////// If you are supporting `Python 2.5`_ and newer there are still some features of Python that you can utilize. @@ -251,7 +224,7 @@ '''''''''''''''''''''''''''''''''''''''''' Implicit relative imports (e.g., importing ``spam.bacon`` from within -``spam.eggs`` with the statement ``import bacon``) does not work in Python 3. +``spam.eggs`` with the statement ``import bacon``) do not work in Python 3. This future statement moves away from that and allows the use of explicit relative imports (e.g., ``from . import bacon``). @@ -261,16 +234,74 @@ the statement, but you still want the __future__ statement to prevent implicit relative imports. In `Python 2.7`_ the __future__ statement is not needed. In other words, unless you are only supporting Python 2.7 or a version earlier -than Python 2.5, use the __future__ statement. +than Python 2.5, use this __future__ statement. +Mark all Unicode strings with a ``u`` prefix +''''''''''''''''''''''''''''''''''''''''''''' + +While Python 2.6 has a ``__future__`` statement to automatically cause Python 2 +to treat all string literals as Unicode, Python 2.5 does not have that shortcut. +This means you should go through and mark all string literals with a ``u`` +prefix to turn them explicitly into text strings where appropriate and only +support Python 3.3 or newer. Otherwise use a project like six_ which provides a +function to pass all text string literals through. + + +Capturing the Currently Raised Exception +'''''''''''''''''''''''''''''''''''''''' + +In Python 2.5 and earlier the syntax to access the current exception is:: + + try: + raise Exception() + except Exception, exc: + # Current exception is 'exc'. + pass + +This syntax changed in Python 3 (and backported to `Python 2.6`_ and later) +to:: + + try: + raise Exception() + except Exception as exc: + # Current exception is 'exc'. + # In Python 3, 'exc' is restricted to the block; in Python 2.6/2.7 it will "leak". + pass + +Because of this syntax change you must change how you capture the current +exception in Python 2.5 and earlier to:: + + try: + raise Exception() + except Exception: + import sys + exc = sys.exc_info()[1] + # Current exception is 'exc'. + pass + +You can get more information about the raised exception from +:func:`sys.exc_info` than simply the current exception instance, but you most +likely don't need it. + +.. note:: + In Python 3, the traceback is attached to the exception instance + through the ``__traceback__`` attribute. If the instance is saved in + a local variable that persists outside of the ``except`` block, the + traceback will create a reference cycle with the current frame and its + dictionary of local variables. This will delay reclaiming dead + resources until the next cyclic :term:`garbage collection` pass. + + In Python 2, this problem only occurs if you save the traceback itself + (e.g. the third element of the tuple returned by :func:`sys.exc_info`) + in a variable. + Handle Common "Gotchas" ------------------------ +/////////////////////// -There are a few things that just consistently come up as sticking points for -people which 2to3 cannot handle automatically or can easily be done in Python 2 -to help modernize your code. +These are things to watch out for no matter what version of Python 2 you are +supporting which are not syntactic considerations. ``from __future__ import division`` @@ -327,9 +358,9 @@ the bytes/string dichotomy. Because Python 2 allowed the ``str`` type to hold textual data, people have over the years been rather loose in their delineation of what ``str`` instances held text compared to bytes. In Python 3 you cannot -be so care-free anymore and need to properly handle the difference. The key +be so care-free anymore and need to properly handle the difference. The key to handling this issue is to make sure that **every** string literal in your -Python 2 code is either syntactically of functionally marked as either bytes or +Python 2 code is either syntactically or functionally marked as either bytes or text data. After this is done you then need to make sure your APIs are designed to either handle a specific type or made to be properly polymorphic. @@ -436,14 +467,7 @@ happen to use the ``unicode(self).encode('utf8')`` idiom as the body of your ``__str__()`` method). -There are two ways to solve this issue. One is to use a custom 2to3 fixer. The -blog post at http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/ -specifies how to do this. That will allow 2to3 to change all instances of ``def -__unicode(self): ...`` to ``def __str__(self): ...``. This does require that you -define your ``__str__()`` method in Python 2 before your ``__unicode__()`` -method. - -The other option is to use a mixin class. This allows you to only define a +You can use a mixin class to work around this. This allows you to only define a ``__unicode__()`` method for your class and let the mixin derive ``__str__()`` for you (code from http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/):: @@ -486,6 +510,7 @@ Even better is to use the documented attributes the exception provides. + Don't use ``__getslice__`` & Friends '''''''''''''''''''''''''''''''''''' @@ -497,189 +522,62 @@ Updating doctests ''''''''''''''''' -2to3_ will attempt to generate fixes for doctests that it comes across. It's -not perfect, though. If you wrote a monolithic set of doctests (e.g., a single -docstring containing all of your doctests), you should at least consider -breaking the doctests up into smaller pieces to make it more manageable to fix. -Otherwise it might very well be worth your time and effort to port your tests -to :mod:`unittest`. +Don't forget to make them Python 2/3 compatible as well. If you wrote a +monolithic set of doctests (e.g., a single docstring containing all of your +doctests), you should at least consider breaking the doctests up into smaller +pieces to make it more manageable to fix. Otherwise it might very well be worth +your time and effort to port your tests to :mod:`unittest`. +Update ``map`` for imbalanced input sequences +''''''''''''''''''''''''''''''''''''''''''''' + +With Python 2, when ``map`` was given more than one input sequence it would pad +the shorter sequences with `None` values, returning a sequence as long as the +longest input sequence. + +With Python 3, if the input sequences to ``map`` are of unequal length, ``map`` +will stop at the termination of the shortest of the sequences. For full +compatibility with ``map`` from Python 2.x, wrap the sequence arguments in +:func:`itertools.zip_longest`, e.g. ``map(func, *sequences)`` becomes +``list(map(func, itertools.zip_longest(*sequences)))``. + Eliminate ``-3`` Warnings ------------------------- When you run your application's test suite, run it using the ``-3`` flag passed to Python. This will cause various warnings to be raised during execution about -things that 2to3 cannot handle automatically (e.g., modules that have been -removed). Try to eliminate those warnings to make your code even more portable -to Python 3. +things that are semantic changes between Python 2 and 3. Try to eliminate those +warnings to make your code even more portable to Python 3. -Run 2to3 --------- +Alternative Approaches +====================== -Once you have made your Python 2 code future-compatible with Python 3, it's -time to use 2to3_ to actually port your code. +While supporting Python 2 & 3 simultaneously is typically the preferred choice +by people so that they can continue to improve code and have it work for the +most number of users, your life may be easier if you only have to support one +major version of Python going forward. +Supporting Only Python 3 Going Forward From Python 2 Code +--------------------------------------------------------- -Manually -'''''''' +If you have Python 2 code but going forward only want to improve it as Python 3 +code, then you can use 2to3_ to translate your Python 2 code to Python 3 code. +This is only recommended, though, if your current version of your project is +going into maintenance mode and you want all new features to be exclusive to +Python 3. -To manually convert source code using 2to3_, you use the ``2to3`` script that -is installed with Python 2.6 and later.:: - 2to3 +Backporting Python 3 code to Python 2 +------------------------------------- -This will cause 2to3 to write out a diff with all of the fixers applied for the -converted source code. If you would like 2to3 to go ahead and apply the changes -you can pass it the ``-w`` flag:: - - 2to3 -w - -There are other flags available to control exactly which fixers are applied, -etc. - - -During Installation -''''''''''''''''''' - -When a user installs your project for Python 3, you can have either -:mod:`distutils` or Distribute_ run 2to3_ on your behalf. -For distutils, use the following idiom:: - - try: # Python 3 - from distutils.command.build_py import build_py_2to3 as build_py - except ImportError: # Python 2 - from distutils.command.build_py import build_py - - setup(cmdclass = {'build_py': build_py}, - # ... - ) - -For Distribute:: - - setup(use_2to3=True, - # ... - ) - -This will allow you to not have to distribute a separate Python 3 version of -your project. It does require, though, that when you perform development that -you at least build your project and use the built Python 3 source for testing. - - -Verify & Test -------------- - -At this point you should (hopefully) have your project converted in such a way -that it works in Python 3. Verify it by running your unit tests and making sure -nothing has gone awry. If you miss something then figure out how to fix it in -Python 3, backport to your Python 2 code, and run your code through 2to3 again -to verify the fix transforms properly. - - -.. _2to3: http://docs.python.org/py3k/library/2to3.html -.. _Distribute: http://packages.python.org/distribute/ - - -.. _use_same_source: - -Python 2/3 Compatible Source -============================ - -While it may seem counter-intuitive, you can write Python code which is -source-compatible between Python 2 & 3. It does lead to code that is not -entirely idiomatic Python (e.g., having to extract the currently raised -exception from ``sys.exc_info()[1]``), but it can be run under Python 2 -**and** Python 3 without using 2to3_ as a translation step (although the tool -should be used to help find potential portability problems). This allows you to -continue to have a rapid development process regardless of whether you are -developing under Python 2 or Python 3. Whether this approach or using -:ref:`use_2to3` works best for you will be a per-project decision. - -To get a complete idea of what issues you will need to deal with, see the -`What's New in Python 3.0`_. Others have reorganized the data in other formats -such as http://docs.pythonsprints.com/python3_porting/py-porting.html\ . - -The following are some steps to take to try to support both Python 2 & 3 from -the same source code. - - -.. _What's New in Python 3.0: http://docs.python.org/release/3.0/whatsnew/3.0.html - - -Follow The Steps for Using 2to3_ --------------------------------- - -All of the steps outlined in how to -:ref:`port Python 2 code with 2to3 ` apply -to creating a Python 2/3 codebase. This includes trying only support Python 2.6 -or newer (the :mod:`__future__` statements work in Python 3 without issue), -eliminating warnings that are triggered by ``-3``, etc. - -You should even consider running 2to3_ over your code (without committing the -changes). This will let you know where potential pain points are within your -code so that you can fix them properly before they become an issue. - - -Use six_ --------- - -The six_ project contains many things to help you write portable Python code. -You should make sure to read its documentation from beginning to end and use -any and all features it provides. That way you will minimize any mistakes you -might make in writing cross-version code. - - -Capturing the Currently Raised Exception ----------------------------------------- - -One change between Python 2 and 3 that will require changing how you code (if -you support `Python 2.5`_ and earlier) is -accessing the currently raised exception. In Python 2.5 and earlier the syntax -to access the current exception is:: - - try: - raise Exception() - except Exception, exc: - # Current exception is 'exc' - pass - -This syntax changed in Python 3 (and backported to `Python 2.6`_ and later) -to:: - - try: - raise Exception() - except Exception as exc: - # Current exception is 'exc' - # In Python 3, 'exc' is restricted to the block; Python 2.6 will "leak" - pass - -Because of this syntax change you must change to capturing the current -exception to:: - - try: - raise Exception() - except Exception: - import sys - exc = sys.exc_info()[1] - # Current exception is 'exc' - pass - -You can get more information about the raised exception from -:func:`sys.exc_info` than simply the current exception instance, but you most -likely don't need it. - -.. note:: - In Python 3, the traceback is attached to the exception instance - through the ``__traceback__`` attribute. If the instance is saved in - a local variable that persists outside of the ``except`` block, the - traceback will create a reference cycle with the current frame and its - dictionary of local variables. This will delay reclaiming dead - resources until the next cyclic :term:`garbage collection` pass. - - In Python 2, this problem only occurs if you save the traceback itself - (e.g. the third element of the tuple returned by :func:`sys.exc_info`) - in a variable. +If you have Python 3 code and have little interest in supporting Python 2 you +can use 3to2_ to translate from Python 3 code to Python 2 code. This is only +recommended if you don't plan to heavily support Python 2 users. Otherwise +write your code for Python 3 and then backport as far back as you want. This +is typically easier than going from Python 2 to 3 as you will have worked out +any difficulties with e.g. bytes/strings, etc. Other Resources @@ -687,17 +585,41 @@ The authors of the following blog posts, wiki pages, and books deserve special thanks for making public their tips for porting Python 2 code to Python 3 (and -thus helping provide information for this document): +thus helping provide information for this document and its various revisions +over the years): +* http://wiki.python.org/moin/PortingPythonToPy3k * http://python3porting.com/ * http://docs.pythonsprints.com/python3_porting/py-porting.html * http://techspot.zzzeek.org/2011/01/24/zzzeek-s-guide-to-python-3-porting/ * http://dabeaz.blogspot.com/2011/01/porting-py65-and-my-superboard-to.html * http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/ * http://lucumr.pocoo.org/2010/2/11/porting-to-python-3-a-guide/ -* http://wiki.python.org/moin/PortingPythonToPy3k +* https://wiki.ubuntu.com/Python/3 If you feel there is something missing from this document that should be added, please email the python-porting_ mailing list. + + +.. _2to3: http://docs.python.org/2/library/2to3.html +.. _3to2: https://pypi.python.org/pypi/3to2 +.. _Cheeseshop: PyPI_ +.. _coverage: https://pypi.python.org/pypi/coverage +.. _future: http://python-future.org/ +.. _modernize: https://github.com/mitsuhiko/python-modernize +.. _Porting to Python 3: http://python3porting.com/ +.. _PyPI: http://pypi.python.org/ +.. _Python 2.2: http://www.python.org/2.2.x +.. _Python 2.5: http://www.python.org/2.5.x +.. _Python 2.6: http://www.python.org/2.6.x +.. _Python 2.7: http://www.python.org/2.7.x +.. _Python 2.5: http://www.python.org/2.5.x +.. _Python 3.3: http://www.python.org/3.3.x +.. _Python 3 Packages: https://pypi.python.org/pypi?:action=browse&c=533&show=all +.. _Python 3 Q & A: http://ncoghlan-devs-python-notes.readthedocs.org/en/latest/python3/questions_and_answers.html .. _python-porting: http://mail.python.org/mailman/listinfo/python-porting +.. _six: https://pypi.python.org/pypi/six +.. _tox: https://pypi.python.org/pypi/tox +.. _trove classifiers: https://pypi.python.org/pypi?%3Aaction=list_classifiers + diff --git a/Doc/howto/sockets.rst b/Doc/howto/sockets.rst --- a/Doc/howto/sockets.rst +++ b/Doc/howto/sockets.rst @@ -207,13 +207,15 @@ totalsent = totalsent + sent def myreceive(self): - msg = '' - while len(msg) < MSGLEN: - chunk = self.sock.recv(MSGLEN-len(msg)) + chunks = [] + bytes_recd = 0 + while bytes_recd < MSGLEN: + chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048)) if chunk == '': raise RuntimeError("socket connection broken") - msg = msg + chunk - return msg + chunks.append(chunk) + bytes_recd = bytes_recd + len(chunk) + return ''.join(chunks) The sending code here is usable for almost any messaging scheme - in Python you send strings, and you can use ``len()`` to determine its length (even if it has diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst --- a/Doc/howto/unicode.rst +++ b/Doc/howto/unicode.rst @@ -49,7 +49,7 @@ 255 characters aren't very many. For example, you can't fit both the accented characters used in Western Europe and the Cyrillic alphabet used for Russian -into the 128-255 range because there are more than 127 such characters. +into the 128-255 range because there are more than 128 such characters. You could write files using different codes (all your Russian files in a coding system called KOI8, all your French files in a different coding system called diff --git a/Doc/howto/webservers.rst b/Doc/howto/webservers.rst --- a/Doc/howto/webservers.rst +++ b/Doc/howto/webservers.rst @@ -691,7 +691,7 @@ The newest version of TurboGears, version 2.0, moves even further in direction of WSGI support and a component-based architecture. TurboGears 2 is based on the WSGI stack of another popular component-based web framework, `Pylons -`_. +`_. Zope diff --git a/Doc/install/index.rst b/Doc/install/index.rst --- a/Doc/install/index.rst +++ b/Doc/install/index.rst @@ -58,7 +58,9 @@ document; there will be some brief forays into using Python's interactive mode to explore your installation, but that's it. If you're looking for information on how to distribute your own Python modules so that others may use them, see -the :ref:`distutils-index` manual. +the :ref:`distutils-index` manual. :ref:`debug-setup-script` may also be of +interest. + .. _inst-trivial-install: diff --git a/Doc/library/2to3.rst b/Doc/library/2to3.rst --- a/Doc/library/2to3.rst +++ b/Doc/library/2to3.rst @@ -336,7 +336,7 @@ Replaces use of the :class:`set` constructor with set literals. This fixer is optional. -.. 2to3fixer:: standard_error +.. 2to3fixer:: standarderror Renames :exc:`StandardError` to :exc:`Exception`. diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -23,24 +23,26 @@ It defines the following functions: -.. function:: encode(obj, encoding='ascii', errors='strict') +.. function:: encode(obj, [encoding[, errors]]) - Encodes *obj* using the codec registered for *encoding*. + Encodes *obj* using the codec registered for *encoding*. The default + encoding is ``'ascii'``. *Errors* may be given to set the desired error handling scheme. The - default error handler is ``strict`` meaning that encoding errors raise + default error handler is ``'strict'`` meaning that encoding errors raise :exc:`ValueError` (or a more codec specific subclass, such as :exc:`UnicodeEncodeError`). Refer to :ref:`codec-base-classes` for more information on codec error handling. .. versionadded:: 2.4 -.. function:: decode(obj, encoding='ascii', errors='strict') +.. function:: decode(obj, [encoding[, errors]]) - Decodes *obj* using the codec registered for *encoding*. + Decodes *obj* using the codec registered for *encoding*. The default + encoding is ``'ascii'``. *Errors* may be given to set the desired error handling scheme. The - default error handler is ``strict`` meaning that decoding errors raise + default error handler is ``'strict'`` meaning that decoding errors raise :exc:`ValueError` (or a more codec specific subclass, such as :exc:`UnicodeDecodeError`). Refer to :ref:`codec-base-classes` for more information on codec error handling. diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -164,36 +164,43 @@ The :mod:`csv` module defines the following classes: -.. class:: DictReader(csvfile, fieldnames=None, restkey=None, restval=None, dialect='excel', *args, **kwds) +.. class:: DictReader(csvfile, fieldnames=None, restkey=None, restval=None, \ + dialect='excel', *args, **kwds) - Create an object which operates like a regular reader but maps the information - read into a dict whose keys are given by the optional *fieldnames* parameter. - If the *fieldnames* parameter is omitted, the values in the first row of the - *csvfile* will be used as the fieldnames. If the row read has more fields - than the fieldnames sequence, the remaining data is added as a sequence - keyed by the value of *restkey*. If the row read has fewer fields than the - fieldnames sequence, the remaining keys take the value of the optional - *restval* parameter. Any other optional or keyword arguments are passed to - the underlying :class:`reader` instance. + Create an object which operates like a regular reader but maps the + information read into a dict whose keys are given by the optional + *fieldnames* parameter. The *fieldnames* parameter is a :ref:`sequence + ` whose elements are associated with the + fields of the input data in order. These elements become the keys of the + resulting dictionary. If the *fieldnames* parameter is omitted, the values + in the first row of the *csvfile* will be used as the fieldnames. If the + row read has more fields than the fieldnames sequence, the remaining data is + added as a sequence keyed by the value of *restkey*. If the row read has + fewer fields than the fieldnames sequence, the remaining keys take the value + of the optional *restval* parameter. Any other optional or keyword + arguments are passed to the underlying :class:`reader` instance. -.. class:: DictWriter(csvfile, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds) +.. class:: DictWriter(csvfile, fieldnames, restval='', extrasaction='raise', \ + dialect='excel', *args, **kwds) - Create an object which operates like a regular writer but maps dictionaries onto - output rows. The *fieldnames* parameter identifies the order in which values in - the dictionary passed to the :meth:`writerow` method are written to the - *csvfile*. The optional *restval* parameter specifies the value to be written - if the dictionary is missing a key in *fieldnames*. If the dictionary passed to - the :meth:`writerow` method contains a key not found in *fieldnames*, the - optional *extrasaction* parameter indicates what action to take. If it is set - to ``'raise'`` a :exc:`ValueError` is raised. If it is set to ``'ignore'``, - extra values in the dictionary are ignored. Any other optional or keyword - arguments are passed to the underlying :class:`writer` instance. + Create an object which operates like a regular writer but maps dictionaries + onto output rows. The *fieldnames* parameter is a :ref:`sequence + ` of keys that identify the order in + which values in the dictionary passed to the :meth:`writerow` method are + written to the *csvfile*. The optional *restval* parameter specifies the + value to be written if the dictionary is missing a key in *fieldnames*. If + the dictionary passed to the :meth:`writerow` method contains a key not + found in *fieldnames*, the optional *extrasaction* parameter indicates what + action to take. If it is set to ``'raise'`` a :exc:`ValueError` is raised. + If it is set to ``'ignore'``, extra values in the dictionary are ignored. + Any other optional or keyword arguments are passed to the underlying + :class:`writer` instance. - Note that unlike the :class:`DictReader` class, the *fieldnames* parameter of - the :class:`DictWriter` is not optional. Since Python's :class:`dict` objects - are not ordered, there is not enough information available to deduce the order - in which the row should be written to the *csvfile*. + Note that unlike the :class:`DictReader` class, the *fieldnames* parameter + of the :class:`DictWriter` is not optional. Since Python's :class:`dict` + objects are not ordered, there is not enough information available to deduce + the order in which the row should be written to the *csvfile*. .. class:: Dialect diff --git a/Doc/library/email.util.rst b/Doc/library/email.util.rst --- a/Doc/library/email.util.rst +++ b/Doc/library/email.util.rst @@ -76,12 +76,9 @@ .. function:: mktime_tz(tuple) - Turn a 10-tuple as returned by :func:`parsedate_tz` into a UTC timestamp. It - the timezone item in the tuple is ``None``, assume local time. Minor - deficiency: :func:`mktime_tz` interprets the first 8 elements of *tuple* as a - local time and then compensates for the timezone difference. This may yield a - slight error around changes in daylight savings time, though not worth worrying - about for common use. + Turn a 10-tuple as returned by :func:`parsedate_tz` into a UTC + timestamp (seconds since the Epoch). If the timezone item in the + tuple is ``None``, assume local time. .. function:: formatdate([timeval[, localtime][, usegmt]]) @@ -165,4 +162,3 @@ .. [#] Note that the sign of the timezone offset is the opposite of the sign of the ``time.timezone`` variable for the same timezone; the latter variable follows the POSIX standard while this module follows :rfc:`2822`. - diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -38,10 +38,10 @@ interpreter raises the same exception; but beware that there is nothing to prevent user code from raising an inappropriate error. -The built-in exception classes can be sub-classed to define new exceptions; -programmers are encouraged to at least derive new exceptions from the -:exc:`Exception` class and not :exc:`BaseException`. More information on -defining exceptions is available in the Python Tutorial under +The built-in exception classes can be subclassed to define new exceptions; +programmers are encouraged to derive new exceptions from the :exc:`Exception` +class or one of its subclasses, and not from :exc:`BaseException`. More +information on defining exceptions is available in the Python Tutorial under :ref:`tut-userexceptions`. The following exceptions are only used as base classes for other exceptions. @@ -158,9 +158,9 @@ .. exception:: GeneratorExit - Raise when a :term:`generator`\'s :meth:`close` method is called. It - directly inherits from :exc:`BaseException` instead of :exc:`StandardError` since - it is technically not an error. + Raised when a :term:`generator`\'s :meth:`close` method is called. It + directly inherits from :exc:`BaseException` instead of :exc:`StandardError` + since it is technically not an error. .. versionadded:: 2.5 diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -596,8 +596,21 @@ .. function:: hex(x) - Convert an integer number (of any size) to a hexadecimal string. The result is a - valid Python expression. + Convert an integer number (of any size) to a lowercase hexadecimal string + prefixed with "0x", for example: + + >>> hex(255) + '0xff' + >>> hex(-42) + '-0x2a' + >>> hex(1L) + '0x1L' + + If x is not a Python :class:`int` or :class:`long` object, it has to + define an __index__() method that returns an integer. + + See also :func:`int` for converting a hexadecimal string to an + integer using a base of 16. .. note:: @@ -713,7 +726,8 @@ .. function:: len(s) Return the length (the number of items) of an object. The argument may be a - sequence (string, tuple or list) or a mapping (dictionary). + sequence (such as a string, bytes, tuple, list, or range) or a collection + (such as a dictionary, set, or frozen set). .. function:: list([iterable]) @@ -880,8 +894,8 @@ to use the system default, which is usually line buffered for tty devices and fully buffered for other files. If omitted, the system default is used. [#]_ - Modes ``'r+'``, ``'w+'`` and ``'a+'`` open the file for updating (note that - ``'w+'`` truncates the file). Append ``'b'`` to the mode to open the file in + Modes ``'r+'``, ``'w+'`` and ``'a+'`` open the file for updating (reading and writing); + note that ``'w+'`` truncates the file. Append ``'b'`` to the mode to open the file in binary mode, on systems that differentiate between binary and text files; on systems that don't have this distinction, adding the ``'b'`` has no effect. diff --git a/Doc/library/future_builtins.rst b/Doc/library/future_builtins.rst --- a/Doc/library/future_builtins.rst +++ b/Doc/library/future_builtins.rst @@ -50,6 +50,11 @@ Works like :func:`itertools.imap`. + .. note:: + + In Python 3, :func:`map` does not accept ``None`` for the + function argument. + .. function:: oct(object) Works like the built-in :func:`oct`, but instead of :meth:`__oct__` it will diff --git a/Doc/library/getopt.rst b/Doc/library/getopt.rst --- a/Doc/library/getopt.rst +++ b/Doc/library/getopt.rst @@ -10,6 +10,7 @@ -------------- .. note:: + The :mod:`getopt` module is a parser for command line options whose API is designed to be familiar to users of the C :c:func:`getopt` function. Users who are unfamiliar with the C :c:func:`getopt` function or who would like to write diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst --- a/Doc/library/hashlib.rst +++ b/Doc/library/hashlib.rst @@ -135,6 +135,46 @@ compute the digests of strings that share a common initial substring. +Key Derivation Function +----------------------- + +Key derivation and key stretching algorithms are designed for secure password +hashing. Naive algorithms such as ``sha1(password)`` are not resistant against +brute-force attacks. A good password hashing function must be tunable, slow, and +include a `salt `_. + + +.. function:: pbkdf2_hmac(name, password, salt, rounds, dklen=None) + + The function provides PKCS#5 password-based key derivation function 2. It + uses HMAC as pseudorandom function. + + The string *name* is the desired name of the hash digest algorithm for + HMAC, e.g. 'sha1' or 'sha256'. *password* and *salt* are interpreted as + buffers of bytes. Applications and libraries should limit *password* to + a sensible value (e.g. 1024). *salt* should be about 16 or more bytes from + a proper source, e.g. :func:`os.urandom`. + + The number of *rounds* should be chosen based on the hash algorithm and + computing power. As of 2013, at least 100,000 rounds of SHA-256 is suggested. + + *dklen* is the length of the derived key. If *dklen* is ``None`` then the + digest size of the hash algorithm *name* is used, e.g. 64 for SHA-512. + + >>> import hashlib, binascii + >>> dk = hashlib.pbkdf2_hmac('sha256', b'password', b'salt', 100000) + >>> binascii.hexlify(dk) + b'0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5' + + .. versionadded:: 2.7.8 + + .. note:: + + A fast implementation of *pbkdf2_hmac* is available with OpenSSL. The + Python implementation uses an inline version of :mod:`hmac`. It is about + three times slower and doesn't release the GIL. + + .. seealso:: Module :mod:`hmac` diff --git a/Doc/library/hmac.rst b/Doc/library/hmac.rst --- a/Doc/library/hmac.rst +++ b/Doc/library/hmac.rst @@ -38,6 +38,13 @@ This string will be the same length as the *digest_size* of the digest given to the constructor. It may contain non-ASCII characters, including NUL bytes. + .. warning:: + + When comparing the output of :meth:`digest` to an externally-supplied + digest during a verification routine, it is recommended to use the + :func:`compare_digest` function instead of the ``==`` operator + to reduce the vulnerability to timing attacks. + .. method:: HMAC.hexdigest() @@ -45,6 +52,13 @@ containing only hexadecimal digits. This may be used to exchange the value safely in email or other non-binary environments. + .. warning:: + + When comparing the output of :meth:`hexdigest` to an externally-supplied + digest during a verification routine, it is recommended to use the + :func:`compare_digest` function instead of the ``==`` operator + to reduce the vulnerability to timing attacks. + .. method:: HMAC.copy() @@ -52,6 +66,25 @@ compute the digests of strings that share a common initial substring. +This module also provides the following helper function: + +.. function:: compare_digest(a, b) + + Return ``a == b``. This function uses an approach designed to prevent + timing analysis by avoiding content-based short circuiting behaviour, + making it appropriate for cryptography. *a* and *b* must both be of the + same type: either :class:`unicode` or a :term:`bytes-like object`. + + .. note:: + + If *a* and *b* are of different lengths, or if an error occurs, + a timing attack could theoretically reveal information about the + types and lengths of *a* and *b*--but not their values. + + + .. versionadded:: 2.7.7 + + .. seealso:: Module :mod:`hashlib` diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -346,6 +346,12 @@ is usual for each of the lines provided to have a line separator at the end. + .. method:: __del__() + + Prepare for object destruction. :class:`IOBase` provides a default + implementation of this method that calls the instance's + :meth:`~IOBase.close` method. + .. class:: RawIOBase @@ -644,6 +650,7 @@ :exc:`UnsupportedOperation`. .. warning:: + :class:`BufferedRWPair` does not attempt to synchronize accesses to its underlying raw streams. You should not pass it the same object as reader and writer; use :class:`BufferedRandom` instead. 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 @@ -118,7 +118,9 @@ send it to the socket as a string of bytes preceded by a four-byte length string packed in binary using ``struct.pack('>L', n)``. - .. note:: Because portions of the configuration are passed through + .. note:: + + Because portions of the configuration are passed through :func:`eval`, use of this function may open its users to a security risk. While the function only binds to a socket on ``localhost``, and so does not accept connections from remote machines, there are scenarios where @@ -721,7 +723,9 @@ :class:`~logging.Formatter` can present exception tracebacks in an expanded or condensed format. -.. note:: Due to the use of :func:`eval` as described above, there are +.. note:: + + Due to the use of :func:`eval` as described above, there are potential security risks which result from using the :func:`listen` to send and receive configurations via sockets. The risks are limited to where multiple users with no mutual trust run code on the same machine; see the diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -729,15 +729,29 @@ .. class:: HTTPHandler(host, url, method='GET') - Returns a new instance of the :class:`HTTPHandler` class. The *host* can be + Returns a new instance of the :class:`HTTPHandler` class. The ``host`` can be of the form ``host:port``, should you need to use a specific port number. - If no *method* is specified, ``GET`` is used. + .. method:: mapLogRecord(record) + + Provides a dictionary, based on ``record``, which is to be URL-encoded + and sent to the web server. The default implementation just returns + ``record.__dict__``. This method can be overridden if e.g. only a + subset of :class:`~logging.LogRecord` is to be sent to the web server, or + if more specific customization of what's sent to the server is required. .. method:: emit(record) - Sends the record to the Web server as a percent-encoded dictionary. + Sends the record to the Web server as an URL-encoded dictionary. The + :meth:`mapLogRecord` method is used to convert the record to the + dictionary to be sent. + .. note:: Since preparing a record for sending it to a Web server is not + the same as a generic formatting operation, using :meth:`setFormatter` + to specify a :class:`Formatter` for a :class:`HTTPHandler` has no effect. + Instead of calling :meth:`format`, this handler calls :meth:`mapLogRecord` + and then :func:`urllib.urlencode` to encode the dictionary in a form + suitable for sending to a Web server. .. seealso:: diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -222,7 +222,7 @@ interpreted as for :meth:`debug`. -.. method:: Logger.exception(msg, *args) +.. method:: Logger.exception(msg, *args, **kwargs) Logs a message with level :const:`ERROR` on this logger. The arguments are interpreted as for :meth:`debug`. Exception info is added to the logging @@ -840,7 +840,7 @@ are interpreted as for :func:`debug`. -.. function:: exception(msg[, *args]) +.. function:: exception(msg[, *args[, **kwargs]]) Logs a message with level :const:`ERROR` on the root logger. The arguments are interpreted as for :func:`debug`. Exception info is added to the logging diff --git a/Doc/library/marshal.rst b/Doc/library/marshal.rst --- a/Doc/library/marshal.rst +++ b/Doc/library/marshal.rst @@ -66,8 +66,9 @@ .. function:: dump(value, file[, version]) Write the value on the open file. The value must be a supported type. The - file must be an open file object such as ``sys.stdout`` or returned by - :func:`open` or :func:`os.popen`. It must be opened in binary mode (``'wb'`` + file must be a open file object such as ``sys.stdout`` or returned by + :func:`open` or :func:`os.popen`. It may not be a wrapper such as + TemporaryFile on Windows. It must be opened in binary mode (``'wb'`` or ``'w+b'``). If the value has (or contains an object that has) an unsupported type, a diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -1257,7 +1257,7 @@ *exposed* is used to specify a sequence of method names which proxies for this typeid should be allowed to access using - :meth:`BaseProxy._callMethod`. (If *exposed* is ``None`` then + :meth:`BaseProxy._callmethod`. (If *exposed* is ``None`` then :attr:`proxytype._exposed_` is used instead if it exists.) In the case where no exposed list is specified, all "public methods" of the shared object will be accessible. (Here a "public method" means any attribute diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -157,7 +157,9 @@ Availability: Unix. - .. note:: On Mac OS X, :func:`getgroups` behavior differs somewhat from + .. note:: + + On Mac OS X, :func:`getgroups` behavior differs somewhat from other Unix platforms. If the Python interpreter was built with a deployment target of :const:`10.5` or earlier, :func:`getgroups` returns the list of effective group ids associated with the current user process; @@ -255,7 +257,7 @@ .. index:: single: user; id - Return the current process's user id. + Return the current process's real user id. Availability: Unix. @@ -461,8 +463,9 @@ .. index:: single: I/O control; buffering Return an open file object connected to the file descriptor *fd*. The *mode* - and *bufsize* arguments have the same meaning as the corresponding arguments to - the built-in :func:`open` function. + and *bufsize* arguments have the same meaning as the corresponding arguments + to the built-in :func:`open` function. If :func:`fdopen` raises an + exception, it leaves *fd* untouched (unclosed). Availability: Unix, Windows. @@ -744,7 +747,7 @@ by *how*: :const:`SEEK_SET` or ``0`` to set the position relative to the beginning of the file; :const:`SEEK_CUR` or ``1`` to set it relative to the current position; :const:`SEEK_END` or ``2`` to set it relative to the end of - the file. + the file. Return the new cursor position in bytes, starting from the beginning. Availability: Unix, Windows. @@ -1601,9 +1604,11 @@ If optional argument *topdown* is ``True`` or not specified, the triple for a directory is generated before the triples for any of its subdirectories - (directories are generated top-down). If *topdown* is ``False``, the triple for a - directory is generated after the triples for all of its subdirectories - (directories are generated bottom-up). + (directories are generated top-down). If *topdown* is ``False``, the triple + for a directory is generated after the triples for all of its subdirectories + (directories are generated bottom-up). No matter the value of *topdown*, the + list of subdirectories is retrieved before the tuples for the directory and + its subdirectories are generated. When *topdown* is ``True``, the caller can modify the *dirnames* list in-place (perhaps using :keyword:`del` or slice assignment), and :func:`walk` will only diff --git a/Doc/library/pyexpat.rst b/Doc/library/pyexpat.rst --- a/Doc/library/pyexpat.rst +++ b/Doc/library/pyexpat.rst @@ -103,6 +103,10 @@ http://www.python.org/ns/ elem1 elem2 + Due to limitations in the ``Expat`` library used by :mod:`pyexpat`, + the :class:`xmlparser` instance returned can only be used to parse a single + XML document. Call ``ParserCreate`` for each document to provide unique + parser instances. .. seealso:: @@ -122,7 +126,9 @@ Parses the contents of the string *data*, calling the appropriate handler functions to process the parsed data. *isfinal* must be true on the final call - to this method. *data* can be the empty string at any time. + to this method; it allows the parsing of a single file in fragments, + not the submission of multiple files. + *data* can be the empty string at any time. .. method:: xmlparser.ParseFile(file) diff --git a/Doc/library/re.rst b/Doc/library/re.rst --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -527,7 +527,7 @@ .. function:: search(pattern, string, flags=0) - Scan through *string* looking for a location where the regular expression + Scan through *string* looking for the first location where the regular expression *pattern* produces a match, and return a corresponding :class:`MatchObject` instance. Return ``None`` if no position in the string matches the pattern; note that this is different from finding a zero-length match at some point in the diff --git a/Doc/library/repr.rst b/Doc/library/repr.rst --- a/Doc/library/repr.rst +++ b/Doc/library/repr.rst @@ -24,8 +24,9 @@ .. class:: Repr() Class which provides formatting services useful in implementing functions - similar to the built-in :func:`repr`; size limits for different object types - are added to avoid the generation of representations which are excessively long. + similar to the built-in :ref:`repr() `; size limits for different + object types are added to avoid the generation of representations which are + excessively long. .. data:: aRepr @@ -96,8 +97,8 @@ .. method:: Repr.repr(obj) - The equivalent to the built-in :func:`repr` that uses the formatting imposed by - the instance. + The equivalent to the built-in :ref:`repr() ` that uses the + formatting imposed by the instance. .. method:: Repr.repr1(obj, level) diff --git a/Doc/library/site.rst b/Doc/library/site.rst --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -83,7 +83,11 @@ :mod:`sitecustomize`, which can perform arbitrary site-specific customizations. It is typically created by a system administrator in the site-packages directory. If this import fails with an :exc:`ImportError` exception, it is -silently ignored. +silently ignored. If Python is started without output streams available, as +with :file:`pythonw.exe` on Windows (which is used by default to start IDLE), +attempted output from :mod:`sitecustomize` is ignored. Any exception other +than :exc:`ImportError` causes a silent and perhaps mysterious failure of the +process. .. index:: module: usercustomize diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -32,7 +32,8 @@ than a success code, an :exc:`SMTPConnectError` is raised. The optional *timeout* parameter specifies a timeout in seconds for blocking operations like the connection attempt (if not specified, the global default timeout - setting will be used). + setting will be used). If the timeout expires, :exc:`socket.timeout` + is raised. For normal use, you should only require the initialization/connect, :meth:`sendmail`, and :meth:`~smtplib.quit` methods. @@ -54,7 +55,8 @@ formatted private key and certificate chain file for the SSL connection. The optional *timeout* parameter specifies a timeout in seconds for blocking operations like the connection attempt (if not specified, the global default - timeout setting will be used). + timeout setting will be used). If the timeout expires, :exc:`socket.timeout` + is raised. .. versionadded:: 2.6 diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -562,6 +562,7 @@ automatically closed when they are garbage-collected. .. note:: + :meth:`close()` releases the resource associated with a connection but does not necessarily close the connection immediately. If you want to close the connection in a timely fashion, call :meth:`shutdown()` diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -617,7 +617,7 @@ .. method:: keys - This method returns a tuple of column names. Immediately after a query, + This method returns a list of column names. Immediately after a query, it is the first member of each tuple in :attr:`Cursor.description`. .. versionadded:: 2.6 @@ -727,9 +727,6 @@ sqlite3 module's supported types for SQLite: one of NoneType, int, long, float, str, unicode, buffer. -The :mod:`sqlite3` module uses Python object adaptation, as described in -:pep:`246` for this. The protocol to use is :class:`PrepareProtocol`. - There are two ways to enable the :mod:`sqlite3` module to adapt a custom Python type to one of the supported ones. diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -490,8 +490,7 @@ chain it finds in the file which matches. Some "standard" root certificates are available from various certification -authorities: `CACert.org `_, `Thawte -`_, `Verisign +authorities: `Thawte `_, `Verisign `_, `Positive SSL `_ (used by python.org), `Equifax and GeoTrust diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -399,8 +399,8 @@ | ``math.trunc(x)`` | *x* truncated to Integral | | +--------------------+------------------------------------+--------+ | ``round(x[, n])`` | *x* rounded to n digits, | | -| | rounding half to even. If n is | | -| | omitted, it defaults to 0. | | +| | rounding ties away from zero. If n | | +| | is omitted, it defaults to 0. | | +--------------------+------------------------------------+--------+ | ``math.floor(x)`` | the greatest integral float <= *x* | | +--------------------+------------------------------------+--------+ @@ -754,11 +754,11 @@ +------------------+--------------------------------+----------+ | ``max(s)`` | largest item of *s* | | +------------------+--------------------------------+----------+ -| ``s.index(i)`` | index of the first occurrence | | -| | of *i* in *s* | | +| ``s.index(x)`` | index of the first occurrence | | +| | of *x* in *s* | | +------------------+--------------------------------+----------+ -| ``s.count(i)`` | total number of occurrences of | | -| | *i* in *s* | | +| ``s.count(x)`` | total number of occurrences of | | +| | *x* in *s* | | +------------------+--------------------------------+----------+ Sequence types also support comparisons. In particular, tuples and lists @@ -1989,8 +1989,8 @@ If no positional argument is given, an empty dictionary is created. If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, - the positional argument must be an :term:`iterator` object. Each item in - the iterable must itself be an iterator with exactly two objects. The + the positional argument must be an :term:`iterable` object. Each item in + the iterable must itself be an iterable with exactly two objects. The first object of each item becomes a key in the new dictionary, and the second object the corresponding value. If a key occurs more than once, the last value for that key becomes the corresponding value in the new diff --git a/Doc/library/stringprep.rst b/Doc/library/stringprep.rst --- a/Doc/library/stringprep.rst +++ b/Doc/library/stringprep.rst @@ -4,7 +4,6 @@ .. module:: stringprep :synopsis: String preparation, as per RFC 3453 - :deprecated: .. moduleauthor:: Martin v. L??wis .. sectionauthor:: Martin v. L??wis diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -12,7 +12,7 @@ The :mod:`subprocess` module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to -replace several other, older modules and functions, such as:: +replace several older modules and functions:: os.system os.spawn* @@ -20,20 +20,26 @@ popen2.* commands.* -Information about how the :mod:`subprocess` module can be used to replace these -modules and functions can be found in the following sections. +Information about how this module can be used to replace the older +functions can be found in the subprocess-replacements_ section. .. seealso:: + POSIX users (Linux, BSD, etc.) are strongly encouraged to install + and use the much more recent subprocess32_ module instead of the + version included with python 2.7. It is a drop in replacement with + better behavior in many situations. + :pep:`324` -- PEP proposing the subprocess module +.. _subprocess32: https://pypi.python.org/pypi/subprocess32/ Using the :mod:`subprocess` Module ---------------------------------- -The recommended approach to invoking subprocesses is to use the following -convenience functions for all use cases they can handle. For more advanced -use cases, the underlying :class:`Popen` interface can be used directly. +The recommended way to launch subprocesses is to use the following +convenience functions. For more advanced use cases when these do not +meet your needs, use the underlying :class:`Popen` interface. .. function:: call(args, *, stdin=None, stdout=None, stderr=None, shell=False) @@ -57,16 +63,15 @@ .. warning:: - Invoking the system shell with ``shell=True`` can be a security hazard - if combined with untrusted input. See the warning under - :ref:`frequently-used-arguments` for details. + Using ``shell=True`` can be a security hazard. See the warning + under :ref:`frequently-used-arguments` for details. .. note:: - Do not use ``stdout=PIPE`` or ``stderr=PIPE`` with this function. As - the pipes are not being read in the current process, the child - process may block if it generates enough output to a pipe to fill up - the OS pipe buffer. + Do not use ``stdout=PIPE`` or ``stderr=PIPE`` with this function + as that can deadlock based on the child process output volume. + Use :class:`Popen` with the :meth:`communicate` method when you + need pipes. .. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False) @@ -96,16 +101,15 @@ .. warning:: - Invoking the system shell with ``shell=True`` can be a security hazard - if combined with untrusted input. See the warning under - :ref:`frequently-used-arguments` for details. + Using ``shell=True`` can be a security hazard. See the warning + under :ref:`frequently-used-arguments` for details. .. note:: - Do not use ``stdout=PIPE`` or ``stderr=PIPE`` with this function. As - the pipes are not being read in the current process, the child - process may block if it generates enough output to a pipe to fill up - the OS pipe buffer. + Do not use ``stdout=PIPE`` or ``stderr=PIPE`` with this function + as that can deadlock based on the child process output volume. + Use :class:`Popen` with the :meth:`communicate` method when you + need pipes. .. function:: check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False) @@ -145,19 +149,16 @@ .. versionadded:: 2.7 - .. - .. warning:: - Invoking the system shell with ``shell=True`` can be a security hazard - if combined with untrusted input. See the warning under - :ref:`frequently-used-arguments` for details. + Using ``shell=True`` can be a security hazard. See the warning + under :ref:`frequently-used-arguments` for details. .. note:: - Do not use ``stderr=PIPE`` with this function. As the pipe is not being - read in the current process, the child process may block if it - generates enough output to the pipe to fill up the OS pipe buffer. + Do not use ``stderr=PIPE`` with this function as that can deadlock + based on the child process error volume. Use :class:`Popen` with + the :meth:`communicate` method when you need a stderr pipe. .. data:: PIPE @@ -740,9 +741,9 @@ :: - sts = os.system("mycmd" + " myarg") + status = os.system("mycmd" + " myarg") # becomes - sts = call("mycmd" + " myarg", shell=True) + status = subprocess.call("mycmd" + " myarg", shell=True) Notes: @@ -865,7 +866,7 @@ (child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) ==> - p = Popen(["somestring"], shell=True, bufsize=bufsize, + p = Popen("somestring", shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -77,6 +77,10 @@ If *fileobj* is specified, it is used as an alternative to a file object opened for *name*. It is supposed to be at position 0. + For modes ``'w:gz'``, ``'r:gz'``, ``'w:bz2'``, ``'r:bz2'``, :func:`tarfile.open` + accepts the keyword argument *compresslevel* to specify the compression level of + the file. + For special purposes, there is a second format for *mode*: ``'filemode|[compression]'``. :func:`tarfile.open` will return a :class:`TarFile` object that processes its data as a stream of blocks. No random seeking will @@ -305,7 +309,7 @@ .. versionadded:: 2.6 -.. method:: TarFile.open(...) +.. classmethod:: TarFile.open(...) Alternative constructor. The :func:`tarfile.open` function is actually a shortcut to this classmethod. @@ -544,7 +548,7 @@ :const:`AREGTYPE`, :const:`LNKTYPE`, :const:`SYMTYPE`, :const:`DIRTYPE`, :const:`FIFOTYPE`, :const:`CONTTYPE`, :const:`CHRTYPE`, :const:`BLKTYPE`, :const:`GNUTYPE_SPARSE`. To determine the type of a :class:`TarInfo` object - more conveniently, use the ``is_*()`` methods below. + more conveniently, use the ``is*()`` methods below. .. attribute:: TarInfo.linkname diff --git a/Doc/library/time.rst b/Doc/library/time.rst --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -236,7 +236,9 @@ :func:`gmtime` or :func:`localtime` to a string as specified by the *format* argument. If *t* is not provided, the current time as returned by :func:`localtime` is used. *format* must be a string. :exc:`ValueError` is - raised if any field in *t* is outside of the allowed range. + raised if any field in *t* is outside of the allowed range. :func:`strftime` + returns a locale depedent byte string; the result may be converted to unicode + by doing ``strftime().decode(locale.getlocale()[1])``. .. versionchanged:: 2.1 Allowed *t* to be omitted. diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -19,7 +19,7 @@ .. seealso:: - `Python Tkinter Resources `_ + `Python Tkinter Resources `_ The Python Tkinter Topic Guide provides a great deal of information on using Tk from Python and links to other sources of information on Tk. diff --git a/Doc/library/tokenize.rst b/Doc/library/tokenize.rst --- a/Doc/library/tokenize.rst +++ b/Doc/library/tokenize.rst @@ -17,9 +17,10 @@ To simplify token stream handling, all :ref:`operators` and :ref:`delimiters` tokens are returned using the generic :data:`token.OP` token type. The exact -type can be determined by checking the token ``string`` field on the -:term:`named tuple` returned from :func:`tokenize.tokenize` for the character -sequence that identifies a specific operator token. +type can be determined by checking the second field (containing the actual +token string matched) of the tuple returned from +:func:`tokenize.generate_tokens` for the character sequence that identifies a +specific operator token. The primary entry point is a :term:`generator`: @@ -97,6 +98,24 @@ .. versionadded:: 2.5 +.. exception:: TokenError + + Raised when either a docstring or expression that may be split over several + lines is not completed anywhere in the file, for example:: + + """Beginning of + docstring + + or:: + + [1, + 2, + 3 + +Note that unclosed single-quoted strings do not cause an error to be +raised. They are tokenized as ``ERRORTOKEN``, followed by the tokenization of +their contents. + Example of a script re-writer that transforms float literals into Decimal objects:: diff --git a/Doc/library/trace.rst b/Doc/library/trace.rst --- a/Doc/library/trace.rst +++ b/Doc/library/trace.rst @@ -41,8 +41,8 @@ At least one of the following options must be specified when invoking :mod:`trace`. The :option:`--listfuncs <-l>` option is mutually exclusive with -the :option:`--trace <-t>` and :option:`--counts <-c>` options. When -:option:`--listfuncs <-l>` is provided, neither :option:`--counts <-c>` nor +the :option:`--trace <-t>` and :option:`--count <-c>` options. When +:option:`--listfuncs <-l>` is provided, neither :option:`--count <-c>` nor :option:`--trace <-t>` are accepted, and vice versa. .. program:: trace diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -75,7 +75,7 @@ Return a list of up to *limit* "pre-processed" stack trace entries extracted from the traceback object *traceback*. It is useful for alternate formatting of stack traces. If *limit* is omitted or ``None``, all entries are extracted. A - "pre-processed" stack trace entry is a quadruple (*filename*, *line number*, + "pre-processed" stack trace entry is a 4-tuple (*filename*, *line number*, *function name*, *text*) representing the information that is usually printed for a stack trace. The *text* is a string with leading and trailing whitespace stripped; if the source is not available it is ``None``. diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -719,9 +719,9 @@ .. method:: setUp() Method called to prepare the test fixture. This is called immediately - before calling the test method; any exception raised by this method will - be considered an error rather than a test failure. The default - implementation does nothing. + before calling the test method; other than :exc:`AssertionError` or :exc:`SkipTest`, + any exception raised by this method will be considered an error rather than + a test failure. The default implementation does nothing. .. method:: tearDown() @@ -729,10 +729,10 @@ Method called immediately after the test method has been called and the result recorded. This is called even if the test method raised an exception, so the implementation in subclasses may need to be particularly - careful about checking internal state. Any exception raised by this - method will be considered an error rather than a test failure. This - method will only be called if the :meth:`setUp` succeeds, regardless of - the outcome of the test method. The default implementation does nothing. + careful about checking internal state. Any exception, other than :exc:`AssertionError` + or :exc:`SkipTest`, raised by this method will be considered an error rather than a + test failure. This method will only be called if the :meth:`setUp` succeeds, + regardless of the outcome of the test method. The default implementation does nothing. .. method:: setUpClass() @@ -1510,11 +1510,11 @@ .. method:: discover(start_dir, pattern='test*.py', top_level_dir=None) - Find and return all test modules from the specified start directory, - recursing into subdirectories to find them. Only test files that match - *pattern* will be loaded. (Using shell style pattern matching.) Only - module names that are importable (i.e. are valid Python identifiers) will - be loaded. + Find all the test modules by recursing into subdirectories from the + specified start directory, and return a TestSuite object containing them. + Only test files that match *pattern* will be loaded. (Using shell style + pattern matching.) Only module names that are importable (i.e. are valid + Python identifiers) will be loaded. All test modules must be importable from the top level of the project. If the start directory is not the top level directory then the top level @@ -1687,14 +1687,14 @@ Called after the test case *test* has been executed, regardless of the outcome. - .. method:: startTestRun(test) + .. method:: startTestRun() Called once before any tests are executed. .. versionadded:: 2.7 - .. method:: stopTestRun(test) + .. method:: stopTestRun() Called once after all tests are executed. diff --git a/Doc/library/urllib.rst b/Doc/library/urllib.rst --- a/Doc/library/urllib.rst +++ b/Doc/library/urllib.rst @@ -9,8 +9,9 @@ Python 3 to :mod:`urllib.request`, :mod:`urllib.parse`, and :mod:`urllib.error`. The :term:`2to3` tool will automatically adapt imports when converting your sources to Python 3. - Also note that the :func:`urllib.urlopen` function has been removed in - Python 3 in favor of :func:`urllib2.urlopen`. + Also note that the :func:`urllib.request.urlopen` function in Python 3 is + equivalent to :func:`urllib2.urlopen` and that :func:`urllib.urlopen` has + been removed. .. index:: single: WWW 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 @@ -319,7 +319,7 @@ +=======================+======================================================+ | ``tag`` | Selects all child elements with the given tag. | | | For example, ``spam`` selects all child elements | -| | named ``spam``, ``spam/egg`` selects all | +| | named ``spam``, and ``spam/egg`` selects all | | | grandchildren named ``egg`` in all children named | | | ``spam``. | +-----------------------+------------------------------------------------------+ diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -296,14 +296,14 @@ exception or executes a :keyword:`return` or :keyword:`break` statement, the saved exception is discarded:: - def f(): - try: - 1/0 - finally: - return 42 - - >>> f() - 42 + >>> def f(): + ... try: + ... 1/0 + ... finally: + ... return 42 + ... + >>> f() + 42 The exception information is not available to the program during execution of the :keyword:`finally` clause. @@ -320,6 +320,20 @@ reason is a problem with the current implementation --- this restriction may be lifted in the future). +The return value of a function is determined by the last :keyword:`return` +statement executed. Since the :keyword:`finally` clause always executes, a +:keyword:`return` statement executed in the :keyword:`finally` clause will +always be the last one executed:: + + >>> def foo(): + ... try: + ... return 'try' + ... finally: + ... return 'finally' + ... + >>> foo() + 'finally' + Additional information on exceptions can be found in section :ref:`exceptions`, and information on using the :keyword:`raise` statement to generate exceptions may be found in section :ref:`raise`. @@ -331,7 +345,9 @@ The :keyword:`with` statement ============================= -.. index:: statement: with +.. index:: + statement: with + single: as; with statement .. versionadded:: 2.5 diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -356,8 +356,6 @@ object: mutable sequence object: mutable pair: assignment; statement - single: delete - statement: del single: subscription single: slicing @@ -1422,7 +1420,7 @@ User-defined classes have :meth:`__cmp__` and :meth:`__hash__` methods by default; with them, all objects compare unequal (except with themselves) - and ``x.__hash__()`` returns ``id(x)``. + and ``x.__hash__()`` returns a result derived from ``id(x)``. Classes which inherit a :meth:`__hash__` method from a parent class but change the meaning of :meth:`__cmp__` or :meth:`__eq__` such that the hash diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -104,9 +104,7 @@ Encoding declarations --------------------- -.. index:: - single: source character set - single: encodings +.. index:: source character set, encoding declarations (source file) If a comment in the first or second line of the Python script matches the regular expression ``coding[=:]\s*([-\w.]+)``, this comment is processed as an diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -72,6 +72,7 @@ ===================== .. index:: + single: =; assignment statement pair: assignment; statement pair: binding; name pair: rebinding; name @@ -241,6 +242,18 @@ .. index:: pair: augmented; assignment single: statement; assignment, augmented + single: +=; augmented assignment + single: -=; augmented assignment + single: *=; augmented assignment + single: /=; augmented assignment + single: %=; augmented assignment + single: &=; augmented assignment + single: ^=; augmented assignment + single: |=; augmented assignment + single: **=; augmented assignment + single: //=; augmented assignment + single: >>=; augmented assignment + single: <<=; augmented assignment Augmented assignment is the combination, in a single statement, of a binary operation and an assignment statement: @@ -653,6 +666,7 @@ single: module; importing pair: name; binding keyword: from + single: as; import statement .. productionlist:: import_stmt: "import" `module` ["as" `name`] ( "," `module` ["as" `name`] )* diff --git a/Doc/tools/dailybuild.py b/Doc/tools/dailybuild.py --- a/Doc/tools/dailybuild.py +++ b/Doc/tools/dailybuild.py @@ -29,16 +29,34 @@ BUILDROOT = '/home/gbrandl/docbuild' +SPHINXBUILD = os.path.join(BUILDROOT, 'sphinx-env/bin/sphinx-build') WWWROOT = '/data/ftp.python.org/pub/docs.python.org' BRANCHES = [ # checkout, target, isdev - (BUILDROOT + '/python33', WWWROOT + '/3.3', False), - (BUILDROOT + '/python34', WWWROOT + '/3.4', True), + (BUILDROOT + '/python34', WWWROOT + '/3.4', False), + (BUILDROOT + '/python35', WWWROOT + '/3.5', True), (BUILDROOT + '/python27', WWWROOT + '/2.7', False), ] +def _files_changed(old, new): + with open(old, 'rb') as fp1, open(new, 'rb') as fp2: + st1 = os.fstat(fp1.fileno()) + st2 = os.fstat(fp2.fileno()) + if st1.st_size != st2.st_size: + return False + if st1.st_mtime >= st2.st_mtime: + return True + while True: + one = fp1.read(4096) + two = fp2.read(4096) + if one != two: + return False + if one == '': + break + return True + def build_one(checkout, target, isdev, quick): print 'Doc autobuild started in %s' % checkout os.chdir(checkout) @@ -47,15 +65,43 @@ print 'Running make autobuild' maketarget = 'autobuild-' + ('html' if quick else ('dev' if isdev else 'stable')) - if os.WEXITSTATUS(os.system('cd Doc; make %s' % maketarget)) == 2: + if os.WEXITSTATUS(os.system('cd Doc; make SPHINXBUILD=%s %s' % (SPHINXBUILD, maketarget))) == 2: print '*' * 80 return + print('Computing changed files') + changed = [] + for dirpath, dirnames, filenames in os.walk('Doc/build/html/'): + dir_rel = dirpath[len('Doc/build/html/'):] + for fn in filenames: + local_path = os.path.join(dirpath, fn) + rel_path = os.path.join(dir_rel, fn) + target_path = os.path.join(target, rel_path) + if (os.path.exists(target_path) and + not _files_changed(target_path, local_path)): + changed.append(rel_path) print 'Copying HTML files to %s' % target os.system('cp -a Doc/build/html/* %s' % target) if not quick: print 'Copying dist files' os.system('mkdir -p %s/archives' % target) os.system('cp -a Doc/dist/* %s/archives' % target) + changed.append('archives/') + for fn in os.listdir(os.path.join(target, 'archives')): + changed.append('archives/' + fn) + print '%s files changed' % len(changed) + if changed: + target_ino = os.stat(target).st_ino + targets_dir = os.path.dirname(target) + prefixes = [] + for fn in os.listdir(targets_dir): + if os.stat(os.path.join(targets_dir, fn)).st_ino == target_ino: + prefixes.append(fn) + to_purge = [] + for prefix in prefixes: + to_purge.extend(prefix + "/" + p for p in changed) + purge_cmd = 'curl -X PURGE "https://docs.python.org/{%s}"' % ','.join(to_purge) + print("Running CDN purge") + os.system(purge_cmd) print 'Finished' print '=' * 80 @@ -83,7 +129,7 @@ if args: if len(args) != 2: usage() - build_one(args[0], args[1], devel, quick) + build_one(os.path.abspath(args[0]), os.path.abspath(args[1]), devel, quick) else: for checkout, dest, devel in BRANCHES: build_one(checkout, dest, devel, quick) diff --git a/Doc/tools/sphinx-build.py b/Doc/tools/sphinx-build.py --- a/Doc/tools/sphinx-build.py +++ b/Doc/tools/sphinx-build.py @@ -15,13 +15,13 @@ if __name__ == '__main__': - if sys.version_info[:3] < (2, 4, 0): - print >>sys.stderr, """\ -Error: Sphinx needs to be executed with Python 2.4 or newer + if sys.version_info[:3] < (2, 4, 0) or sys.version_info[:3] > (3, 0, 0): + sys.stderr.write("""\ +Error: Sphinx needs to be executed with Python 2.4 or newer (not 3.x though). (If you run this from the Makefile, you can set the PYTHON variable to the path of an alternative interpreter executable, e.g., ``make html PYTHON=python2.5``). -""" +""") sys.exit(1) from sphinx import main diff --git a/Doc/tools/sphinxext/indexsidebar.html b/Doc/tools/sphinxext/indexsidebar.html --- a/Doc/tools/sphinxext/indexsidebar.html +++ b/Doc/tools/sphinxext/indexsidebar.html @@ -1,17 +1,17 @@ -

Download

-

Download these documents

-

Docs for other versions

- +

Download

+

Download these documents

+

Docs for other versions

+ -

Other resources

- +

Other resources

+ diff --git a/Doc/tools/sphinxext/layout.html b/Doc/tools/sphinxext/layout.html --- a/Doc/tools/sphinxext/layout.html +++ b/Doc/tools/sphinxext/layout.html @@ -12,11 +12,19 @@ {%- endif %} {% endblock %} +{% block relbar1 %} {% if builder != 'qthelp' %} {{ relbar() }} {% endif %} {% endblock %} +{% block relbar2 %} {% if builder != 'qthelp' %} {{ relbar() }} {% endif %} {% endblock %} {% block extrahead %} {% if not embedded %}{% endif %} {% if versionswitcher is defined and not embedded %}{% endif %} {{ super() }} + {% if builder == 'qthelp' %} + + {% endif %} {% endblock %} {% block footer %}

+ + + +\x20\x20\x20\x20 + +
 
+class C(__builtin__.object)
    Methods defined here:
+
get_answer(self)
Return say_no()
+ +
is_it_true(self)
Return self.get_answer()
+ +
say_no(self)
+ +
+Data descriptors defined here:
+
__dict__
+
dictionary for instance variables (if defined)
+
+
__weakref__
+
list of weak references to the object (if defined)
+

@@ -289,6 +333,14 @@ result, doc_loc = get_pydoc_text(xml.etree) self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link") + def test_getpager_with_stdin_none(self): + previous_stdin = sys.stdin + try: + sys.stdin = None + pydoc.getpager() # Shouldn't fail. + finally: + sys.stdin = previous_stdin + def test_non_str_name(self): # issue14638 # Treat illegal (non-str) name like no name diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -228,6 +228,17 @@ finally: test_support.unlink(test_support.TESTFN) + def test_parse_again(self): + parser = expat.ParserCreate() + file = StringIO.StringIO(data) + parser.ParseFile(file) + # Issue 6676: ensure a meaningful exception is raised when attempting + # to parse more than one XML document per xmlparser instance, + # a limitation of the Expat library. + with self.assertRaises(expat.error) as cm: + parser.ParseFile(file) + self.assertEqual(expat.ErrorString(cm.exception.code), + expat.errors.XML_ERROR_FINISHED) class NamespaceSeparatorTest(unittest.TestCase): def test_legal(self): diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -541,7 +541,7 @@ for variate, args, expected in [ (g.uniform, (10.0, 10.0), 10.0), (g.triangular, (10.0, 10.0), 10.0), - #(g.triangular, (10.0, 10.0, 10.0), 10.0), + (g.triangular, (10.0, 10.0, 10.0), 10.0), (g.expovariate, (float('inf'),), 0.0), (g.vonmisesvariate, (3.0, float('inf')), 3.0), (g.gauss, (10.0, 0.0), 10.0), 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 @@ -933,6 +933,19 @@ self.assertEqual(out.getvalue().splitlines(), ['literal 102', 'literal 111', 'literal 111']) + def test_keyword_parameters(self): + # Issue #20283: Accepting the string keyword parameter. + pat = re.compile(r'(ab)') + self.assertEqual( + pat.match(string='abracadabra', pos=7, endpos=10).span(), (7, 9)) + self.assertEqual( + pat.search(string='abracadabra', pos=3, endpos=10).span(), (7, 9)) + self.assertEqual( + pat.findall(string='abracadabra', pos=3, endpos=10), ['ab']) + self.assertEqual( + pat.split(string='abracadabra', maxsplit=1), + ['', 'ab', 'racadabra']) + def run_re_tests(): from test.re_tests import tests, SUCCEED, FAIL, SYNTAX_ERROR diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -2,6 +2,12 @@ from test import test_support from urllib2 import urlopen, HTTPError +HAVE_HTTPS = True +try: + from urllib2 import HTTPSHandler +except ImportError: + HAVE_HTTPS = False + class RobotTestCase(unittest.TestCase): def __init__(self, index, parser, url, good, agent): unittest.TestCase.__init__(self) @@ -269,6 +275,7 @@ self.skipTest('%s is unavailable' % url) self.assertEqual(parser.can_fetch("*", robots_url), False) + @unittest.skipUnless(HAVE_HTTPS, 'need SSL support to download license') def testPythonOrg(self): test_support.requires('network') with test_support.transient_internet('www.python.org'): diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1017,8 +1017,6 @@ # without calling __cmp__. self.assertEqual(cmp(a, a), 0) - self.assertRaises(TypeError, cmp, a, 12) - self.assertRaises(TypeError, cmp, "abc", a) #============================================================================== @@ -1269,17 +1267,6 @@ self.assertEqual(self.other != self.set, True) self.assertEqual(self.set != self.other, True) - def test_ge_gt_le_lt(self): - self.assertRaises(TypeError, lambda: self.set < self.other) - self.assertRaises(TypeError, lambda: self.set <= self.other) - self.assertRaises(TypeError, lambda: self.set > self.other) - self.assertRaises(TypeError, lambda: self.set >= self.other) - - self.assertRaises(TypeError, lambda: self.other < self.set) - self.assertRaises(TypeError, lambda: self.other <= self.set) - self.assertRaises(TypeError, lambda: self.other > self.set) - self.assertRaises(TypeError, lambda: self.other >= self.set) - def test_update_operator(self): try: self.set |= self.other @@ -1392,18 +1379,6 @@ #------------------------------------------------------------------------------ -class TestOnlySetsOperator(TestOnlySetsInBinaryOps): - def setUp(self): - self.set = set((1, 2, 3)) - self.other = operator.add - self.otherIsIterable = False - - def test_ge_gt_le_lt(self): - with test_support.check_py3k_warnings(): - super(TestOnlySetsOperator, self).test_ge_gt_le_lt() - -#------------------------------------------------------------------------------ - class TestOnlySetsTuple(TestOnlySetsInBinaryOps): def setUp(self): self.set = set((1, 2, 3)) @@ -1615,7 +1590,7 @@ for meth in (s.union, s.intersection, s.difference, s.symmetric_difference, s.isdisjoint): for g in (G, I, Ig, L, R): expected = meth(data) - actual = meth(G(data)) + actual = meth(g(data)) if isinstance(expected, bool): self.assertEqual(actual, expected) else: @@ -1801,7 +1776,6 @@ TestSubsetNonOverlap, TestOnlySetsNumeric, TestOnlySetsDict, - TestOnlySetsOperator, TestOnlySetsTuple, TestOnlySetsString, TestOnlySetsGenerator, diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -698,6 +698,15 @@ self._check_move_dir(self.src_dir, self.dir_other_fs, os.path.join(self.dir_other_fs, os.path.basename(self.src_dir))) + def test_move_dir_sep_to_dir(self): + self._check_move_dir(self.src_dir + os.path.sep, self.dst_dir, + os.path.join(self.dst_dir, os.path.basename(self.src_dir))) + + @unittest.skipUnless(os.path.altsep, 'requires os.path.altsep') + def test_move_dir_altsep_to_dir(self): + self._check_move_dir(self.src_dir + os.path.altsep, self.dst_dir, + os.path.join(self.dst_dir, os.path.basename(self.src_dir))) + def test_existing_file_inside_dest_dir(self): # A file with the same name inside the destination dir already exists. with open(self.dst_file, "wb"): 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 @@ -663,9 +663,15 @@ socket.getaddrinfo(None, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) - # Issue 17269 + # Issue 17269: test workaround for OS X platform bug segfault if hasattr(socket, 'AI_NUMERICSERV'): - socket.getaddrinfo("localhost", None, 0, 0, 0, socket.AI_NUMERICSERV) + try: + # The arguments here are undefined and the call may succeed + # or fail. All we care here is that it doesn't segfault. + socket.getaddrinfo("localhost", None, 0, 0, 0, + socket.AI_NUMERICSERV) + except socket.gaierror: + pass def check_sendall_interrupted(self, with_timeout): # socketpair() is not stricly required, but it makes things easier. diff --git a/Lib/test/test_spwd.py b/Lib/test/test_spwd.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_spwd.py @@ -0,0 +1,62 @@ +import os +import unittest +from test import test_support + +spwd = test_support.import_module('spwd') + + + at unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0, + 'root privileges required') +class TestSpwdRoot(unittest.TestCase): + + def test_getspall(self): + entries = spwd.getspall() + self.assertIsInstance(entries, list) + for entry in entries: + self.assertIsInstance(entry, spwd.struct_spwd) + + def test_getspnam(self): + entries = spwd.getspall() + if not entries: + self.skipTest('empty shadow password database') + random_name = entries[0].sp_nam + entry = spwd.getspnam(random_name) + self.assertIsInstance(entry, spwd.struct_spwd) + self.assertEqual(entry.sp_nam, random_name) + self.assertEqual(entry.sp_nam, entry[0]) + self.assertIsInstance(entry.sp_pwd, str) + self.assertEqual(entry.sp_pwd, entry[1]) + self.assertIsInstance(entry.sp_lstchg, int) + self.assertEqual(entry.sp_lstchg, entry[2]) + self.assertIsInstance(entry.sp_min, int) + self.assertEqual(entry.sp_min, entry[3]) + self.assertIsInstance(entry.sp_max, int) + self.assertEqual(entry.sp_max, entry[4]) + self.assertIsInstance(entry.sp_warn, int) + self.assertEqual(entry.sp_warn, entry[5]) + self.assertIsInstance(entry.sp_inact, int) + self.assertEqual(entry.sp_inact, entry[6]) + self.assertIsInstance(entry.sp_expire, int) + self.assertEqual(entry.sp_expire, entry[7]) + self.assertIsInstance(entry.sp_flag, int) + self.assertEqual(entry.sp_flag, entry[8]) + with self.assertRaises(KeyError) as cx: + spwd.getspnam('invalid user name') + self.assertEqual(str(cx.exception), "'getspnam(): name not found'") + self.assertRaises(TypeError, spwd.getspnam) + self.assertRaises(TypeError, spwd.getspnam, 0) + self.assertRaises(TypeError, spwd.getspnam, random_name, 0) + if test_support.have_unicode: + try: + unicode_name = unicode(random_name) + except UnicodeDecodeError: + pass + else: + self.assertEqual(spwd.getspnam(unicode_name), entry) + + +def test_main(): + test_support.run_unittest(TestSpwdRoot) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -390,19 +390,24 @@ def test_get_server_certificate(self): with test_support.transient_internet("svn.python.org"): - pem = ssl.get_server_certificate(("svn.python.org", 443)) + pem = ssl.get_server_certificate(("svn.python.org", 443), + ssl.PROTOCOL_SSLv23) if not pem: self.fail("No server certificate on svn.python.org:443!") try: - pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE) + pem = ssl.get_server_certificate(("svn.python.org", 443), + ssl.PROTOCOL_SSLv23, + ca_certs=CERTFILE) except ssl.SSLError: #should fail pass else: self.fail("Got server certificate %s for svn.python.org!" % pem) - pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT) + pem = ssl.get_server_certificate(("svn.python.org", 443), + ssl.PROTOCOL_SSLv23, + ca_certs=SVN_PYTHON_ORG_ROOT_CERT) if not pem: self.fail("No server certificate on svn.python.org:443!") if test_support.verbose: diff --git a/Lib/test/test_strop.py b/Lib/test/test_strop.py --- a/Lib/test/test_strop.py +++ b/Lib/test/test_strop.py @@ -4,6 +4,7 @@ r'test.test_strop|unittest') import strop import unittest +import sys from test import test_support @@ -115,6 +116,11 @@ strop.uppercase strop.whitespace + @unittest.skipUnless(sys.maxsize == 2147483647, "only for 32-bit") + def test_expandtabs_overflow(self): + s = '\t\n' * 0x10000 + 'A' * 0x1000000 + self.assertRaises(OverflowError, strop.expandtabs, s, 0x10001) + @test_support.precisionbigmemtest(size=test_support._2G - 1, memuse=5) def test_stropjoin_huge_list(self, size): a = "A" * size 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 @@ -270,12 +270,16 @@ # is exited) but there is a .pyo file. unlink(os.path.join(dirname, modname + os.extsep + 'pyo')) -# On some platforms, should not run gui test even if it is allowed -# in `use_resources'. -if sys.platform.startswith('win'): - import ctypes - import ctypes.wintypes - def _is_gui_available(): +# Check whether a gui is actually available +def _is_gui_available(): + if hasattr(_is_gui_available, 'result'): + return _is_gui_available.result + reason = None + if sys.platform.startswith('win'): + # if Python is running as a service (such as the buildbot service), + # gui interaction may be disallowed + import ctypes + import ctypes.wintypes UOI_FLAGS = 1 WSF_VISIBLE = 0x0001 class USEROBJECTFLAGS(ctypes.Structure): @@ -295,27 +299,64 @@ ctypes.byref(needed)) if not res: raise ctypes.WinError() - return bool(uof.dwFlags & WSF_VISIBLE) -else: - def _is_gui_available(): - return True + if not bool(uof.dwFlags & WSF_VISIBLE): + reason = "gui not available (WSF_VISIBLE flag not set)" + elif sys.platform == 'darwin': + # The Aqua Tk implementations on OS X can abort the process if + # being called in an environment where a window server connection + # cannot be made, for instance when invoked by a buildbot or ssh + # process not running under the same user id as the current console + # user. To avoid that, raise an exception if the window manager + # connection is not available. + from ctypes import cdll, c_int, pointer, Structure + from ctypes.util import find_library + + app_services = cdll.LoadLibrary(find_library("ApplicationServices")) + + if app_services.CGMainDisplayID() == 0: + reason = "gui tests cannot run without OS X window manager" + else: + class ProcessSerialNumber(Structure): + _fields_ = [("highLongOfPSN", c_int), + ("lowLongOfPSN", c_int)] + psn = ProcessSerialNumber() + psn_p = pointer(psn) + if ( (app_services.GetCurrentProcess(psn_p) < 0) or + (app_services.SetFrontProcess(psn_p) < 0) ): + reason = "cannot run without OS X gui process" + + # check on every platform whether tkinter can actually do anything + # but skip the test on OS X because it can cause segfaults in Cocoa Tk + # when running regrtest with the -j option (multiple threads/subprocesses) + if (not reason) and (sys.platform != 'darwin'): + try: + from Tkinter import Tk + root = Tk() + root.destroy() + except Exception as e: + err_string = str(e) + if len(err_string) > 50: + err_string = err_string[:50] + ' [...]' + reason = 'Tk unavailable due to {}: {}'.format(type(e).__name__, + err_string) + + _is_gui_available.reason = reason + _is_gui_available.result = not reason + + return _is_gui_available.result def is_resource_enabled(resource): - """Test whether a resource is enabled. Known resources are set by - regrtest.py.""" - return use_resources is not None and resource in use_resources + """Test whether a resource is enabled. + + Known resources are set by regrtest.py. If not running under regrtest.py, + all resources are assumed enabled unless use_resources has been set. + """ + return use_resources is None or resource in use_resources def requires(resource, msg=None): - """Raise ResourceDenied if the specified resource is not available. - - If the caller's module is __main__ then automatically return True. The - possibility of False being returned occurs when regrtest.py is executing.""" + """Raise ResourceDenied if the specified resource is not available.""" if resource == 'gui' and not _is_gui_available(): - raise unittest.SkipTest("Cannot use the 'gui' resource") - # see if the caller's module is __main__ - if so, treat as if - # the resource was set - if sys._getframe(1).f_globals.get("__name__") == "__main__": - return + raise ResourceDenied(_is_gui_available.reason) if not is_resource_enabled(resource): if msg is None: msg = "Use of the `%s' resource not enabled" % resource @@ -465,6 +506,52 @@ is_jython = sys.platform.startswith('java') +# FS_NONASCII: non-ASCII Unicode character encodable by +# sys.getfilesystemencoding(), or None if there is no such character. +FS_NONASCII = None +if have_unicode: + for character in ( + # First try printable and common characters to have a readable filename. + # For each character, the encoding list are just example of encodings able + # to encode the character (the list is not exhaustive). + + # U+00E6 (Latin Small Letter Ae): cp1252, iso-8859-1 + unichr(0x00E6), + # U+0130 (Latin Capital Letter I With Dot Above): cp1254, iso8859_3 + unichr(0x0130), + # U+0141 (Latin Capital Letter L With Stroke): cp1250, cp1257 + unichr(0x0141), + # U+03C6 (Greek Small Letter Phi): cp1253 + unichr(0x03C6), + # U+041A (Cyrillic Capital Letter Ka): cp1251 + unichr(0x041A), + # U+05D0 (Hebrew Letter Alef): Encodable to cp424 + unichr(0x05D0), + # U+060C (Arabic Comma): cp864, cp1006, iso8859_6, mac_arabic + unichr(0x060C), + # U+062A (Arabic Letter Teh): cp720 + unichr(0x062A), + # U+0E01 (Thai Character Ko Kai): cp874 + unichr(0x0E01), + + # Then try more "special" characters. "special" because they may be + # interpreted or displayed differently depending on the exact locale + # encoding and the font. + + # U+00A0 (No-Break Space) + unichr(0x00A0), + # U+20AC (Euro Sign) + unichr(0x20AC), + ): + try: + character.encode(sys.getfilesystemencoding())\ + .decode(sys.getfilesystemencoding()) + except UnicodeError: + pass + else: + FS_NONASCII = character + break + # Filename used for testing if os.name == 'java': # Jython disallows @ in module names @@ -1167,7 +1254,7 @@ def requires_resource(resource): if resource == 'gui' and not _is_gui_available(): - return unittest.skip("resource 'gui' is not available") + return unittest.skip(_is_gui_available.reason) if is_resource_enabled(resource): return _id else: 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 @@ -1,5 +1,6 @@ # -*- coding: iso-8859-1 -*- import unittest, test.test_support +from test.script_helper import assert_python_ok, assert_python_failure import sys, os, cStringIO import struct import operator @@ -114,90 +115,69 @@ clear_check(exc) def test_exit(self): + # call with two arguments self.assertRaises(TypeError, sys.exit, 42, 42) # call without argument - try: - sys.exit(0) - except SystemExit, exc: - self.assertEqual(exc.code, 0) - except: - self.fail("wrong exception") - else: - self.fail("no exception") + with self.assertRaises(SystemExit) as cm: + sys.exit() + self.assertIsNone(cm.exception.code) + + rc, out, err = assert_python_ok('-c', 'import sys; sys.exit()') + self.assertEqual(rc, 0) + self.assertEqual(out, b'') + self.assertEqual(err, b'') + + # call with integer argument + with self.assertRaises(SystemExit) as cm: + sys.exit(42) + self.assertEqual(cm.exception.code, 42) # call with tuple argument with one entry # entry will be unpacked - try: - sys.exit(42) - except SystemExit, exc: - self.assertEqual(exc.code, 42) - except: - self.fail("wrong exception") - else: - self.fail("no exception") - - # call with integer argument - try: + with self.assertRaises(SystemExit) as cm: sys.exit((42,)) - except SystemExit, exc: - self.assertEqual(exc.code, 42) - except: - self.fail("wrong exception") - else: - self.fail("no exception") + self.assertEqual(cm.exception.code, 42) # call with string argument - try: + with self.assertRaises(SystemExit) as cm: sys.exit("exit") - except SystemExit, exc: - self.assertEqual(exc.code, "exit") - except: - self.fail("wrong exception") - else: - self.fail("no exception") + self.assertEqual(cm.exception.code, "exit") # call with tuple argument with two entries - try: + with self.assertRaises(SystemExit) as cm: sys.exit((17, 23)) - except SystemExit, exc: - self.assertEqual(exc.code, (17, 23)) - except: - self.fail("wrong exception") - else: - self.fail("no exception") + self.assertEqual(cm.exception.code, (17, 23)) # test that the exit machinery handles SystemExits properly - import subprocess # both unnormalized... - rc = subprocess.call([sys.executable, "-c", - "raise SystemExit, 46"]) + rc, out, err = assert_python_failure('-c', 'raise SystemExit, 46') self.assertEqual(rc, 46) + self.assertEqual(out, b'') + self.assertEqual(err, b'') # ... and normalized - rc = subprocess.call([sys.executable, "-c", - "raise SystemExit(47)"]) + rc, out, err = assert_python_failure('-c', 'raise SystemExit(47)') self.assertEqual(rc, 47) + self.assertEqual(out, b'') + self.assertEqual(err, b'') - def check_exit_message(code, expected, env=None): - process = subprocess.Popen([sys.executable, "-c", code], - stderr=subprocess.PIPE, env=env) - stdout, stderr = process.communicate() - self.assertEqual(process.returncode, 1) - self.assertTrue(stderr.startswith(expected), - "%s doesn't start with %s" % (repr(stderr), repr(expected))) + def check_exit_message(code, expected, **env_vars): + rc, out, err = assert_python_failure('-c', code, **env_vars) + self.assertEqual(rc, 1) + self.assertEqual(out, b'') + self.assertTrue(err.startswith(expected), + "%s doesn't start with %s" % (repr(err), repr(expected))) - # test that stderr buffer if flushed before the exit message is written + # test that stderr buffer is flushed before the exit message is written # into stderr check_exit_message( r'import sys; sys.stderr.write("unflushed,"); sys.exit("message")', b"unflushed,message") # test that the unicode message is encoded to the stderr encoding - env = os.environ.copy() - env['PYTHONIOENCODING'] = 'latin-1' check_exit_message( r'import sys; sys.exit(u"h\xe9")', - b"h\xe9", env=env) + b"h\xe9", PYTHONIOENCODING='latin-1') def test_getdefaultencoding(self): if test.test_support.have_unicode: 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 @@ -127,6 +127,53 @@ tcl = self.interp self.assertRaises(TclError,tcl.unsetvar,'a') + def test_getint(self): + tcl = self.interp.tk + self.assertEqual(tcl.getint(' 42 '), 42) + self.assertEqual(tcl.getint(42), 42) + self.assertRaises(TypeError, tcl.getint) + self.assertRaises(TypeError, tcl.getint, '42', '10') + self.assertRaises(TypeError, tcl.getint, 42.0) + self.assertRaises(TclError, tcl.getint, 'a') + self.assertRaises((TypeError, ValueError, TclError), + tcl.getint, '42\0') + if test_support.have_unicode: + self.assertEqual(tcl.getint(unicode('42')), 42) + self.assertRaises((UnicodeEncodeError, ValueError, TclError), + tcl.getint, '42' + unichr(0xd800)) + + def test_getdouble(self): + tcl = self.interp.tk + self.assertEqual(tcl.getdouble(' 42 '), 42.0) + self.assertEqual(tcl.getdouble(' 42.5 '), 42.5) + self.assertEqual(tcl.getdouble(42.5), 42.5) + self.assertRaises(TypeError, tcl.getdouble) + self.assertRaises(TypeError, tcl.getdouble, '42.5', '10') + self.assertRaises(TypeError, tcl.getdouble, 42) + self.assertRaises(TclError, tcl.getdouble, 'a') + self.assertRaises((TypeError, ValueError, TclError), + tcl.getdouble, '42.5\0') + if test_support.have_unicode: + self.assertEqual(tcl.getdouble(unicode('42.5')), 42.5) + self.assertRaises((UnicodeEncodeError, ValueError, TclError), + tcl.getdouble, '42.5' + unichr(0xd800)) + + def test_getboolean(self): + tcl = self.interp.tk + self.assertIs(tcl.getboolean('on'), True) + self.assertIs(tcl.getboolean('1'), True) + self.assertEqual(tcl.getboolean(42), 42) + self.assertRaises(TypeError, tcl.getboolean) + self.assertRaises(TypeError, tcl.getboolean, 'on', '1') + self.assertRaises(TypeError, tcl.getboolean, 1.0) + self.assertRaises(TclError, tcl.getboolean, 'a') + self.assertRaises((TypeError, ValueError, TclError), + tcl.getboolean, 'on\0') + if test_support.have_unicode: + self.assertIs(tcl.getboolean(unicode('on')), True) + self.assertRaises((UnicodeEncodeError, ValueError, TclError), + tcl.getboolean, 'on' + unichr(0xd800)) + def testEvalFile(self): tcl = self.interp filename = "testEvalFile.tcl" @@ -386,6 +433,7 @@ result.append(arg) return arg self.interp.createcommand('testfunc', testfunc) + self.addCleanup(self.interp.tk.deletecommand, 'testfunc') def check(value, expected, expected2=None, eq=self.assertEqual): if expected2 is None: expected2 = expected @@ -521,6 +569,7 @@ for arg, res in testcases: self.assertEqual(split(arg), res) +character_size = 4 if sys.maxunicode > 0xFFFF else 2 class BigmemTclTest(unittest.TestCase): @@ -530,10 +579,59 @@ @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): + def test_huge_string_call(self, size): value = ' ' * size self.assertRaises(OverflowError, self.interp.call, 'set', '_', value) + @test_support.cpython_only + @unittest.skipUnless(test_support.have_unicode, 'requires unicode support') + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @test_support.precisionbigmemtest(size=INT_MAX + 1, + memuse=2*character_size + 2, + dry_run=False) + def test_huge_unicode_call(self, size): + value = unicode(' ') * size + self.assertRaises(OverflowError, self.interp.call, 'set', '_', value) + + + @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=9, dry_run=False) + def test_huge_string_builtins(self, size): + value = '1' + ' ' * size + self.check_huge_string_builtins(value) + + @test_support.cpython_only + @unittest.skipUnless(test_support.have_unicode, 'requires unicode support') + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @test_support.precisionbigmemtest(size=INT_MAX + 1, + memuse=2*character_size + 7, + dry_run=False) + def test_huge_unicode_builtins(self, size): + value = unicode('1' + ' ' * size) + self.check_huge_string_builtins(value) + + def check_huge_string_builtins(self, value): + self.assertRaises(OverflowError, self.interp.tk.getint, value) + self.assertRaises(OverflowError, self.interp.tk.getdouble, value) + self.assertRaises(OverflowError, self.interp.tk.getboolean, value) + self.assertRaises(OverflowError, self.interp.eval, value) + self.assertRaises(OverflowError, self.interp.evalfile, value) + self.assertRaises(OverflowError, self.interp.record, value) + self.assertRaises(OverflowError, self.interp.adderrorinfo, value) + self.assertRaises(OverflowError, self.interp.setvar, value, 'x', 'a') + self.assertRaises(OverflowError, self.interp.setvar, 'x', value, 'a') + self.assertRaises(OverflowError, self.interp.unsetvar, value) + self.assertRaises(OverflowError, self.interp.unsetvar, 'x', value) + self.assertRaises(OverflowError, self.interp.adderrorinfo, value) + self.assertRaises(OverflowError, self.interp.exprstring, value) + self.assertRaises(OverflowError, self.interp.exprlong, value) + self.assertRaises(OverflowError, self.interp.exprboolean, value) + self.assertRaises(OverflowError, self.interp.splitlist, value) + self.assertRaises(OverflowError, self.interp.split, value) + self.assertRaises(OverflowError, self.interp.createcommand, value, max) + self.assertRaises(OverflowError, self.interp.deletecommand, value) + def setUpModule(): if test_support.verbose: diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -771,6 +771,24 @@ pass self.assertRaises(ValueError, use_closed) + def test_no_leak_fd(self): + # Issue #21058: don't leak file descriptor when fdopen() fails + old_close = os.close + old_fdopen = os.fdopen + closed = [] + def close(fd): + closed.append(fd) + def fdopen(*args): + raise ValueError() + os.close = close + os.fdopen = fdopen + try: + self.assertRaises(ValueError, tempfile.NamedTemporaryFile) + self.assertEqual(len(closed), 1) + finally: + os.close = old_close + os.fdopen = old_fdopen + # How to test the mode and bufsize parameters? test_classes.append(test_NamedTemporaryFile) 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 @@ -700,6 +700,49 @@ output = "end of worker thread\nend of main thread\n" self.assertScriptHasOutput(script, output) + @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") + def test_6_daemon_threads(self): + # Check that a daemon thread cannot crash the interpreter on shutdown + # by manipulating internal structures that are being disposed of in + # the main thread. + script = """if True: + import os + import random + import sys + import time + import threading + + thread_has_run = set() + + def random_io(): + '''Loop for a while sleeping random tiny amounts and doing some I/O.''' + while True: + in_f = open(os.__file__, 'rb') + stuff = in_f.read(200) + null_f = open(os.devnull, 'wb') + null_f.write(stuff) + time.sleep(random.random() / 1995) + null_f.close() + in_f.close() + thread_has_run.add(threading.current_thread()) + + def main(): + count = 0 + for _ in range(40): + new_thread = threading.Thread(target=random_io) + new_thread.daemon = True + new_thread.start() + count += 1 + while len(thread_has_run) < count: + time.sleep(0.001) + # Trigger process shutdown + sys.exit(0) + + main() + """ + rc, out, err = assert_python_ok('-c', script) + self.assertFalse(err) + @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") def test_reinit_tls_after_fork(self): diff --git a/Lib/test/test_tk.py b/Lib/test/test_tk.py --- a/Lib/test/test_tk.py +++ b/Lib/test/test_tk.py @@ -1,8 +1,9 @@ import os from test import test_support -# Skip test if _tkinter wasn't built. +# Skip test if _tkinter wasn't built or gui resource is not available. test_support.import_module('_tkinter') +test_support.requires('gui') this_dir = os.path.dirname(os.path.abspath(__file__)) lib_tk_test = os.path.abspath(os.path.join(this_dir, os.path.pardir, @@ -11,19 +12,10 @@ with test_support.DirsOnSysPath(lib_tk_test): import runtktests -# Skip test if tk cannot be initialized. -runtktests.check_tk_availability() - -def test_main(enable_gui=False): - if enable_gui: - if test_support.use_resources is None: - test_support.use_resources = ['gui'] - elif 'gui' not in test_support.use_resources: - test_support.use_resources.append('gui') - +def test_main(): with test_support.DirsOnSysPath(lib_tk_test): test_support.run_unittest( *runtktests.get_tests(text=False, packages=['test_tkinter'])) if __name__ == '__main__': - test_main(enable_gui=True) + test_main() diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -4,7 +4,7 @@ >>> import glob, random, sys The tests can be really simple. Given a small fragment of source -code, print out a table with tokens. The ENDMARK is omitted for +code, print out a table with tokens. The ENDMARKER is omitted for brevity. >>> dump_tokens("1 + 1") @@ -559,9 +559,10 @@ from test import test_support from tokenize import (untokenize, generate_tokens, NUMBER, NAME, OP, - STRING, ENDMARKER, tok_name) + STRING, ENDMARKER, tok_name, Untokenizer) from StringIO import StringIO import os +from unittest import TestCase def dump_tokens(s): """Print out the tokens in s in a table format. @@ -614,12 +615,47 @@ return untokenize(result) +class UntokenizeTest(TestCase): + + def test_bad_input_order(self): + # raise if previous row + u = Untokenizer() + u.prev_row = 2 + u.prev_col = 2 + with self.assertRaises(ValueError) as cm: + u.add_whitespace((1,3)) + self.assertEqual(cm.exception.args[0], + 'start (1,3) precedes previous end (2,2)') + # raise if previous column in row + self.assertRaises(ValueError, u.add_whitespace, (2,1)) + + def test_backslash_continuation(self): + # The problem is that \ leaves no token + u = Untokenizer() + u.prev_row = 1 + u.prev_col = 1 + u.tokens = [] + u.add_whitespace((2, 0)) + self.assertEqual(u.tokens, ['\\\n']) + u.prev_row = 2 + u.add_whitespace((4, 4)) + self.assertEqual(u.tokens, ['\\\n', '\\\n\\\n', ' ']) + + def test_iter_compat(self): + u = Untokenizer() + token = (NAME, 'Hello') + u.compat(token, iter([])) + self.assertEqual(u.tokens, ["Hello "]) + u = Untokenizer() + self.assertEqual(u.untokenize(iter([token])), 'Hello ') + + __test__ = {"doctests" : doctests, 'decistmt': decistmt} - def test_main(): from test import test_tokenize test_support.run_doctest(test_tokenize, True) + test_support.run_unittest(UntokenizeTest) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_ttk_guionly.py b/Lib/test/test_ttk_guionly.py --- a/Lib/test/test_ttk_guionly.py +++ b/Lib/test/test_ttk_guionly.py @@ -2,8 +2,9 @@ import unittest from test import test_support -# Skip this test if _tkinter wasn't built. +# Skip this test if _tkinter wasn't built or gui resource is not available. test_support.import_module('_tkinter') +test_support.requires('gui') this_dir = os.path.dirname(os.path.abspath(__file__)) lib_tk_test = os.path.abspath(os.path.join(this_dir, os.path.pardir, @@ -12,9 +13,6 @@ with test_support.DirsOnSysPath(lib_tk_test): import runtktests -# Skip test if tk cannot be initialized. -runtktests.check_tk_availability() - import ttk from _tkinter import TclError @@ -24,13 +22,7 @@ # assuming ttk is not available raise unittest.SkipTest("ttk not available: %s" % msg) -def test_main(enable_gui=False): - if enable_gui: - if test_support.use_resources is None: - test_support.use_resources = ['gui'] - elif 'gui' not in test_support.use_resources: - test_support.use_resources.append('gui') - +def test_main(): with test_support.DirsOnSysPath(lib_tk_test): from test_ttk.support import get_tk_root try: @@ -40,4 +32,4 @@ get_tk_root().destroy() if __name__ == '__main__': - test_main(enable_gui=True) + test_main() 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 @@ -1463,6 +1463,27 @@ self.assertEqual(u'{0:10000}'.format(u''), u' ' * 10000) self.assertEqual(u'{0:10000000}'.format(u''), u' ' * 10000000) + # issue 12546: use \x00 as a fill character + self.assertEqual('{0:\x00<6s}'.format('foo'), 'foo\x00\x00\x00') + self.assertEqual('{0:\x01<6s}'.format('foo'), 'foo\x01\x01\x01') + self.assertEqual('{0:\x00^6s}'.format('foo'), '\x00foo\x00\x00') + self.assertEqual('{0:^6s}'.format('foo'), ' foo ') + + self.assertEqual('{0:\x00<6}'.format(3), '3\x00\x00\x00\x00\x00') + self.assertEqual('{0:\x01<6}'.format(3), '3\x01\x01\x01\x01\x01') + self.assertEqual('{0:\x00^6}'.format(3), '\x00\x003\x00\x00\x00') + self.assertEqual('{0:<6}'.format(3), '3 ') + + self.assertEqual('{0:\x00<6}'.format(3.14), '3.14\x00\x00') + self.assertEqual('{0:\x01<6}'.format(3.14), '3.14\x01\x01') + self.assertEqual('{0:\x00^6}'.format(3.14), '\x003.14\x00') + self.assertEqual('{0:^6}'.format(3.14), ' 3.14 ') + + self.assertEqual('{0:\x00<12}'.format(3+2.0j), '(3+2j)\x00\x00\x00\x00\x00\x00') + self.assertEqual('{0:\x01<12}'.format(3+2.0j), '(3+2j)\x01\x01\x01\x01\x01\x01') + self.assertEqual('{0:\x00^12}'.format(3+2.0j), '\x00\x00\x00(3+2j)\x00\x00\x00') + self.assertEqual('{0:^12}'.format(3+2.0j), ' (3+2j) ') + # format specifiers for user defined type self.assertEqual(u'{0:abc}'.format(C()), u'abc') 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 @@ -78,7 +78,7 @@ # underlying socket # delve deep into response to fetch socket._socketobject - response = _urlopen_with_retry("http://www.python.org/") + response = _urlopen_with_retry("http://www.example.com/") abused_fileobject = response.fp self.assertIs(abused_fileobject.__class__, socket._fileobject) httpresponse = abused_fileobject._sock @@ -155,15 +155,15 @@ ## self._test_urls(urls, self._extra_handlers()+[bauth, dauth]) def test_urlwithfrag(self): - urlwith_frag = "http://docs.python.org/2/glossary.html#glossary" + urlwith_frag = "https://docs.python.org/2/glossary.html#glossary" with test_support.transient_internet(urlwith_frag): req = urllib2.Request(urlwith_frag) res = urllib2.urlopen(req) self.assertEqual(res.geturl(), - "http://docs.python.org/2/glossary.html#glossary") + "https://docs.python.org/2/glossary.html#glossary") def test_fileno(self): - req = urllib2.Request("http://www.python.org") + req = urllib2.Request("http://www.example.com") opener = urllib2.build_opener() res = opener.open(req) try: @@ -251,14 +251,14 @@ class TimeoutTest(unittest.TestCase): def test_http_basic(self): self.assertIsNone(socket.getdefaulttimeout()) - url = "http://www.python.org" + url = "http://www.example.com" with test_support.transient_internet(url, timeout=None): u = _urlopen_with_retry(url) self.assertIsNone(u.fp._sock.fp._sock.gettimeout()) def test_http_default_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) - url = "http://www.python.org" + url = "http://www.example.com" with test_support.transient_internet(url): socket.setdefaulttimeout(60) try: @@ -269,7 +269,7 @@ def test_http_no_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) - url = "http://www.python.org" + url = "http://www.example.com" with test_support.transient_internet(url): socket.setdefaulttimeout(60) try: @@ -279,7 +279,7 @@ self.assertIsNone(u.fp._sock.fp._sock.gettimeout()) def test_http_timeout(self): - url = "http://www.python.org" + url = "http://www.example.com" with test_support.transient_internet(url): u = _urlopen_with_retry(url, timeout=120) self.assertEqual(u.fp._sock.fp._sock.gettimeout(), 120) diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -34,7 +34,7 @@ socket.setdefaulttimeout(None) def testURLread(self): - f = _open_with_retry(urllib.urlopen, "http://www.python.org/") + f = _open_with_retry(urllib.urlopen, "http://www.example.com/") x = f.read() class urlopenNetworkTests(unittest.TestCase): @@ -46,7 +46,7 @@ for transparent redirection have been written. setUp is not used for always constructing a connection to - http://www.python.org/ since there a few tests that don't use that address + http://www.example.com/ since there a few tests that don't use that address and making a connection is expensive enough to warrant minimizing unneeded connections. @@ -57,7 +57,7 @@ def test_basic(self): # Simple test expected to pass. - open_url = self.urlopen("http://www.python.org/") + open_url = self.urlopen("http://www.example.com/") for attr in ("read", "readline", "readlines", "fileno", "close", "info", "geturl"): self.assertTrue(hasattr(open_url, attr), "object returned from " @@ -69,7 +69,7 @@ def test_readlines(self): # Test both readline and readlines. - open_url = self.urlopen("http://www.python.org/") + open_url = self.urlopen("http://www.example.com/") try: self.assertIsInstance(open_url.readline(), basestring, "readline did not return a string") @@ -80,7 +80,7 @@ def test_info(self): # Test 'info'. - open_url = self.urlopen("http://www.python.org/") + open_url = self.urlopen("http://www.example.com/") try: info_obj = open_url.info() finally: @@ -92,7 +92,7 @@ def test_geturl(self): # Make sure same URL as opened is returned by geturl. - URL = "http://www.python.org/" + URL = "http://www.example.com/" open_url = self.urlopen(URL) try: gotten_url = open_url.geturl() @@ -102,7 +102,7 @@ def test_getcode(self): # test getcode() with the fancy opener to get 404 error codes - URL = "http://www.python.org/XXXinvalidXXX" + URL = "http://www.example.com/XXXinvalidXXX" open_url = urllib.FancyURLopener().open(URL) try: code = open_url.getcode() @@ -114,7 +114,7 @@ @unittest.skipUnless(hasattr(os, 'fdopen'), 'os.fdopen not available') def test_fileno(self): # Make sure fd returned by fileno is valid. - open_url = self.urlopen("http://www.python.org/") + open_url = self.urlopen("http://www.example.com/") fd = open_url.fileno() FILE = os.fdopen(fd) try: @@ -152,7 +152,7 @@ def test_basic(self): # Test basic functionality. - file_location,info = self.urlretrieve("http://www.python.org/") + file_location,info = self.urlretrieve("http://www.example.com/") self.assertTrue(os.path.exists(file_location), "file location returned by" " urlretrieve is not a valid path") FILE = file(file_location) @@ -165,7 +165,7 @@ def test_specified_path(self): # Make sure that specifying the location of the file to write to works. - file_location,info = self.urlretrieve("http://www.python.org/", + file_location,info = self.urlretrieve("http://www.example.com/", test_support.TESTFN) self.assertEqual(file_location, test_support.TESTFN) self.assertTrue(os.path.exists(file_location)) @@ -178,13 +178,13 @@ def test_header(self): # Make sure header returned as 2nd value from urlretrieve is good. - file_location, header = self.urlretrieve("http://www.python.org/") + file_location, header = self.urlretrieve("http://www.example.com/") os.unlink(file_location) self.assertIsInstance(header, mimetools.Message, "header is not an instance of mimetools.Message") def test_data_header(self): - logo = "http://www.python.org/community/logos/python-logo-master-v3-TM.png" + logo = "http://www.example.com/" file_location, fileheaders = self.urlretrieve(logo) os.unlink(file_location) datevalue = fileheaders.getheader('Date') diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -329,6 +329,15 @@ finally: DeleteKey(HKEY_CURRENT_USER, test_key_name) + def test_setvalueex_with_memoryview(self): + try: + with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck: + self.assertNotEqual(ck.handle, 0) + with self.assertRaises(TypeError): + SetValueEx(ck, "test_name", None, REG_BINARY, memoryview('val')) + finally: + DeleteKey(HKEY_CURRENT_USER, test_key_name) + def test_queryvalueex_return_value(self): # Test for Issue #16759, return unsigned int from QueryValueEx. # Reg2Py, which gets called by QueryValueEx, was returning a value diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -8,7 +8,6 @@ import io import sys import time -import shutil import struct import zipfile import unittest @@ -19,7 +18,7 @@ from unittest import skipUnless from test.test_support import TESTFN, TESTFN_UNICODE, TESTFN_ENCODING, \ - run_unittest, findfile, unlink, check_warnings + run_unittest, findfile, unlink, rmtree, check_warnings try: TESTFN_UNICODE.encode(TESTFN_ENCODING) except (UnicodeError, TypeError): @@ -367,7 +366,8 @@ produces the expected result.""" with zipfile.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN) - self.assertEqual(zipfp.read(TESTFN), open(TESTFN).read()) + with open(TESTFN,'r') as fid: + self.assertEqual(zipfp.read(TESTFN), fid.read()) @skipUnless(zlib, "requires zlib") def test_per_file_compression(self): @@ -406,11 +406,12 @@ self.assertEqual(writtenfile, correctfile) # make sure correct data is in correct file - self.assertEqual(fdata, open(writtenfile, "rb").read()) + with open(writtenfile, "rb") as fid: + self.assertEqual(fdata, fid.read()) os.remove(writtenfile) # remove the test file subdirectories - shutil.rmtree(os.path.join(os.getcwd(), 'ziptest2dir')) + rmtree(os.path.join(os.getcwd(), 'ziptest2dir')) def test_extract_all(self): with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: @@ -422,11 +423,12 @@ for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(os.getcwd(), fpath) - self.assertEqual(fdata, open(outfile, "rb").read()) + with open(outfile, "rb") as fid: + self.assertEqual(fdata, fid.read()) os.remove(outfile) # remove the test file subdirectories - shutil.rmtree(os.path.join(os.getcwd(), 'ziptest2dir')) + rmtree(os.path.join(os.getcwd(), 'ziptest2dir')) def check_file(self, filename, content): self.assertTrue(os.path.isfile(filename)) @@ -511,12 +513,12 @@ self.assertEqual(writtenfile, correctfile, msg="extract %r" % arcname) self.check_file(correctfile, content) - shutil.rmtree('target') + rmtree('target') with zipfile.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall(targetpath) self.check_file(correctfile, content) - shutil.rmtree('target') + rmtree('target') correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) @@ -525,12 +527,12 @@ self.assertEqual(writtenfile, correctfile, msg="extract %r" % arcname) self.check_file(correctfile, content) - shutil.rmtree(fixedname.split('/')[0]) + rmtree(fixedname.split('/')[0]) with zipfile.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall() self.check_file(correctfile, content) - shutil.rmtree(fixedname.split('/')[0]) + rmtree(fixedname.split('/')[0]) os.remove(TESTFN2) @@ -775,11 +777,12 @@ self.assertNotIn('mod2.txt', names) finally: - shutil.rmtree(TESTFN2) + rmtree(TESTFN2) def test_write_non_pyfile(self): with zipfile.PyZipFile(TemporaryFile(), "w") as zipfp: - open(TESTFN, 'w').write('most definitely not a python file') + with open(TESTFN, 'w') as fid: + fid.write('most definitely not a python file') self.assertRaises(RuntimeError, zipfp.writepy, TESTFN) os.remove(TESTFN) @@ -942,8 +945,9 @@ self.assertRaises(RuntimeError, zipf.open, "foo.txt") self.assertRaises(RuntimeError, zipf.testzip) self.assertRaises(RuntimeError, zipf.writestr, "bogus.txt", "bogus") - open(TESTFN, 'w').write('zipfile test data') - self.assertRaises(RuntimeError, zipf.write, TESTFN) + with open(TESTFN, 'w') as fid: + fid.write('zipfile test data') + self.assertRaises(RuntimeError, zipf.write, TESTFN) def test_bad_constructor_mode(self): """Check that bad modes passed to ZipFile constructor are caught.""" @@ -1129,6 +1133,7 @@ pass try: zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf.close() except zipfile.BadZipfile: self.fail("Unable to create empty ZIP file in 'w' mode") @@ -1136,6 +1141,7 @@ pass try: zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf.close() except: self.fail("Unable to create empty ZIP file in 'a' mode") @@ -1151,6 +1157,21 @@ self.assertRaises(ValueError, zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0)) + def test_zipfile_with_short_extra_field(self): + """If an extra field in the header is less than 4 bytes, skip it.""" + zipdata = ( + b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e' + b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab' + b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00' + b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00' + b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00' + b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' + b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertIsNone(zipf.testzip()) + def tearDown(self): unlink(TESTFN) unlink(TESTFN2) @@ -1332,12 +1353,11 @@ # Verify that (when the ZipFile is in control of creating file objects) # multiple open() calls can be made without interfering with each other. with zipfile.ZipFile(TESTFN2, mode="r") as zipf: - zopen1 = zipf.open('ones') - zopen2 = zipf.open('ones') - data1 = zopen1.read(500) - data2 = zopen2.read(500) - data1 += zopen1.read(500) - data2 += zopen2.read(500) + with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2: + data1 = zopen1.read(500) + data2 = zopen2.read(500) + data1 += zopen1.read(500) + data2 += zopen2.read(500) self.assertEqual(data1, data2) def test_different_file(self): @@ -1364,6 +1384,17 @@ self.assertEqual(data1, '1'*FIXEDTEST_SIZE) self.assertEqual(data2, '2'*FIXEDTEST_SIZE) + def test_many_opens(self): + # Verify that read() and open() promptly close the file descriptor, + # and don't rely on the garbage collector to free resources. + with zipfile.ZipFile(TESTFN2, mode="r") as zipf: + for x in range(100): + zipf.read('ones') + with zipf.open('ones') as zopen1: + pass + with open(os.devnull) as f: + self.assertLess(f.fileno(), 100) + def tearDown(self): unlink(TESTFN2) @@ -1386,12 +1417,12 @@ def test_store_dir(self): os.mkdir(os.path.join(TESTFN2, "x")) - zipf = zipfile.ZipFile(TESTFN, "w") - zipf.write(os.path.join(TESTFN2, "x"), "x") - self.assertTrue(zipf.filelist[0].filename.endswith("x/")) + with zipfile.ZipFile(TESTFN, "w") as zipf: + zipf.write(os.path.join(TESTFN2, "x"), "x") + self.assertTrue(zipf.filelist[0].filename.endswith("x/")) def tearDown(self): - shutil.rmtree(TESTFN2) + rmtree(TESTFN2) if os.path.exists(TESTFN): unlink(TESTFN) @@ -1405,7 +1436,8 @@ for n, s in enumerate(self.seps): self.arcdata[s] = s.join(self.line_gen) + s self.arcfiles[s] = '%s-%d' % (TESTFN, n) - open(self.arcfiles[s], "wb").write(self.arcdata[s]) + with open(self.arcfiles[s], "wb") as fid: + fid.write(self.arcdata[s]) def make_test_archive(self, f, compression): # Create the ZIP archive @@ -1474,8 +1506,9 @@ # Read the ZIP archive with zipfile.ZipFile(f, "r") as zipfp: for sep, fn in self.arcfiles.items(): - for line, zipline in zip(self.line_gen, zipfp.open(fn, "rU")): - self.assertEqual(zipline, line + '\n') + with zipfp.open(fn, "rU") as fid: + for line, zipline in zip(self.line_gen, fid): + self.assertEqual(zipline, line + '\n') def test_read_stored(self): for f in (TESTFN2, TemporaryFile(), StringIO()): diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -1,4 +1,3 @@ -import io import sys import os import marshal @@ -56,27 +55,6 @@ TEMP_ZIP = os.path.abspath("junk95142" + os.extsep + "zip") -def _write_zip_package(zipname, files, - data_to_prepend=b"", compression=ZIP_STORED): - z = ZipFile(zipname, "w") - try: - for name, (mtime, data) in files.items(): - zinfo = ZipInfo(name, time.localtime(mtime)) - zinfo.compress_type = compression - z.writestr(zinfo, data) - finally: - z.close() - - if data_to_prepend: - # Prepend data to the start of the zipfile - with open(zipname, "rb") as f: - zip_data = f.read() - - with open(zipname, "wb") as f: - f.write(data_to_prepend) - f.write(zip_data) - - class UncompressedZipImportTestCase(ImportHooksBaseTestCase): compression = ZIP_STORED @@ -89,9 +67,26 @@ ImportHooksBaseTestCase.setUp(self) def doTest(self, expected_ext, files, *modules, **kw): - _write_zip_package(TEMP_ZIP, files, data_to_prepend=kw.get("stuff"), - compression=self.compression) + z = ZipFile(TEMP_ZIP, "w") try: + for name, (mtime, data) in files.items(): + zinfo = ZipInfo(name, time.localtime(mtime)) + zinfo.compress_type = self.compression + z.writestr(zinfo, data) + z.close() + + stuff = kw.get("stuff", None) + if stuff is not None: + # Prepend 'stuff' to the start of the zipfile + f = open(TEMP_ZIP, "rb") + data = f.read() + f.close() + + f = open(TEMP_ZIP, "wb") + f.write(stuff) + f.write(data) + f.close() + sys.path.insert(0, TEMP_ZIP) mod = __import__(".".join(modules), globals(), locals(), @@ -106,6 +101,7 @@ self.assertEqual(file, os.path.join(TEMP_ZIP, *modules) + expected_ext) finally: + z.close() os.remove(TEMP_ZIP) def testAFakeZlib(self): @@ -127,7 +123,7 @@ # so we'll simply skip it then. Bug #765456. # if "zlib" in sys.builtin_module_names: - self.skipTest('zlib is a builtin module') + return if "zlib" in sys.modules: del sys.modules["zlib"] files = {"zlib.py": (NOW, test_src)} @@ -391,152 +387,6 @@ compression = ZIP_DEFLATED -class ZipFileModifiedAfterImportTestCase(ImportHooksBaseTestCase): - def setUp(self): - zipimport._zip_directory_cache.clear() - zipimport._zip_stat_cache.clear() - # save sys.modules so we can unimport everything done by our tests. - self._sys_modules_orig = dict(sys.modules) - ImportHooksBaseTestCase.setUp(self) - - def tearDown(self): - ImportHooksBaseTestCase.tearDown(self) - # The closest we can come to un-importing our zipped up test modules. - sys.modules.clear() - sys.modules.update(self._sys_modules_orig) - if os.path.exists(TEMP_ZIP): - os.remove(TEMP_ZIP) - - def setUpZipFileModuleAndTestImports(self): - # Create a .zip file to test with - self.zipfile_path = TEMP_ZIP - packdir = TESTPACK + os.sep - files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc), - packdir + TESTMOD + ".py": (NOW, "test_value = 38\n"), - "ziptest_a.py": (NOW, "test_value = 23\n"), - "ziptest_b.py": (NOW, "test_value = 42\n"), - "ziptest_c.py": (NOW, "test_value = 1337\n")} - _write_zip_package(self.zipfile_path, files) - self.assertTrue(os.path.exists(self.zipfile_path)) - sys.path.insert(0, self.zipfile_path) - - self.testpack_testmod = TESTPACK + "." + TESTMOD - - with io.open(self.zipfile_path, "rb") as orig_zip_file: - self.orig_zip_file_contents = orig_zip_file.read() - - # Import something out of the zipfile and confirm it is correct. - testmod = __import__(self.testpack_testmod, - globals(), locals(), ["__dummy__"]) - self.assertEqual(testmod.test_value, 38) - del sys.modules[TESTPACK] - del sys.modules[self.testpack_testmod] - - # Import something else out of the zipfile and confirm it is correct. - ziptest_b = __import__("ziptest_b", globals(), locals(), ["test_value"]) - self.assertEqual(ziptest_b.test_value, 42) - del sys.modules["ziptest_b"] - - def truncateAndFillZipWithNonZipGarbage(self): - with io.open(self.zipfile_path, "wb") as byebye_valid_zip_file: - byebye_valid_zip_file.write(b"Tear down this wall!\n"*1987) - - def restoreZipFileWithDifferentHeaderOffsets(self): - """Make it a valid zipfile with some garbage at the start.""" - # This alters all of the caches offsets within the file. - with io.open(self.zipfile_path, "wb") as new_zip_file: - new_zip_file.write(b"X"*1991) # The year Python was created. - new_zip_file.write(self.orig_zip_file_contents) - - def testZipFileChangesAfterFirstImport(self): - """Alter the zip file after caching its index and try an import.""" - self.setUpZipFileModuleAndTestImports() - # The above call cached the .zip table of contents during its tests. - self.truncateAndFillZipWithNonZipGarbage() - # Now that the zipfile has been replaced, import something else from it - # which should fail as the file contents are now garbage. - with self.assertRaises(ImportError): - ziptest_a = __import__("ziptest_a", {}, {}, ["test_value"]) - # The code path used by the __import__ call is different than - # that used by import statements. Try these as well. Some of - # these may create new zipimporter instances. We need to - # function properly using the global zipimport caches - # regardless of how many zipimporter instances for the same - # .zip file exist. - with self.assertRaises(ImportError): - import ziptest_a - with self.assertRaises(ImportError): - from ziptest_a import test_value - with self.assertRaises(ImportError): - exec("from {} import {}".format(TESTPACK, TESTMOD), {}) - - # Alters all of the offsets within the file - self.restoreZipFileWithDifferentHeaderOffsets() - - # Now that the zip file has been "restored" to a valid but different - # zipfile all zipimporter instances should *successfully* re-read the - # new file's end of file central index and be able to import again. - - # Importing a submodule triggers a different import code path. - test_ns = {} - exec("import " + self.testpack_testmod, test_ns) - self.assertEqual(getattr(test_ns[TESTPACK], TESTMOD).test_value, 38) - test_ns = {} - exec("from {} import {}".format(TESTPACK, TESTMOD), test_ns) - self.assertEqual(test_ns[TESTMOD].test_value, 38) - - ziptest_a = __import__("ziptest_a", {}, {}, ["test_value"]) - self.assertEqual(ziptest_a.test_value, 23) - ziptest_c = __import__("ziptest_c", {}, {}, ["test_value"]) - self.assertEqual(ziptest_c.test_value, 1337) - - def testZipFileSubpackageImport(self): - """Import via multiple sys.path entries into parts of the zip.""" - self.setUpZipFileModuleAndTestImports() - # Put a subdirectory within the zip file into the import path. - sys.path.insert(0, self.zipfile_path + os.sep + TESTPACK) - - testmod = __import__(TESTMOD, {}, {}, ["test_value"]) - self.assertEqual(testmod.test_value, 38) - del sys.modules[TESTMOD] - test_ns = {} - exec("from {} import test_value".format(TESTMOD), test_ns) - self.assertEqual(test_ns["test_value"], 38) - del sys.modules[TESTMOD] - - # Confirm that imports from the top level of the zip file - # (already in sys.path from the setup call above) still work. - ziptest_a = __import__("ziptest_a", {}, {}, ["test_value"]) - self.assertEqual(ziptest_a.test_value, 23) - del sys.modules["ziptest_a"] - import ziptest_c - self.assertEqual(ziptest_c.test_value, 1337) - del sys.modules["ziptest_c"] - - self.truncateAndFillZipWithNonZipGarbage() - # Imports should now fail. - with self.assertRaises(ImportError): - testmod = __import__(TESTMOD, {}, {}, ["test_value"]) - with self.assertRaises(ImportError): - exec("from {} import test_value".format(TESTMOD), {}) - with self.assertRaises(ImportError): - import ziptest_a - - self.restoreZipFileWithDifferentHeaderOffsets() - # Imports should work again, the central directory TOC will be re-read. - testmod = __import__(TESTMOD, {}, {}, ["test_value"]) - self.assertEqual(testmod.test_value, 38) - del sys.modules[TESTMOD] - test_ns = {} - exec("from {} import test_value".format(TESTMOD), test_ns) - self.assertEqual(test_ns['test_value'], 38) - - ziptest_a = __import__("ziptest_a", {}, {}, ["test_value"]) - self.assertEqual(ziptest_a.test_value, 23) - import ziptest_c - self.assertEqual(ziptest_c.test_value, 1337) - - class BadFileZipImportTestCase(unittest.TestCase): def assertZipFailure(self, filename): self.assertRaises(zipimport.ZipImportError, @@ -614,7 +464,6 @@ UncompressedZipImportTestCase, CompressedZipImportTestCase, BadFileZipImportTestCase, - ZipFileModifiedAfterImportTestCase, ) finally: test_support.unlink(TESTMOD) diff --git a/Lib/tokenize.py b/Lib/tokenize.py --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -26,6 +26,7 @@ __credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, ' 'Skip Montanaro, Raymond Hettinger') +from itertools import chain import string, re from token import * @@ -184,17 +185,26 @@ def add_whitespace(self, start): row, col = start - assert row <= self.prev_row + if row < self.prev_row or row == self.prev_row and col < self.prev_col: + raise ValueError("start ({},{}) precedes previous end ({},{})" + .format(row, col, self.prev_row, self.prev_col)) + row_offset = row - self.prev_row + if row_offset: + self.tokens.append("\\\n" * row_offset) + self.prev_col = 0 col_offset = col - self.prev_col if col_offset: self.tokens.append(" " * col_offset) def untokenize(self, iterable): - for t in iterable: + it = iter(iterable) + for t in it: if len(t) == 2: - self.compat(t, iterable) + self.compat(t, it) break tok_type, token, start, end, line = t + if tok_type == ENDMARKER: + break self.add_whitespace(start) self.tokens.append(token) self.prev_row, self.prev_col = end @@ -204,16 +214,12 @@ return "".join(self.tokens) def compat(self, token, iterable): - startline = False indents = [] toks_append = self.tokens.append - toknum, tokval = token - if toknum in (NAME, NUMBER): - tokval += ' ' - if toknum in (NEWLINE, NL): - startline = True + startline = token[0] in (NEWLINE, NL) prevstring = False - for tok in iterable: + + for tok in chain([token], iterable): toknum, tokval = tok[:2] if toknum in (NAME, NUMBER): diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -122,8 +122,6 @@ return True expected_regexp = self.expected_regexp - if isinstance(expected_regexp, basestring): - expected_regexp = re.compile(expected_regexp) if not expected_regexp.search(str(exc_value)): raise self.failureException('"%s" does not match "%s"' % (expected_regexp.pattern, str(exc_value))) @@ -986,6 +984,8 @@ args: Extra args. kwargs: Extra kwargs. """ + if expected_regexp is not None: + expected_regexp = re.compile(expected_regexp) context = _AssertRaisesContext(expected_exception, self, expected_regexp) if callable_obj is None: return context diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py --- a/Lib/unittest/test/test_case.py +++ b/Lib/unittest/test/test_case.py @@ -979,6 +979,12 @@ self.assertRaisesRegexp, Exception, u'x', lambda: None) + def testAssertRaisesRegexpInvalidRegexp(self): + # Issue 20145. + class MyExc(Exception): + pass + self.assertRaises(TypeError, self.assertRaisesRegexp, MyExc, lambda: True) + def testAssertRaisesRegexpMismatch(self): def Stub(): raise Exception('Unexpected') diff --git a/Lib/zipfile.py b/Lib/zipfile.py --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -384,7 +384,7 @@ # Try to decode the extra field. extra = self.extra unpack = struct.unpack - while extra: + while len(extra) >= 4: tp, ln = unpack('= 24: diff --git a/Mac/BuildScript/README.txt b/Mac/BuildScript/README.txt --- a/Mac/BuildScript/README.txt +++ b/Mac/BuildScript/README.txt @@ -8,11 +8,15 @@ an Installer package from the installation plus other files in ``resources`` and ``scripts`` and placed that on a ``.dmg`` disk image. -For Python 2.7.x and 3.2.x, PSF practice is to build two installer variants +For Python 2.7.x and 3.x, PSF practice is to build two installer variants for each release. -1. 32-bit-only, i386 and PPC universal, capable on running on all machines - supported by Mac OS X 10.3.9 through (at least) 10.8:: +Beginning with Python 2.7.8, we plan to drop binary installer support for +Mac OS X 10.3.9 and 10.4.x systems. To ease the transition, for Python 2.7.7 +only there will be three installers provided: + +1. DEPRECATED - 32-bit-only, i386 and PPC universal, capable on running on all + machines supported by Mac OS X 10.3.9 through (at least) 10.9:: /usr/bin/python build-installer.py \ --sdk-path=/Developer/SDKs/MacOSX10.4u.sdk \ @@ -45,8 +49,42 @@ - need to change ``/System/Library/Frameworks/{Tcl,Tk}.framework/Version/Current`` to ``8.4`` * Note Xcode 4.* does not support building for PPC so cannot be used for this build +2. 32-bit-only, i386 and PPC universal, capable on running on all machines + supported by Mac OS X 10.5 through (at least) 10.9:: -2. 64-bit / 32-bit, x86_64 and i386 universal, for OS X 10.6 (and later):: + /usr/bin/python build-installer.py \ + --sdk-path=/Developer/SDKs/MacOSX10.5.sdk \ + --universal-archs=32-bit \ + --dep-target=10.5 + + - builds the following third-party libraries + + * NCurses 5.9 + * SQLite 3.7.13 + * Oracle Sleepycat DB 4.8 (Python 2.x only) + + - uses system-supplied versions of third-party libraries + + * readline module links with Apple BSD editline (libedit) + + - requires ActiveState ``Tcl/Tk 8.4`` (currently 8.4.20) to be installed for building + + - recommended build environment: + + * Mac OS X 10.5.8 Intel or PPC + * Xcode 3.1.4 + * ``MacOSX10.5`` SDK + * ``MACOSX_DEPLOYMENT_TARGET=10.5`` + * Apple ``gcc-4.2`` + * system Python 2.5+ for documentation build with Sphinx + + - alternate build environments: + + * Mac OS X 10.6.8 with Xcode 3.2.6 + - need to change ``/System/Library/Frameworks/{Tcl,Tk}.framework/Version/Current`` to ``8.4`` + * Note Xcode 4.* does not support building for PPC so cannot be used for this build + +3. 64-bit / 32-bit, x86_64 and i386 universal, for OS X 10.6 (and later):: /usr/bin/python build-installer.py \ --sdk-path=/Developer/SDKs/MacOSX10.6.sdk \ @@ -57,13 +95,13 @@ * NCurses 5.9 (http://bugs.python.org/issue15037) * SQLite 3.7.13 + * Oracle Sleepycat DB 4.8 (Python 2.x only) - uses system-supplied versions of third-party libraries * readline module links with Apple BSD editline (libedit) - * builds Oracle Sleepycat DB 4.8 (Python 2.x only) - - requires ActiveState Tcl/Tk 8.5.9 (or later) to be installed for building + - requires ActiveState Tcl/Tk 8.5.15 (or later) to be installed for building - recommended build environment: @@ -82,10 +120,10 @@ considered a migration aid by Apple and is not likely to be fixed, its use should be avoided. The other compiler, ``clang``, has been undergoing rapid development. While it appears to have become - production-ready in the most recent Xcode 4 releases (Xcode 4.5.x - as of this writing), there are still some open issues when - building Python and there has not yet been the level of exposure in - production environments that the Xcode 3 gcc-4.2 compiler has had. + production-ready in the most recent Xcode 5 releases, the versions + available on the deprecated Xcode 4.x for 10.6 were early releases + and did not receive the level of exposure in production environments + that the Xcode 3 gcc-4.2 compiler has had. General Prerequisites 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 @@ -8,7 +8,9 @@ Please ensure that this script keeps working with Python 2.5, to avoid bootstrap issues (/usr/bin/python is Python 2.5 on OSX 10.5). Sphinx, which is used to build the documentation, currently requires at least -Python 2.4. +Python 2.4. However, as of Python 3.4.1, Doc builds require an external +sphinx-build and the current versions of Sphinx now require at least +Python 2.6. In addition to what is supplied with OS X 10.5+ and Xcode 3+, the script requires an installed version of hg and a third-party version of @@ -21,8 +23,8 @@ 32-bit-only installer builds are still possible on OS X 10.4 with Xcode 2.5 and the installation of additional components, such as a newer Python -(2.5 is needed for Python parser updates), hg, and svn (for the documentation -build). +(2.5 is needed for Python parser updates), hg, and for the documentation +build either svn (pre-3.4.1) or sphinx-build (3.4.1 and later). Usage: see USAGE variable in the script. """ @@ -193,7 +195,8 @@ LT_10_5 = bool(DEPTARGET < '10.5') - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): +# Disable for now + if False: # if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 5)): result.extend([ dict( name="Tcl 8.5.15", @@ -237,9 +240,9 @@ if getVersionTuple() >= (3, 3): result.extend([ dict( - name="XZ 5.0.3", - url="http://tukaani.org/xz/xz-5.0.3.tar.gz", - checksum='fefe52f9ecd521de2a8ce38c21a27574', + name="XZ 5.0.5", + url="http://tukaani.org/xz/xz-5.0.5.tar.gz", + checksum='19d924e066b6fff0bc9d1981b4e53196', configure_pre=[ '--disable-dependency-tracking', ] @@ -282,9 +285,9 @@ ), ), dict( - name="SQLite 3.7.13", - url="http://www.sqlite.org/sqlite-autoconf-3071300.tar.gz", - checksum='c97df403e8a3d5b67bb408fcd6aabd8e', + name="SQLite 3.8.3.1", + url="http://www.sqlite.org/2014/sqlite-autoconf-3080301.tar.gz", + checksum='509ff98d8dc9729b618b7e96612079c6', extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS4 ' '-DSQLITE_ENABLE_FTS3_PARENTHESIS ' @@ -364,6 +367,8 @@ # Instructions for building packages inside the .mpkg. def pkg_recipes(): unselected_for_python3 = ('selected', 'unselected')[PYTHON_3] + # unselected if 3.0 through 3.3, selected otherwise (2.x or >= 3.4) + unselected_for_lt_python34 = ('selected', 'unselected')[(3, 0) <= getVersionTuple() < (3, 4)] result = [ dict( name="PythonFramework", @@ -432,10 +437,27 @@ topdir="/Library/Frameworks/Python.framework", source="/empty-dir", required=False, - selected=unselected_for_python3, + selected=unselected_for_lt_python34, ), ] + if getVersionTuple() >= (3, 4): + result.append( + dict( + name="PythonInstallPip", + long_name="Install or upgrade pip", + readme="""\ + This package installs (or upgrades from an earlier version) + pip, a tool for installing and managing Python packages. + """, + postflight="scripts/postflight.ensurepip", + topdir="/Library/Frameworks/Python.framework", + source="/empty-dir", + required=False, + selected='selected', + ) + ) + if DEPTARGET < '10.4' and not PYTHON_3: result.append( dict( @@ -453,6 +475,7 @@ selected=unselected_for_python3, ) ) + return result def fatal(msg): @@ -567,20 +590,6 @@ % frameworks['Tk'], ] - # For 10.6+ builds, we build two versions of _tkinter: - # - the traditional version (renamed to _tkinter_library.so) linked - # with /Library/Frameworks/{Tcl,Tk}.framework - # - the default version linked with our builtin copies of Tcl and Tk - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): - EXPECTED_SHARED_LIBS['_tkinter_library.so'] = \ - EXPECTED_SHARED_LIBS['_tkinter.so'] - EXPECTED_SHARED_LIBS['_tkinter.so'] = [ - "/Library/Frameworks/Python.framework/Versions/%s/lib/libtcl%s.dylib" - % (getVersion(), frameworks['Tcl']), - "/Library/Frameworks/Python.framework/Versions/%s/lib/libtk%s.dylib" - % (getVersion(), frameworks['Tk']), - ] - # Remove inherited environment variables which might influence build environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_', 'LD_', 'LIBRARY_', 'PATH', 'PYTHON'] @@ -601,7 +610,11 @@ base_path = base_path + ':' + OLD_DEVELOPER_TOOLS os.environ['PATH'] = base_path print("Setting default PATH: %s"%(os.environ['PATH'])) - + # Ensure ws have access to hg and to sphinx-build. + # You may have to create links in /usr/bin for them. + runCommand('hg --version') + if getVersionTuple() >= (3, 4): + runCommand('sphinx-build --version') def parseOptions(args=None): """ @@ -854,7 +867,7 @@ ' -arch '.join(archList), shellQuote(SDKPATH)[1:-1], shellQuote(basedir)[1:-1],), - "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%( + "LDFLAGS=-mmacosx-version-min=%s -isysroot %s -L%s/usr/local/lib -arch %s"%( DEPTARGET, shellQuote(SDKPATH)[1:-1], shellQuote(basedir)[1:-1], @@ -914,8 +927,15 @@ docdir = os.path.join(rootDir, 'pydocs') curDir = os.getcwd() os.chdir(buildDir) - runCommand('make update') - runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable)) + # The Doc build changed for 3.4 (technically, for 3.4.1) + if getVersionTuple() < (3, 4): + # This step does an svn checkout of sphinx and its dependencies + runCommand('make update') + runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable)) + else: + runCommand('make clean') + # Assume sphinx-build is on our PATH, checked in checkEnvironment + runCommand('make html') os.chdir(curDir) if not os.path.exists(docdir): os.mkdir(docdir) @@ -955,34 +975,22 @@ runCommand("%s -C --enable-framework --enable-universalsdk=%s " "--with-universal-archs=%s " "%s " + "%s " "LDFLAGS='-g -L%s/libraries/usr/local/lib' " "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%( shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH), UNIVERSALARCHS, (' ', '--with-computed-gotos ')[PYTHON_3], + (' ', '--without-ensurepip ')[getVersionTuple() >= (3, 4)], shellQuote(WORKDIR)[1:-1], shellQuote(WORKDIR)[1:-1])) + print("Running make touch") + runCommand("make touch") + print("Running make") runCommand("make") - # For deployment targets of 10.6 and higher, we build our own version - # of Tcl and Cocoa Aqua Tk libs because the Apple-supplied Tk 8.5 is - # out-of-date and has critical bugs. Save the _tkinter.so that was - # linked with /Library/Frameworks/{Tck,Tk}.framework and build - # another _tkinter.so linked with our builtin Tcl and Tk libs. - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): - runCommand("find build -name '_tkinter.so' " - " -execdir mv '{}' _tkinter_library.so \;") - print("Running make to build builtin _tkinter") - runCommand("make TCLTK_INCLUDES='-I%s/libraries/usr/local/include' " - "TCLTK_LIBS='-L%s/libraries/usr/local/lib -ltcl8.5 -ltk8.5'"%( - shellQuote(WORKDIR)[1:-1], - shellQuote(WORKDIR)[1:-1])) - # make a copy which will be moved to lib-tkinter later - runCommand("find build -name '_tkinter.so' " - " -execdir cp -p '{}' _tkinter_builtin.so \;") - print("Running make install") runCommand("make install DESTDIR=%s"%( shellQuote(rootDir))) @@ -1007,27 +1015,11 @@ 'Python.framework', 'Versions', version, 'lib', 'python%s'%(version,)) - # If we made multiple versions of _tkinter, move them to - # their own directories under python lib. This allows - # users to select which to import by manipulating sys.path - # directly or with PYTHONPATH. - - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): - TKINTERS = ['builtin', 'library'] - tkinter_moves = [('_tkinter_' + tkn + '.so', - os.path.join(path_to_lib, 'lib-tkinter', tkn)) - for tkn in TKINTERS] - # Create the destination directories under lib-tkinter. - # The permissions and uid/gid will be fixed up next. - for tkm in tkinter_moves: - os.makedirs(tkm[1]) - print("Fix file modes") frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework') gid = grp.getgrnam('admin').gr_gid shared_lib_error = False - moves_list = [] for dirpath, dirnames, filenames in os.walk(frmDir): for dn in dirnames: os.chmod(os.path.join(dirpath, dn), STAT_0o775) @@ -1053,25 +1045,9 @@ % (sl, p)) shared_lib_error = True - # If this is a _tkinter variant, move it to its own directory - # now that we have fixed its permissions and checked that it - # was linked properly. The directory was created earlier. - # The files are moved after the entire tree has been walked - # since the shared library checking depends on the files - # having unique names. - if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 4)): - for tkm in tkinter_moves: - if fn == tkm[0]: - moves_list.append( - (p, os.path.join(tkm[1], '_tkinter.so'))) - if shared_lib_error: fatal("Unexpected shared library errors.") - # Now do the moves. - for ml in moves_list: - shutil.move(ml[0], ml[1]) - if PYTHON_3: LDVERSION=None VERSION=None diff --git a/Mac/BuildScript/resources/ReadMe.txt b/Mac/BuildScript/resources/ReadMe.txt --- a/Mac/BuildScript/resources/ReadMe.txt +++ b/Mac/BuildScript/resources/ReadMe.txt @@ -28,6 +28,32 @@ for current information about supported and recommended versions of Tcl/Tk for this version of Python and of Mac OS X. + **** IMPORTANT **** + +Binary installer support for 10.4 and 10.3.9 to be discontinued +=============================================================== + +Python 2.7.7 is the last release for which binary installers will be +released on python.org that support OS X 10.3.9 (Panther) and 10.4.x +(Tiger) systems. These systems were last updated by Apple in 2005 +and 2007. As of 2.7.8, the 32-bit-only installer will support PPC +and Intel Macs running OS X 10.5 (Leopard) and later. 10.5 was the +last OS X release for PPC machines (G4 and G5). (The 64-/32-bit +installer configuration will remain unchanged.) This aligns Python +2.7.x installer configurations with those currently provided with +Python 3.x. Some of the reasons for making this change are: +there were significant additions and compatibility improvements to +the OS X POSIX system APIs in OS X 10.5 that Python users can now +take advantage of; it is increasingly difficult to build and test +on obsolete 10.3 and 10.4 systems and with the 10.3 ABI; and it is +assumed that most remaining legacy PPC systems have upgraded to 10.5. +To ease the transition, for Python 2.7.7 only we are providing three +binary installers: (1) the legacy deprecated 32-bit-only 10.3+ +PPC/Intel format, (2) the newer 32-bit-only 10.5+ PPC/Intel format, +and (3) the current 64-bit/32-bit 10.6+ Intel-only format. While +future releases will not provide the deprecated installer, it will +still be possible to build Python from source on 10.3.9 and 10.4 +systems if needed. Using this version of Python on OS X ==================================== diff --git a/Mac/BuildScript/resources/Welcome.rtf b/Mac/BuildScript/resources/Welcome.rtf --- a/Mac/BuildScript/resources/Welcome.rtf +++ b/Mac/BuildScript/resources/Welcome.rtf @@ -1,8 +1,8 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} -\paperw11904\paperh16836\margl1440\margr1440\vieww9640\viewh10620\viewkind0 -\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural +\paperw11905\paperh16837\margl1440\margr1440\vieww9640\viewh10620\viewkind0 +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640 \f0\fs24 \cf0 This package will install \b Python $FULL_VERSION @@ -16,7 +16,7 @@ \b IDLE \b0 and a set of pre-built extension modules that open up specific Macintosh technologies to Python programs.\ \ -See the ReadMe file and the Python documentation for more information.\ +See the ReadMe file and the Python documentation for important information, including the dropping of support for OS X 10.3.9 and 10.4 in future Python 2.7.x binary installers.\ \ \b IMPORTANT: diff --git a/Mac/Modules/carbonevt/_CarbonEvtmodule.c b/Mac/Modules/carbonevt/_CarbonEvtmodule.c --- a/Mac/Modules/carbonevt/_CarbonEvtmodule.c +++ b/Mac/Modules/carbonevt/_CarbonEvtmodule.c @@ -1051,8 +1051,7 @@ _err = RemoveEventHandler(_self->ob_itself); if (_err != noErr) return PyMac_Error(_err); _self->ob_itself = NULL; - Py_DECREF(_self->ob_callback); - _self->ob_callback = NULL; + Py_CLEAR(_self->ob_callback); Py_INCREF(Py_None); _res = Py_None; return _res; diff --git a/Mac/Modules/list/_Listmodule.c b/Mac/Modules/list/_Listmodule.c --- a/Mac/Modules/list/_Listmodule.c +++ b/Mac/Modules/list/_Listmodule.c @@ -76,8 +76,7 @@ static void ListObj_dealloc(ListObject *self) { - Py_XDECREF(self->ob_ldef_func); - self->ob_ldef_func = NULL; + Py_CLEAR(self->ob_ldef_func); SetListRefCon(self->ob_itself, (long)0); if (self->ob_must_be_disposed && self->ob_itself) LDispose(self->ob_itself); self->ob_type->tp_free((PyObject *)self); diff --git a/Mac/README b/Mac/README --- a/Mac/README +++ b/Mac/README @@ -1,12 +1,19 @@ -============ -MacOSX Notes -============ +========================= +Python on Mac OS X README +========================= + +:Authors: + Jack Jansen (2004-07), + Ronald Oussoren (2010-04), + Ned Deily (2014-05) + +:Version: 2.7.7 This document provides a quick overview of some Mac OS X specific features in the Python distribution. -Mac-specific arguments to configure -=================================== +OS X specific arguments to configure +==================================== * ``--enable-framework[=DIR]`` @@ -15,11 +22,11 @@ _`Building and using a framework-based Python on Mac OS X` for more information on frameworks. - If the optional directory argument is specified the framework it installed + If the optional directory argument is specified the framework is installed into that directory. This can be used to install a python framework into your home directory:: - $ configure --enable-framework=/Users/ronald/Library/Frameworks + $ ./configure --enable-framework=/Users/ronald/Library/Frameworks $ make && make install This will install the framework itself in ``/Users/ronald/Library/Frameworks``, @@ -36,9 +43,10 @@ Create a universal binary build of Python. This can be used with both regular and framework builds. - The optional argument specifies which OSX SDK should be used to perform the - build. This defaults to ``/Developer/SDKs/MacOSX.10.4u.sdk``, specify - ``/`` when building on a 10.5 system, especially when building 64-bit code. + The optional argument specifies which OS X SDK should be used to perform the + build. This defaults to ``/Developer/SDKs/MacOSX.10.4u.sdk``. When building + on OS X 10.5 or later, you can specify ``/`` to use the installed system + headers rather than an SDK. See the section _`Building and using a universal binary of Python on Mac OS X` for more information. @@ -56,9 +64,14 @@ 1. What is a universal binary ----------------------------- -A universal binary build of Python contains object code for both PPC and i386 -and can therefore run at native speed on both classic powerpc based macs and -the newer intel based macs. +A universal binary build of Python contains object code for more than one +CPU architecture. A universal OS X executable file or library combines the +architecture-specific code into one file and can therefore run at native +speed on all supported architectures. Universal files were introduced in +OS X 10.4 to add support for Intel-based Macs to the existing PowerPC (PPC) +machines. In OS X 10.5 support was extended to 64-bit Intel and 64-bit PPC +architectures. It is possible to build Python with various combinations +of architectures depending on the build tools and OS X version in use. 2. How do I build a universal binary ------------------------------------ @@ -71,44 +84,68 @@ $ make install This flag can be used with a framework build of python, but also with a classic -unix build. Either way you will have to build python on Mac OS X 10.4 (or later) -with Xcode 2.1 (or later). You also have to install the 10.4u SDK when -installing Xcode. +unix build. Universal builds were first supported with OS X 10.4 with Xcode 2.1 +and the 10.4u SDK. Starting with Xcode 3 and OS X 10.5, more configurations are +available. The option ``--enable-universalsdk`` has an optional argument to specify an -SDK, which defaults to the 10.4u SDK. When you build on OSX 10.5 or later +SDK, which defaults to the 10.4u SDK. When you build on OS X 10.5 or later you can use the system headers instead of an SDK:: $ ./configure --enable-universalsdk=/ -2.1 Flavours of universal binaries -.................................. +In general, universal builds depend on specific features provided by the +Apple-supplied compilers and other build tools included in Apple's Xcode +development tools. You should install Xcode and the command line tools +component appropriate for the OS X release you are running on. See the +Python Developer's Guide (http://docs.python.org/devguide/setup.html) +for more information. -It is possible to build a number of flavours of the universal binary build, -the default is a 32-bit only binary (i386 and ppc). The flavour can be +2.1 Flavors of universal binaries +................................. + +It is possible to build a number of flavors of the universal binary build, +the default is a 32-bit only binary (i386 and ppc). Note that starting with +Xcode 4, the build tools no longer support ppc. The flavor can be specified using the option ``--with-universal-archs=VALUE``. The following values are available: + * ``intel``: ``i386``, ``x86_64`` + * ``32-bit``: ``ppc``, ``i386`` + * ``3-way``: ``i386``, ``x86_64``, ``ppc`` + * ``64-bit``: ``ppc64``, ``x86_64`` * ``all``: ``ppc``, ``ppc64``, ``i386``, ``x86_64`` - * ``3-way``: ``ppc``, ``i386`` and ``x86_64`` +To build a universal binary that includes a 64-bit architecture, you must build +on a system running OS X 10.5 or later. The ``all`` and ``64-bit`` flavors can +only be built with an 10.5 SDK because ``ppc64`` support was only included with +OS X 10.5. Although legacy ``ppc`` support was included with Xcode 3 on OS X +10.6, it was removed in Xcode 4, versions of which were released on OS X 10.6 +and which is the standard for OS X 10.7. To summarize, the +following combinations of SDKs and universal-archs flavors are available: - * ``intel``: ``i386``, ``x86_64`` + * 10.4u SDK with Xcode 2 supports ``32-bit`` only -To build a universal binary that includes a 64-bit architecture, you must build -on a system running OSX 10.5 or later. The ``all`` flavour can only be built on -OSX 10.5. + * 10.5 SDK with Xcode 3.1.x supports all flavors -The makefile for a framework build will install ``python32`` and ``pythonw32`` -binaries when the universal architecures includes at least one 32-bit architecture -(that is, for all flavours but ``64-bit``). + * 10.6 SDK with Xcode 3.2.x supports ``intel``, ``3-way``, and ``32-bit`` -Running a specific archicture -............................. + * 10.6 SDK with Xcode 4 supports ``intel`` only + + * 10.7 and 10.8 SDKs with Xcode 4 support ``intel`` only + + * 10.8 and 10.9 SDKs with Xcode 5 support ``intel`` only + +The makefile for a framework build will also install ``python2.7-32`` +binaries when the universal architecture includes at least one 32-bit +architecture (that is, for all flavors but ``64-bit``). + +Running a specific architecture +............................... You can run code using a specific architecture using the ``arch`` command:: @@ -123,6 +160,13 @@ wrapper tools that execute the real interpreter without ensuring that the real interpreter runs with the same architecture. +Using ``arch`` is not a perfect solution as the selected architecture will +not automatically carry through to subprocesses launched by programs and tests +under that Python. If you want to ensure that Python interpreters launched in +subprocesses also run in 32-bit-mode if the main interpreter does, use +a ``python2.7-32`` binary and use the value of ``sys.executable`` as the +``subprocess`` ``Popen`` executable value. + Building and using a framework-based Python on Mac OS X. ======================================================== @@ -132,16 +176,17 @@ The main reason is because you want to create GUI programs in Python. With the exception of X11/XDarwin-based GUI toolkits all GUI programs need to be run -from a fullblown MacOSX application (a ".app" bundle). +from a Mac OS X application bundle (".app"). While it is technically possible to create a .app without using frameworks you will have to do the work yourself if you really want this. A second reason for using frameworks is that they put Python-related items in only two places: "/Library/Framework/Python.framework" and -"/Applications/MacPython 2.6". This simplifies matters for users installing +"/Applications/Python " where ```` can be e.g. "3.4", +"2.7", etc. This simplifies matters for users installing Python from a binary distribution if they want to get rid of it again. Moreover, -due to the way frameworks work a user without admin privileges can install a +due to the way frameworks work, a user without admin privileges can install a binary distribution in his or her home directory without recompilation. 2. How does a framework Python differ from a normal static Python? @@ -156,43 +201,55 @@ 3. Do I need extra packages? ---------------------------- -Yes, probably. If you want Tkinter support you need to get the OSX AquaTk -distribution, this is installed by default on Mac OS X 10.4 or later. If -you want wxPython you need to get that. If you want Cocoa you need to get -PyObjC. +Yes, probably. If you want Tkinter support you need to get the OS X AquaTk +distribution, this is installed by default on Mac OS X 10.4 or later. Be +aware, though, that the Cocoa-based AquaTk's supplied starting with OS X +10.6 have proven to be unstable. If possible, you should consider +installing a newer version before building on OS X 10.6 or later, such as +the ActiveTcl 8.5. See http://www.python.org/download/mac/tcltk/. If you +are building with an SDK, ensure that the newer Tcl and Tk frameworks are +seen in the SDK's ``Library/Frameworks`` directory; you may need to +manually create symlinks to their installed location, ``/Library/Frameworks``. +If you want wxPython you need to get that. +If you want Cocoa you need to get PyObjC. 4. How do I build a framework Python? ------------------------------------- This directory contains a Makefile that will create a couple of python-related -applications (fullblown OSX .app applications, that is) in -"/Applications/MacPython 2.6", and a hidden helper application Python.app -inside the Python.framework, and unix tools "python" and "pythonw" into -/usr/local/bin. In addition it has a target "installmacsubtree" that installs +applications (full-blown OS X .app applications, that is) in +"/Applications/Python ", and a hidden helper application Python.app +inside the Python.framework, and unix tools "python" and "pythonw" into +/usr/local/bin. In addition it has a target "installmacsubtree" that installs the relevant portions of the Mac subtree into the Python.framework. It is normally invoked indirectly through the main Makefile, as the last step -in the sequence:: +in the sequence - $ ./configure --enable-framework - $ make - $ make install + 1. ./configure --enable-framework -This sequence will put the framework in /Library/Framework/Python.framework, -the applications in "/Applications/MacPython 2.6" and the unix tools in -/usr/local/bin. + 2. make + + 3. make install -It is possible to select a different name for the framework using the configure -option ``--with-framework-name=NAME``. This makes it possible to have several -parallel installs of a Python framework. +This sequence will put the framework in ``/Library/Framework/Python.framework``, +the applications in ``/Applications/Python `` and the unix tools in +``/usr/local/bin``. -Installing in another place, for instance $HOME/Library/Frameworks if you have -no admin privileges on your machine, has only been tested very lightly. This -can be done by configuring with --enable-framework=$HOME/Library/Frameworks. -The other two directories, "/Applications/MacPython-2.6" and /usr/local/bin, -will then also be deposited in $HOME. This is sub-optimal for the unix tools, -which you would want in $HOME/bin, but there is no easy way to fix this right -now. +Installing in another place, for instance ``$HOME/Library/Frameworks`` if you +have no admin privileges on your machine, is possible. This can be accomplished +by configuring with ``--enable-framework=$HOME/Library/Frameworks``. +The other two directories will then also be installed in your home directory, +at ``$HOME/Applications/Python-`` and ``$HOME/bin``. + +If you want to install some part, but not all, read the main Makefile. The +frameworkinstall is composed of a couple of sub-targets that install the +framework itself, the Mac subtree, the applications and the unix tools. + +There is an extra target frameworkinstallextras that is not part of the +normal frameworkinstall which installs the Tools directory into +"/Applications/Python ", this is useful for binary +distributions. What do all these programs do? =============================== @@ -200,35 +257,54 @@ "IDLE.app" is an integrated development environment for Python: editor, debugger, etc. -"PythonLauncher.app" is a helper application that will handle things when you +"Python Launcher.app" is a helper application that will handle things when you double-click a .py, .pyc or .pyw file. For the first two it creates a Terminal window and runs the scripts with the normal command-line Python. For the latter it runs the script in the Python.app interpreter so the script can do -GUI-things. Keep the "alt" key depressed while dragging or double-clicking a -script to set runtime options. These options can be set once and for all -through PythonLauncher's preferences dialog. +GUI-things. Keep the ``Option`` key depressed while dragging or double-clicking +a script to set runtime options. These options can be set persistently +through Python Launcher's preferences dialog. -"BuildApplet.app" creates an applet from a Python script. Drop the script on it -and out comes a full-featured MacOS application. BuildApplet.app is now +"Build Applet.app" creates an applet from a Python script. Drop the script on it +and out comes a full-featured Mac OS X application. "Build Applet.app" is now deprecated and has been removed in Python 3. As of OS X 10.8, Xcode 4 no longer supplies the headers for the deprecated QuickDraw APIs used by the EasyDialogs module making BuildApplet unusable as an app. It will not be built by the Mac/Makefile in this case. -The commandline scripts /usr/local/bin/python and pythonw can be used to run -non-GUI and GUI python scripts from the command line, respectively. +The program ``pythonx.x`` runs python scripts from the command line. Various +compatibility aliases are also installed, including ``pythonwx.x`` which +in early releases of Python on OS X was required to run GUI programs. In +current releases, the ``pythonx.x`` and ``pythonwx.x`` commands are identical +and the use of ``pythonwx.x`` should be avoided as it has been removed in +current versions of Python 3. How do I create a binary distribution? ====================================== -Go to the directory "Mac/OSX/BuildScript". There you'll find a script -"build-installer.py" that does all the work. This will download and build +Download and unpack the source release from http://www.python.org/download/. +Go to the directory ``Mac/BuildScript``. There you will find a script +``build-installer.py`` that does all the work. This will download and build a number of 3rd-party libaries, configures and builds a framework Python, installs it, creates the installer package files and then packs this in a -DMG image. +DMG image. The script also builds an HTML copy of the current Python +documentation set for this release for inclusion in the framework. The +installer package will create links to the documentation for use by IDLE, +pydoc, shell users, and Finder user. -The script will build a universal binary, you'll therefore have to run this +The script will build a universal binary so you'll therefore have to run this script on Mac OS X 10.4 or later and with Xcode 2.1 or later installed. +However, the Python build process itself has several build dependencies not +available out of the box with OS X 10.4 so you may have to install +additional software beyond what is provided with Xcode 2. OS X 10.5 +provides a recent enough system Python (in ``/usr/bin``) to build +the Python documentation set. It should be possible to use SDKs and/or older +versions of Xcode to build installers that are compatible with older systems +on a newer system but this may not be completely foolproof so the resulting +executables, shared libraries, and ``.so`` bundles should be carefully +examined and tested on all supported systems for proper dynamic linking +dependencies. It is safest to build the distribution on a system running the +minimum OS X version supported. All of this is normally done completely isolated in /tmp/_py, so it does not use your normal build directory nor does it install into /. @@ -253,7 +329,7 @@ configure: WARNING: ## -------------------------------------- ## This almost always means you are trying to build a universal binary for -Python and have libaries in ``/usr/local`` that don't contain the required +Python and have libraries in ``/usr/local`` that don't contain the required architectures. Temporarily move ``/usr/local`` aside to finish the build. @@ -262,7 +338,7 @@ Uninstalling a framework can be done by manually removing all bits that got installed. That's true for both installations from source and installations using the binary installer. -Sadly enough OSX does not have a central uninstaller. +OS X does not provide a central uninstaller. The main bit of a framework install is the framework itself, installed in ``/Library/Frameworks/Python.framework``. This can contain multiple versions @@ -276,14 +352,12 @@ And lastly a framework installation installs files in ``/usr/local/bin``, all of them symbolic links to files in ``/Library/Frameworks/Python.framework/Versions/X.Y/bin``. -Odds and ends -============= -Something to take note of is that the ".rsrc" files in the distribution are -not actually resource files, they're AppleSingle encoded resource files. The -macresource module and the Mac/OSX/Makefile cater for this, and create -".rsrc.df.rsrc" files on the fly that are normal datafork-based resource -files. +Resources +========= - Jack Jansen, Jack.Jansen at cwi.nl, 15-Jul-2004. - Ronald Oussoren, RonaldOussoren at mac.com, 30-April-2010 + * http://www.python.org/download/mac/ + + * http://www.python.org/community/sigs/current/pythonmac-sig/ + + * http://docs.python.org/devguide/ diff --git a/Makefile.pre.in b/Makefile.pre.in --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1267,6 +1267,7 @@ # Touch generated files touch: + cd $(srcdir); \ touch Include/Python-ast.h Python/Python-ast.c # Sanitation targets -- clean leaves libraries, executables and tags diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1,4 +1,3 @@ - Acknowledgements ---------------- @@ -14,39 +13,55 @@ Aahz Michael Abbott +Rajiv Abraham David Abrahams +Marc Abramowitz +Ron Adam +Anton Afanasyev +Ali Afshar +Nitika Agarwal Jim Ahlstrom Farhan Ahmad +Matthew Ahrens Nir Aides Yaniv Aknin Jyrki Alakuijala Steve Alexander Fred Allen +Ray Allen Billy G. Allie Kevin Altis Joe Amenta A. Amoroso Mark Anacker +Shashwat Anand Anders Andersen John Anderson Pehr Anderson Erik Anders??n Oliver Andrich Ross Andrus +Juancarlo A??ez +Chris Angelico +J??r??my Anger Ankur Ankan +Jon Anglin Heidi Annexstad +Ramchandra Apte ??ric Araujo +Alicia Arlen Jeffrey Armstrong Jason Asbahr David Ascher Chris AtLee +Aymeric Augustin John Aycock -Jan-Hein B??hrman Donovan Baarda Arne Babenhauserheide Attila Babo +Matt Bachmann +Marcin Bachry Alfonso Baciero -Marcin Bachry Dwayne Bailey Stig Bakken Greg Ball @@ -54,53 +69,67 @@ Jeff Balogh Manuel Balsera Matt Bandy +Dmi Baranov Michael J. Barber Daniel Barclay +Nicolas Bareil Chris Barker Anton Barkovsky Nick Barnes Quentin Barnes +David Barnett Matthew Barnett Richard Barran Cesar Eduardo Barros Des Barry Ulf Bartelt Don Bashford +Pior Bastida Nick Bastin Ned Batchelder Jeff Bauer -Mike Bayer Michael R Bax Anthony Baxter +Mike Bayer Samuel L. Bayer Donald Beaudry David Beazley +Neal Becker Robin Becker -Neal Becker Torsten Becker Bill Bedford +Ian Beer +Stefan Behnel Reimer Behrends Ben Bell Thomas Bellman Alexander ?????????????? Belopolsky +Eli Bendersky David Benjamin Oscar Benjamin Andrew Bennetts Andy Bensky Bennett Benson +Ezra Berch Michel Van den Bergh +Julian Berman +Brice Berna +Olivier Bernard Eric Beser Steven Bethard Stephen Bevan Ron Bickers Natalia B. Bidart +Adrian von Bidder David Binger Dominic Binks Philippe Biondi Michael Birtwell Stuart Bishop Roy Bixler +Daniel Black Jonathan Black +Renaud Blanch Mike Bland Martin Bless Pablo Bleyer @@ -113,9 +142,13 @@ David Bolen Wouter Bolsterlee Gawain Bolton +Forest Bond Gregory Bond +Matias Bordese +Jonas Borgstr??m Jurjen Bos Peter Bosch +Dan Boswell Eric Bouck Thierry Bousch Sebastian Boving @@ -126,22 +159,33 @@ Georg Brandl Christopher Brannon Terrence Brannon +Germ??n M. Bravo +Sven Brauch Erik Bray Brian Brazil +Demian Brecht Dave Brennan Tom Bridgman +Anthony Briggs Keith Briggs +Tobias Brink Richard Brodie Michael Broghton +Ammar Brohi Daniel Brotsky Jean Brouwers Gary S. Brown +Titus Brown Oleg Broytmann Dave Brueck Francisco Mart??n Brugu?? Ian Bruntlett +Floris Bruynooghe +Matt Bryant Stan Bubrouski Erik de Bueger +Jan-Hein B??hrman +Lars Buitinck Dick Bulterman Bill Bumgarner Jimmy Burgett @@ -151,20 +195,24 @@ Alastair Burt Tarn Weisner Burton Lee Busby +Katherine Busch Ralph Butler +Nicolas Cadou Jp Calderone Arnaud Calmettes Daniel Calvelo Tony Campbell Brett Cannon Mike Carlton +Pierre Carrier Terry Carroll -Damien Cassou +Edward Catmur Lorenzo M. Catucci Donn Cave Charles Cazabon Jes??s Cea Avi??n Per Cederqvist +Matej Cepl Carl Cerecke Octavian Cerna Dave Chambers @@ -172,13 +220,16 @@ John Chandler Hye-Shik Chang Jeffrey Chang +Godefroid Chapelle +Brad Chapman +Greg Chapman Mitch Chapman -Greg Chapman -Brad Chapman Yogesh Chaudhari David Chaum Nicolas Chauvat +Jerry Chen Michael Chermside +Ingrid Cheung Albert Chin-A-Young Adal Chiriliuc Matt Chisholm @@ -189,21 +240,30 @@ David Cinege Craig Citro Gilles Civario +Chris Clark +Laurie Clark-Michalek Mike Clarkson Andrew Clegg Brad Clements +Robbie Clemons Steve Clift +Herv?? Coatanhay Nick Coghlan Josh Cogliati Dave Cole +Terrence Cole Benjamin Collar Jeffery Collins Robert Collins Paul Colomiets +Christophe Combelles Geremy Condra +Denver Coneybeare +Phil Connell Juan Jos?? Conti Matt Conway David M. Cooke +Jason R. Coombs Garrett Cooper Greg Copeland Aldo Cortesi @@ -225,8 +285,10 @@ Joaquin Cuenca Abela John Cugini Tom Culliton +Antonio Cuni Brian Curtin Lisandro Dalcin +Darren Dale Andrew Dalke Lars Damerow Evan Dandrea @@ -235,35 +297,46 @@ Ben Darnell Kushal Das Jonathan Dasteel +Pierre-Yves David A. Jesse Jiryu Davis +Merlijn van Deen John DeGood Ned Deily Vincent Delft Arnaud Delobelle +Konrad Delong Erik Demaine +Martin Dengler John Dennis L. Peter Deutsch Roger Dev Philippe Devalkeneer Raghuram Devarakonda +Caleb Deveraux Catherine Devlin Scott Dial Toby Dickenson Mark Dickinson Jack Diederich Daniel Diniz +Humberto Diogenes Yves Dionne Daniel Dittmar +Josip Djolonga Jaromir Dolecek Ismail Donmez Robert Donohue Marcos Donolo Dima Dorfman +Yves Dorfsman Cesar Douady Dean Draayer Fred L. Drake, Jr. +Derk Drukker John DuBois Paul Dubois +Jacques Ducasse +Andrei Dorian Duma Graham Dumpleton Quinn Dunkan Robin Dunn @@ -277,36 +350,52 @@ Maxim Dzumanenko Walter D??rwald Hans Eckardt +Rodolpho Eckhardt +Ulrich Eckhardt David Edelsohn +John Edmonds Grant Edwards John Ehresman +Tal Einat Eric Eisner Andrew Eland +Julien ??lie Lance Ellinghaus +Daniel Ellis +Phil Elson David Ely Jeff Epler Tom Epperly +G??kcen Eraslan Stoffel Erasmus J??rgen A. Erhard Michael Ernst Ben Escoto Andy Eskilsson +Andr?? Espaze Stefan Esser +Nicolas Estibals +Carey Evans Stephen D Evans -Carey Evans Tim Everett Paul Everitt David Everly Daniel Evers +Winston Ewert Greg Ewing Martijn Faassen Clovis Fabricio Andreas Faerber Bill Fancher +Michael Farrell Troy J. Farrell Mark Favas +Boris Feld +Thomas Fenzl Niels Ferguson Sebastian Fernandez +Florian Festi +John Feuerstein Carl Feynman Vincent Fiack Tomer Filiba @@ -323,6 +412,7 @@ Amaury Forgeot d'Arc Doug Fort John Fouhy +Andrew Francis Stefan Franke Martin Franklin Kent Frazier @@ -330,10 +420,12 @@ Robin Friedrich Bradley Froehle Ivan Frohne +Matthias Fuchs Jim Fulton Tadayoshi Funaba Gyro Funch Peter Funk +Ethan Furman Geoff Furnish Ulisses Furquim Hagen F??rstenau @@ -345,6 +437,7 @@ Yitzchak Gale Matthew Gallagher Quentin Gallet-Gilles +Riccardo Attilio Galli Raymund Galvin Nitin Ganatra Fred Gansevles @@ -366,9 +459,15 @@ Johannes Gijsbers Michael Gilfix Julian Gindi +Yannick Gingras +Matt Giuca Wim Glenn +Michael Goderbauer +Jeroen Van Goey Christoph Gohlke Tim Golden +Guilherme Gon??alves +Tiago Gon??alves Chris Gonnerman Shelley Gooch David Goodger @@ -376,16 +475,18 @@ Nathaniel Gray Eddy De Greef Grant Griffin +Andrea Griffini Duncan Grisby Fabian Groffen -John S. Gruber +Eric Groo Dag Gruneau Filip Gruszczy??ski Thomas Guettler -Ajitesh Gupta +Anuj Gupta Michael Guravage Lars Gust??bel Thomas G??ttler +Jonas H. Barry Haddow Philipp Hagemeister Paul ten Hagen @@ -393,9 +494,11 @@ Peter Haight V??clav Haisman Zbigniew Halas +Walker Hale IV Bob Halley Jesse Hallio Jun Hamano +Alexandre Hamelin Anders Hammarquist Mark Hammond Harald Hanche-Olsen @@ -406,6 +509,9 @@ Lynda Hardman Derek Harland Jason Harper +David Harrigan +Brian Harring +Jonathan Hartley Travis B. Hartwell Larry Hastings Tim Hatch @@ -414,12 +520,14 @@ Rycharde Hawkes Ben Hayden Jochen Hayek +Tim Heaney Henrik Heimbuerger Christian Heimes Thomas Heller Malte Helmert Lance Finn Helsten Jonathan Hendry +Michael Henry James Henstridge Kasun Herath Chris Herborth @@ -432,6 +540,7 @@ Magnus L. Hetland Raymond Hettinger Kevan Heydon +Kelsey Hightower Jason Hildebrand Richie Hindle Konrad Hinsen @@ -444,12 +553,14 @@ Albert Hofkamp Tomas Hoger Jonathan Hogg +Kamilla Holanda Steve Holden Akintayo Holder Thomas Holenstein Gerrit Holl Shane Holloway Rune Holm +Thomas Holmes Philip Homburg Naofumi Honda Jeffrey Honig @@ -458,87 +569,117 @@ Brian Hooper Randall Hopper Nadav Horesh +Alon Horev Jan Hosang +Alan Hourihane Ken Howard Brad Howes Mike Hoy Chih-Hao Huang +Christian Hudon Lawrence Hudson Michael Hudson Jim Hugunin Greg Humphreys Eric Huss +Nehal Hussain +Taihyun Hwang Jeremy Hylton Ludwig H??hne Gerhard H??ring Fredrik H????rd Catalin Iacob Mihai Ibanescu +Ali Ikinci +Aaron Iles Lars Immisch Bobby Impollonia Meador Inge +Peter Ingebretson Tony Ingraldi John Interrante +Vladimir Iofik Bob Ippolito Roger Irwin Atsuo Ishimoto +Adam Jackson +Ben Jackson Paul Jackson -Ben Jackson Manuel Jacob David Jacobs Kevin Jacobs Kjetil Jacobsen +Bertrand Janin Geert Jansen Jack Jansen Bill Janssen Thomas Jarosch +Juhana Jauhiainen Rajagopalasarma Jayakrishnan +Zbigniew J??drzejewski-Szmek +Julien Jehannet Drew Jenkins Flemming Kj??r Jensen Philip H. Jensen Philip Jenvey +MunSic Jeong Chris Jerdonek -Jiba +Dmitry Jeremov +Jim Jewett Pedro Diaz Jimenez Orjan Johansen Fredrik Johansson Gregory K. Johnson Kent Johnson +Michael Johnson Simon Johnston +Matt Joiner +Thomas Jollans Nicolas Joly +Brian K. Jones Evan Jones Jeremy Jones Richard Jones Irmen de Jong Lucas de Jonge +Kristj??n Valur J??nsson +Jens B. Jorgensen John Jorgensen -Jens B. Jorgensen Sijin Joseph Andreas Jung Tattoo Mabonzo K. Sarah K. +Sunny K Bohuslav Kabrda +Alexey Kachayev Bob Kahn Kurt B. Kaiser Tamito Kajiyama +Jan Kaliszewski Peter van Kampen +Rafe Kaplan Jacob Kaplan-Moss -Piotr Kasprzyk +Janne Karila +Per ??yvind Karlsen Anton Kasyanov Lou Kates Hiroaki Kawai +Brian Kearns Sebastien Keim Ryan Kelly +Dan Kenigsberg +Randall Kern Robert Kern -Randall Kern Jim Kerr Magnus Kessler Lawrence Kesteloot -Rafe Kettler Vivek Khera +Dhiru Kholia Mads Kiilerich +Jason Killen Jan Kim Taek Joo Kim +Sam Kimbrel W. Trevor King Paul Kippes Steve Kirsch @@ -546,6 +687,7 @@ Kamil Kisiel Akira Kitada Ron Klatchko +Reid Kleckner Bastian Kleineidam Bob Kline Matthias Klose @@ -555,12 +697,17 @@ Lenny Kneler Pat Knight Jeff Knupp +Kubilay Kocak Greg Kochanski Damon Kohler Marko Kohtala Vajrasky Kok Guido Kollerie +Jacek Konieczny +???????? ?????????????????? +Arkady Koplyarov Peter A. Koren +Vlad Korolev Joseph Koshy Daniel Kozan Jerzy Kozera @@ -572,34 +719,46 @@ Michael Kremer Fabian Kreutz C??dric Krier +Pedro Kroger Hannu Krosing Andrej Krpic Ivan Krsti?? Steven Kryskalla Andrew Kuchling -Ralf W. Grosse-Kunstleve Dave Kuhlman +Jon Kuhn +Toshio Kuratomi Vladimir Kushnir Erno Kuusela -Kirill Kuzminykh (???????????? ??????????????????) Ross Lagerwall Cameron Laird +David Lam Thomas Lamb +Valerie Lambert +Jean-Baptiste "Jiba" Lamy +Ronan Lamy Torsten Landschoff ??ukasz Langa Tino Lange +Glenn Langford Andrew Langmead Detlef Lannert Soren Larsen +Amos Latteier Piers Lauder Ben Laurie Simon Law +Julia Lawall Chris Lawrence Brian Leair +Mathieu Leduc-Hamel +Amandine Lee +Christopher Lee +Inyeol Lee +James Lee John J. Lee -Inyeol Lee Thomas Lee -Christopher Lee +Tennessee Leeuwenburg Luc Lefebvre Pierre Paul Lefebvre Glyph Lefkowitz @@ -614,14 +773,20 @@ Mateusz Lenik John Lenton Kostyantyn Leschenko +Benno Leslie Christopher Tur Lesniewski-Laas +Alain Leufroy Mark Levinson +Mark Levitt William Lewis +Akira Li Xuanji Li Robert van Liere Ross Light Shawn Ligocki Martin Ligr +Gediminas Liktaras +Grant Limberg Christopher Lindblad Ulf A. Lindgren Bj??rn Lindqvist @@ -632,47 +797,64 @@ Mirko Liss Nick Lockwood Stephanie Lockwood +Hugo Lopes Tavares Anne Lord Tom Loredo Justin Love +Ned Jackson Lovely Jason Lowe Tony Lownds Ray Loyzaga +Kang-Hao (Kenny) Lu Lukas Lueg Loren Luke Fredrik Lundh Zhongyue Luo Mark Lutz +Taras Lyapun Jim Lynch Mikael Lyngvig Martin von L??wis -Till Maas +Guillermo L??pez-Anglada Jeff MacDonald John Machin Andrew I MacIntyre Tim MacKenzie Nick Maclaren +Don MacMillen +Tomasz Ma??kowiak +Wolfgang Maier Steve Majewski +Marek Majkowski Grzegorz Makarewicz David Malcolm +Greg Malcolm +William Mallard Ken Manheimer Vladimir Marangozov +Colin Marc Vincent Marchetti David Marek Doug Marien Sven Marnach Alex Martelli Anthony Martin +Owen Martin Westley Mart??nez S??bastien Martini +Sidney San Mart??n Roger Masse Nick Mathewson +Simon Mathieu Laura Matson Graham Matthews Martin Matusiak Dieter Maurer Daniel May +Madison May +Lucas Maystre Arnaud Mazin +Matt McClure Rebecca McCreary Kirk McDonald Chris McDonough @@ -680,10 +862,11 @@ Alan McIntyre Jessica McKellar Michael McLay +Brendan McLoughlin Mark Mc Mahon Gordon McMillan +Andrew McNamara Caolan McNamara -Andrew McNamara Jeff McNeil Craig McPheeters Lambert Meertens @@ -696,39 +879,55 @@ Carl Meyer Mike Meyer Piotr Meyer +Alexis M??taireau Steven Miale +Trent Mick Jason Michalski -Trent Mick +Franck Michea Tom Middleton +Thomas Miedema Stan Mihai +Stefan Mihaila Aristotelis Mikropoulos Paolo Milani +Chad Miller Damien Miller -Chad Miller Jason V. Miller Jay T. Miller +Katie Miller Roman Milner +Julien Miotte Andrii V. Mishkovskyi +Dom Mitchell Dustin J. Mitchell -Dom Mitchell +Zubin Mithra Florian Mladitsch Doug Moen -Jaakko Moisio The Dragon De Monsyne +Bastien Montagne Skip Montanaro +Peter Moody Paul Moore Ross Moore +Ben Morgan Derek Morr James A Morrison +Martin Morrison +Derek McTavish Mounce Alessandro Moura Pablo Mouzo +Mher Movsisyan Ruslan Mstoi +Valentina Mukhamedzhanova +Michael Mulich +Sape Mullender Sjoerd Mullender -Sape Mullender Michael Muller Neil Muller +Louis Munro R. David Murray Matti M??ki +J??rg M??ller Dale Nagata John Nagle Takahiro Nakayama @@ -736,17 +935,18 @@ Charles-Fran??ois Natali Vilmos Nebehaj Fredrik Nehr +Tony Nelson Trent Nelson -Tony Nelson Chad Netzer Max Neunh??ffer George Neville-Neil Hieu Nguyen Johannes Nicolai Samuel Nicolary +Jonathan Niehof Gustavo Niemeyer Oscar Nierstrasz -Hrvoje Niksic +Hrvoje Nik??i?? Gregory Nofi Jesse Noller Bill Noon @@ -754,13 +954,19 @@ Tim Northover Joe Norton Neal Norwitz +Mikhail Novikov Michal Nowikowski Steffen Daode Nurpmeso Nigel O'Brian +John O'Connor Kevin O'Connor Tim O'Malley Zooko O'Whielacronx +Aaron Oakley +James Oakley Elena Oat +Jon Oberheide +Milan Oberkirch Pascal Oberndoerfer Jeffrey Ollie Adam Olsen @@ -773,6 +979,7 @@ Michele Orr?? Oleg Oshmyan Denis S. Otkidach +Peter Otten Michael Otteneder R. M. Oudkerk Russel Owen @@ -784,6 +991,7 @@ Todd R. Palmer Juan David Ib????ez Palomar Jan Palus +Mathias Panzenb??ck M. Papillon Peter Parente Alexandre Parenteau @@ -791,57 +999,82 @@ William Park Heikki Partanen Harri Pasanen +Ga??l Pasgrimaud +Ashish Nitin Patil Randy Pausch Samuele Pedroni +Justin Peel Marcel van der Peijl Berker Peksag Andreas Pelme Steven Pemberton Bo Peng Santiago Peres??n +George Peristerakis +Mathieu Perreault Mark Perrego Trevor Perrin Gabriel de Perthuis Tim Peters Benjamin Peterson +Joe Peterson Chris Petrilli +Roumen Petrov Bjorn Pettersen Justin D. Pettit +Esa Peuha +Ronny Pfannschmidt Geoff Philbrick Gavrie Philipson Adrian Phillips Christopher J. Phoenix Neale Pickett -Matti Picus Jim St. Pierre Dan Pierson Martijn Pieters +Anand B. Pillai Fran??ois Pinard Tom Pinckney Zach Pincus +Zero Piraeus Michael Piotrowski Antoine Pitrou Jean-Fran??ois Pi??ronne Oleg Plakhotnyuk +Remi Pointel +Ariel Poliak Guilherme Polo +Illia Polosukhin Michael Pomraning +Martin Pool Iustin Pop +Claudiu Popa John Popplewell +Guillaume Pratte Amrit Prem Paul Prescod Donovan Preston +Paul Price +Iuliia Proskurnia +Jyrki Pulliainen Steve Purcell +Eduardo P??rez Fernando P??rez -Eduardo P??rez +Pierre Quentel Brian Quinlan +Kevin Jing Qiu Anders Qvist Thomas Rachel +Ram Rachum +J??r??me Radix Burton Radons Jeff Ramnani Brodie Rao +Senko Rasic Antti Rasinen Nikolaus Rath Sridhar Ratnakumar +Ysj Ray Eric S. Raymond Edward K. Ream Chris Rebert @@ -851,36 +1084,47 @@ Gareth Rees Steve Reeves Lennart Regebro +John Regehr +Federico Reghenzani Ofir Reichenberg Sean Reifschneider Michael P. Reilly Bernhard Reiter Steven Reiz Roeland Rengelink +Antoine Reversat +Fl??vio Ribeiro +Francesco Ricciardi Tim Rice -Francesco Ricciardi Jan Pieter Riegel Armin Rigo +Arc Riley Nicholas Riley Jean-Claude Rimbault Vlad Riscutia Wes Rishel -Dan Riti +Daniel Riti Juan M. Bello Rivas Davide Rizzo Anthony Roach Carl Robben Mark Roberts +Andy Robinson Jim Robinson -Andy Robinson +Mark Roddy Kevin Rodgers +Sean Rodman Giampaolo Rodola +Elson Rodriguez Adi Roiban +Luis Rojas Mike Romberg Armin Ronacher Case Roole Timothy Roscoe +Erik Rose Jim Roskind +Brian Rosner Guido van Rossum Just van Rossum Hugo van Rossum @@ -892,22 +1136,28 @@ Clinton Roy Paul Rubin Sam Ruby +Demur Rumed Audun S. Runde Eran Rundstein Rauli Ruohonen Jeff Rush Sam Rushing Mark Russell +Rusty Russell Nick Russo Chris Ryland Constantina S. +Patrick Sabin S??bastien Sabl?? Suman Saha Hajime Saitou George Sakkis Rich Salz Kevin Samborn +Adrian Sampson +James Sanders Ilya Sandler +Rafael Santos Simon Sapin Mark Sapiro Ty Sarna @@ -916,39 +1166,46 @@ Ben Sayer sbt Marco Scataglini +Andrew Schaaf Michael Scharf +Andreas Schawo Neil Schemenauer David Scherer +Wolfgang Scherer Hynek Schlawack +Bob Schmertz Gregor Schmid Ralf Schmitt Michael Schneider Peter Schneider-Kamp Arvin Schnell Scott Schram +Robin Schreiber Chad J. Schroeder Christian Schubert Sam Schulenburg Stefan Schwarzer Dietmar Schwertberger Federico Schwindt +Barry Scott Steven Scott -Barry Scott Nick Seidenman -??iga Seilnach +??iga Seilnacht Yury Selivanov Fred Sells Jiwon Seo +I??igo Serna Joakim Sernbrant -Roger Serwy +Roger D. Serwy Jerry Seutter Pete Sevander Denis Severson Ian Seyer -Aman Shah +Daniel Shahaf Ha Shao Mark Shannon Richard Shapiro +Varun Sharma Vlad Shcherbina Justin Sheehy Charlie Shepherd @@ -958,18 +1215,18 @@ Michael Shiplett John W. Shipman Joel Shprentz -Itamar Shtull-Trauring Yue Shuaijie Terrel Shumway Eric Siegerman Paul Sijben +SilentGhost Tim Silk Michael Simcich Ionel Simionescu Kirill Simonov Nathan Paul Simons Guilherme Sim??es -Kyle Simpson +Adam Simpkins Ravi Sinha Janne Sinkkonen Ng Pheng Siong @@ -977,16 +1234,21 @@ J. Sipprell Kragen Sitaker Michael Sloan +Nick Sloan +V??clav ??milauer +Christopher Smith Eric V. Smith -Christopher Smith Gregory P. Smith +Mark Smith Roy Smith Ryan Smith-Roberts Rafal Smotrzyk +Eric Snow Dirk Soede Paul Sokolovsky Evgeny Sologubov Cody Somerville +Edoardo Spadolini Clay Spence Stefan Sperling Nicholas Spies @@ -998,10 +1260,13 @@ Tage Stabell-Kulo Quentin Stafford-Fraser Frank Stajano +Joel Stanley Anthony Starks Oliver Steele Greg Stein +Baruch Sterin Chris Stern +Alex Stewart Victor Stinner Richard Stoakley Peter Stoehr @@ -1009,28 +1274,35 @@ Michael Stone Serhiy Storchaka Ken Stox -Patrick Strawderman Dan Stromberg +Donald Stufft Daniel Stutzbach Andreas St??hrk +Colin Su +Pal Subbiah Nathan Sullivan Mark Summerfield Reuben Sumner +Marek ??uppa Hisao Suzuki +Kalle Svensson Andrew Svetlov -Kalle Svensson Paul Swartz Thenault Sylvain P??ter Szab?? +John Szakmeister Amir Szekely Arfrever Frehtes Taifersar Arahesis +Hideaki Takahashi +Indra Talip +Neil Tallim Geoff Talvola +Musashi Tamura William Tanksley Christian Tanzer -Stefano Taschini Steven Taschuk +Amy Taylor Monty Taylor -Amy Taylor Anatoly Techtonik Mikhail Terekhov Victor Terr??n @@ -1041,28 +1313,40 @@ Nicolas M. Thi??ry James Thomas Robin Thomas +Brian Thorne Stephen Thorne +Jeremy Thurgood Eric Tiedemann +July Tikhonov Tracy Tims Oren Tirosh Jason Tishler Christian Tismer Jim Tittsler Frank J. Tobin +Bennett Todd R Lindsay Todd -Bennett Todd Eugene Toder +Erik Tollerud +Stephen Tonkin Matias Torchinsky Sandro Tosi Richard Townsend +David Townshend +Nathan Trapuzzano Laurence Tratt +Alberto Trevino +Matthias Troffaes Tom Tromey John Tromp +Diane Trout Jason Trowbridge Brent Tubbs Anthony Tuininga +Erno Tukia David Turner Stephen Turner +Itamar Turner-Trauring Theodore Turocy Bill Tutt Fraser Tweedale @@ -1070,19 +1354,23 @@ Eren T??rkay Lionel Ulmer Roger Upole +Daniel Urban Michael Urman Hector Urtubia Ville Vainio Andi Vajda Case Van Horsen Kyle VanderBeek +Andrew Vant Atul Varma Dmitry Vasiliev Sebastian Ortiz Vasquez Alexandre Vassalotti +Nadeem Vawda Frank Vercruesse Mike Verdone Jaap Vermeulen +Nikita Vetoshkin Al Vezza Jacques A. Vidrine John Viega @@ -1090,22 +1378,29 @@ Kannan Vijayan Kurt Vile Norman Vine +Pauli Virtanen Frank Visser Johannes Vogel Alex Volkov +Guido Vranken Martijn Vries +Sjoerd de Vries Niki W. Waibel Wojtek Walczak Charles Waldman Richard Walker Larry Wall Kevin Walzer +Rodrigo Steinmuller Wanderley Ke Wang Greg Ward +Tom Wardill Zachary Ware +Jonas Wagner Barry Warsaw Steve Waterbury Bob Watson +David Watson Aaron Watters Henrik Weber Corran Webster @@ -1125,12 +1420,15 @@ Felix Wiemann Gerry Wiener Frank Wierzbicki +Santoso Wijaya Bryce "Zooko" Wilcox-O'Hearn Timothy Wild +Jakub Wilk +Gerald S. Williams Jason Williams John Williams Sue Williams -Gerald S. Williams +Carol Willing Steven Willis Frank Willison Geoff Wilson @@ -1146,25 +1444,30 @@ John Wiseman Chris Withers Stefan Witzel +Irek Wlizlo David Wolever Klaus-Juergen Wolf Dan Wolfe Richard Wolff Adam Woodbeck Steven Work +Gordon Worley Darren Worrall -Gordon Worley Thomas Wouters +Daniel Wozniak Heiko Wundram Doug Wyatt Robert Xiao Florent Xicluna Hirokazu Yamamoto Ka-Ping Yee +Jason Yeo +EungJun Yi Bob Yodlowski Danny Yoo Rory Yorke George Yoshida +Kazuhiro Yoshida Masazumi Yoshikawa Arnaud Ysmal Bernard Yue @@ -1175,7 +1478,10 @@ Yury V. Zaytsev Siebren van der Zee Nickolai Zeldovich +Yuxiao Zeng Uwe Zessin Cheng Zhang +Kai Zhu Tarek Ziad?? +Gennadiy Zlobin Peter ??strand diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -1,7 +1,8 @@ ++++++++++++ Python News +++++++++++ -What's New in Python 2.7.7? +What's New in Python 2.7.8? =========================== *Release date: XXXX-XX-XX* @@ -9,6 +10,139 @@ Core and Builtins ----------------- +- Issue #1856: Avoid crashes and lockups when daemon threads run while the + interpreter is shutting down; instead, these threads are now killed when they + try to take the GIL. + +- Issue #19656: Running Python with the -3 option now also warns about + non-ascii bytes literals. + +- Issue #21642: If the conditional if-else expression, allow an integer written + with no space between itself and the ``else`` keyword (e.g. ``True if 42else + False``) to be valid syntax. + +- Issue #21523: Fix over-pessimistic computation of the stack effect of + some opcodes in the compiler. This also fixes a quadratic compilation + time issue noticeable when compiling code with a large number of "and" + and "or" operators. + +Library +------- + +- Issue #21722: The distutils "upload" command now exits with a non-zero + return code when uploading fails. Patch by Martin Dengler. + +- Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths + before checking for a CGI script at that path. + +- Issue #21310: Fixed possible resource leak in failed open(). + +- Issue #21304: Backport the key derivation function hashlib.pbkdf2_hmac from + Python 3 per PEP 466. + +- Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a + valid file. + +- Issue #13223: Fix pydoc.writedoc so that the HTML documentation for methods + that use 'self' in the example code is generated correctly. + +- Issue #21552: Fixed possible integer overflow of too long string lengths in + the tkinter module on 64-bit platforms. + +- Issue #14315: The zipfile module now ignores extra fields in the central + directory that are too short to be parsed instead of letting a struct.unpack + error bubble up as this "bad data" appears in many real world zip files in + the wild and is ignored by other zip tools. + +- Issue #21402: Tkinter.ttk now works when default root window is not set. + +- Issue #10203: sqlite3.Row now truly supports sequence protocol. In particulr + it supports reverse() and negative indices. Original patch by Claudiu Popa. + +- Issue #8743: Fix interoperability between set objects and the + collections.Set() abstract base class. + +- Issue #21481: Argparse equality and inequality tests now return + NotImplemented when comparing to an unknown type. + +IDLE +---- + +- Issue #21686: add unittest for HyperParser. Original patch by Saimadhav + Heblikar. + +- Issue #12387: Add missing upper(lower)case versions of default Windows key + bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy. + +- Issue #21695: Closing a Find-in-files output window while the search is + still in progress no longer closes Idle. + +- Issue #18910: Add unittest for textView. Patch by Phil Webster. + +- Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. + +- Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster. + +Tests +----- + +- Issue #19493: Refactored the ctypes test package to skip tests explicitly + rather than silently. + +- Issue #18492: All resources are now allowed when tests are not run by + regrtest.py. + +- Issue #21605: Added tests for Tkinter images. + +- Issue #21493: Added test for ntpath.expanduser(). Original patch by + Claudiu Popa. + +- Issue #19925: Added tests for the spwd module. Original patch by Vajrasky Kok. + +- Issue #13355: random.triangular() no longer fails with a ZeroDivisionError + when low equals high. + +- Issue #21522: Added Tkinter tests for Listbox.itemconfigure(), + PanedWindow.paneconfigure(), and Menu.entryconfigure(). + +- Issue #20635: Added tests for Tk geometry managers. + +Windows +------- + +- Issue #21671, CVE-2014-0224: The bundled version of OpenSSL has been + updated to 1.0.1h. + +What's New in Python 2.7.7 +========================== + +*Release date: 2014-05-31* + +Build +----- + +- Issue #21462: Build the Windows installers with OpenSSL 1.0.1g. + +- Issue #19866: Include some test data in the Windows installers, so tests don't + fail. + + +What's New in Python 2.7.7 release candidate 1? +=============================================== + +*Release date: 2014-05-17* + +Core and Builtins +----------------- + +- Issue #21350: Fix file.writelines() to accept arbitrary buffer objects, + as advertised. Patch by Brian Kearns. + +- Issue #20437: Fixed 43 potential bugs when deleting objects references. + +- Issue #21134: Fix segfault when str is called on an uninitialized + UnicodeEncodeError, UnicodeDecodeError, or UnicodeTranslateError object. + - Issue #20494: Ensure that free()d memory arenas are really released on POSIX systems supporting anonymous memory mappings. Patch by Charles-Fran??ois Natali. @@ -16,11 +150,6 @@ - Issue #17825: Cursor "^" is correctly positioned for SyntaxError and IndentationError. -- Issue #19081: When a zipimport .zip file in sys.path being imported from - is modified during the lifetime of the Python process after zipimport has - already cached the zip's table of contents we detect this and recover - rather than read bad data from the .zip (causing odd import errors). - - Raise a better error when non-unicode codecs are used for a file's coding cookie. @@ -39,9 +168,100 @@ - Issue #19638: Fix possible crash / undefined behaviour from huge (more than 2 billion characters) input strings in _Py_dg_strtod. +- Issue #12546: Allow \x00 to be used as a fill character when using str, int, + float, and complex __format__ methods. + Library ------- +- Issue #10744: Fix PEP 3118 format strings on ctypes objects with a nontrivial + shape. + +- Issue #7776: Backport Fix ``Host:'' header and reconnection when using + http.client.HTTPConnection.set_tunnel() from Python 3. Patch by Nikolaus + Rath. + +- Issue #21306: Backport hmac.compare_digest from Python 3. This is part of PEP + 466. + +- Issue #21470: Do a better job seeding the random number generator by + using enough bytes to span the full state space of the Mersenne Twister. + +- Issue #21469: Reduced the risk of false positives in robotparser by + checking to make sure that robots.txt has been read or does not exist + prior to returning True in can_fetch(). + +- Issue #21321: itertools.islice() now releases the reference to the source + iterator when the slice is exhausted. Patch by Anton Afanasyev. + +- Issue #9291: Do not attempt to re-encode mimetype data read from registry in + ANSI mode. Initial patches by Dmitry Jemerov & Vladimir Iofik. + +- Issue #21349: Passing a memoryview to _winreg.SetValueEx now correctly raises + a TypeError where it previously crashed the interpreter. Patch by Brian Kearns + +- Fix arbitrary memory access in JSONDecoder.raw_decode with a negative second + parameter. Bug reported by Guido Vranken. + +- Issue #21172: isinstance check relaxed from dict to collections.Mapping. + +- Issue #21191: In os.fdopen, never close the file descriptor when an exception + happens. + +- Issue #21149: Improved thread-safety in logging cleanup during interpreter + shutdown. Thanks to Devin Jeanpierre for the patch. + +- Fix possible overflow bug in strop.expandtabs. You shouldn't be using this + module! + +- Issue #20145: `assertRaisesRegex` now raises a TypeError if the second + argument is not a string or compiled regex. + +- Issue #21058: Fix a leak of file descriptor in tempfile.NamedTemporaryFile(), + close the file descriptor if os.fdopen() fails + +- Issue #20283: RE pattern methods now accept the string keyword parameters + as documented. The pattern and source keyword parameters are left as + deprecated aliases. + +- Issue #11599: When an external command (e.g. compiler) fails, distutils now + prints out the whole command line (instead of just the command name) if the + environment variable DISTUTILS_DEBUG is set. + +- Issue #4931: distutils should not produce unhelpful "error: None" messages + anymore. distutils.util.grok_environment_error is kept but doc-deprecated. + +- Improve the random module's default seeding to use 256 bits of entropy + from os.urandom(). This was already done for Python 3, mildly improving + security with a bigger seed space. + +- Issue #15618: Make turtle.py compatible with 'from __future__ import + unicode_literals'. Initial patch by Juancarlo A??ez. + +- Issue #20501: fileinput module no longer reads whole file into memory when using + fileinput.hook_encoded. + +- Issue #6815: os.path.expandvars() now supports non-ASCII Unicode environment + variables names and values. + +- Issue #20635: Fixed grid_columnconfigure() and grid_rowconfigure() methods of + Tkinter widgets to work in wantobjects=True mode. + +- Issue #17671: Fixed a crash when use non-initialized io.BufferedRWPair. + Based on patch by Stephen Tu. + +- Issue #8478: Untokenizer.compat processes first token from iterator input. + Patch based on lines from Georg Brandl, Eric Snow, and Gareth Rees. + +- Issue #20594: Avoid name clash with the libc function posix_close. + +- Issue #19856: shutil.move() failed to move a directory to other directory + on Windows if source name ends with os.altsep. + +- 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. @@ -200,9 +420,25 @@ - Issue #19286: Directories in ``package_data`` are no longer added to the filelist, preventing failure outlined in the ticket. +- Issue #6676: Ensure a meaningful exception is raised when attempting + to parse more than one XML document per pyexpat xmlparser instance. + (Original patches by Hirokazu Yamamoto and Amaury Forgeot d'Arc, with + suggested wording by David Gutteridge) + +- Issue #21311: Avoid exception in _osx_support with non-standard compiler + configurations. Patch by John Szakmeister. + Tools/Demos ----------- +- Issue #3561: The Windows installer now has an option, off by default, for + placing the Python installation into the system "Path" environment variable. + This was backported from Python 3.3. + +- Add support for ``yield from`` to 2to3. + +- Add support for the PEP 465 matrix multiplication operator to 2to3. + - Issue #19936: Added executable bits or shebang lines to Python scripts which requires them. Disable executable bits and shebang lines in test and benchmark files in order to prevent using a random system python, and in @@ -211,9 +447,21 @@ IDLE ---- +- Issue #18104: Add idlelib/idle_test/htest.py with a few sample tests to begin + consolidating and improving human-validated tests of Idle. Change other files + as needed to work with htest. Running the module as __main__ runs all tests. + +- Issue #21139: Change default paragraph width to 72, the PEP 8 recommendation. + +- Issue #21284: Paragraph reformat test passes after user changes reformat width. + - Issue #20406: Use Python application icons for Idle window title bars. Patch mostly by Serhiy Storchaka. +- Issue #21029: Occurrences of "print" are now consistently colored as + being a keyword (the colorizer doesn't know if print functions are + enabled in the source). + - Issue #17721: Remove non-functional configuration dialog help button until we make it actually gives some help when clicked. Patch by Guilherme Sim??es. @@ -228,9 +476,26 @@ - Issue #18270: Prevent possible IDLE AttributeError on OS X when no initial shell window is present. +- Issue #17654: Ensure IDLE menus are customized properly on OS X for + non-framework builds and for all variants of Tk. + Tests ----- +- Issue #17752: Fix distutils tests when run from the installed location. + +- Issue #18604: Consolidated checks for GUI availability. All platforms now + at least check whether Tk can be instantiated when the GUI resource is + requested. + +- Issue #20946: Correct alignment assumptions of some ctypes tests. + +- Issue #20743: Fix a reference leak in test_tcl. + +- Issue #20510: Rewrote test_exit in test_sys to match existing comments, + use modern unittest features, and use helpers from test.script_helper + instead of using subprocess directly. Initial patch by Gareth Rees. + - 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. @@ -268,6 +533,21 @@ - Issue #19085: Added basic tests for all tkinter widget options. +- Issue #20605: Make test_socket getaddrinfo OS X segfault test more robust. + +- Issue #20939: Avoid various network test failures due to new + redirect of http://www.python.org/ to https://www.python.org: + use http://www.example.com instead. + +- Issue #21093: Prevent failures of ctypes test_macholib on OS X if a + copy of libz exists in $HOME/lib or /usr/local/lib. + +Build +----- + +- Issue #21285: Refactor and fix curses configure check to always search + in a ncursesw directory. + Documentation ------------- @@ -278,6 +558,26 @@ - Issue #19795: Improved markup of True/False constants. +Windows +------- + +- Issue #21303, #20565: Updated the version of Tcl/Tk included in the + installer from 8.5.2 to 8.5.15. + +Mac OS X +-------- + +- As of 2.7.8, the 32-bit-only installer will support OS X 10.5 + and later systems as is currently done for Python 3.x installers. + For 2.7.7 only, we will provide three installers: + the legacy deprecated 10.3+ 32-bit-only format; + the newer 10.5+ 32-bit-only format; + and the unchanged 10.6+ 64-/32-bit format. + Although binary installers will no longer be available from + python.org as of 2.7.8, it will still be possible to build from + source on 10.3.9 and 10.4 systems if necessary. + See Mac/BuildScript/README.txt for more information. + Whats' New in Python 2.7.6? =========================== diff --git a/Misc/RPM/python-2.7.spec b/Misc/RPM/python-2.7.spec --- a/Misc/RPM/python-2.7.spec +++ b/Misc/RPM/python-2.7.spec @@ -39,7 +39,7 @@ %define name python #--start constants-- -%define version 2.7.6 +%define version 2.7.7 %define libvers 2.7 #--end constants-- %define release 1pydotorg diff --git a/Modules/_bsddb.c b/Modules/_bsddb.c --- a/Modules/_bsddb.c +++ b/Modules/_bsddb.c @@ -949,8 +949,7 @@ * DBTxns and closing any open DBs first. */ if (makeDBError(err)) { if (self->myenvobj) { - Py_DECREF(self->myenvobj); - self->myenvobj = NULL; + Py_CLEAR(self->myenvobj); } Py_DECREF(self); self = NULL; @@ -982,20 +981,16 @@ PyObject_ClearWeakRefs((PyObject *) self); } if (self->myenvobj) { - Py_DECREF(self->myenvobj); - self->myenvobj = NULL; + Py_CLEAR(self->myenvobj); } if (self->associateCallback != NULL) { - Py_DECREF(self->associateCallback); - self->associateCallback = NULL; + Py_CLEAR(self->associateCallback); } if (self->btCompareCallback != NULL) { - Py_DECREF(self->btCompareCallback); - self->btCompareCallback = NULL; + Py_CLEAR(self->btCompareCallback); } if (self->dupCompareCallback != NULL) { - Py_DECREF(self->dupCompareCallback); - self->dupCompareCallback = NULL; + Py_CLEAR(self->dupCompareCallback); } Py_DECREF(self->private_obj); PyObject_Del(self); @@ -1160,8 +1155,7 @@ PyErr_Clear(); } - Py_XDECREF(self->event_notifyCallback); - self->event_notifyCallback = NULL; + Py_CLEAR(self->event_notifyCallback); if (self->in_weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) self); @@ -1640,8 +1634,7 @@ MYDB_END_ALLOW_THREADS; if (err) { - Py_XDECREF(secondaryDB->associateCallback); - secondaryDB->associateCallback = NULL; + Py_CLEAR(secondaryDB->associateCallback); secondaryDB->primaryDBType = 0; } diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -194,10 +194,8 @@ if (-1 == PyDict_DelItem(self->dict, self->key)) /* XXX Error context */ PyErr_WriteUnraisable(Py_None); - Py_DECREF(self->key); - self->key = NULL; - Py_DECREF(self->dict); - self->dict = NULL; + Py_CLEAR(self->key); + Py_CLEAR(self->dict); } Py_INCREF(Py_None); return Py_None; @@ -324,6 +322,48 @@ } /* + Allocate a memory block for a pep3118 format string, adding + the given prefix (if non-null), an additional shape prefix, and a suffix. + Returns NULL on failure, with the error indicator set. If called with + a suffix of NULL the error indicator must already be set. + */ +char * +_ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, + const char *prefix, const char *suffix) +{ + char *new_prefix; + char *result; + char buf[32]; + int prefix_len; + int k; + + prefix_len = 32 * ndim + 3; + if (prefix) + prefix_len += strlen(prefix); + new_prefix = PyMem_Malloc(prefix_len); + if (new_prefix == NULL) + return NULL; + new_prefix[0] = '\0'; + if (prefix) + strcpy(new_prefix, prefix); + if (ndim > 0) { + /* Add the prefix "(shape[0],shape[1],...,shape[ndim-1])" */ + strcat(new_prefix, "("); + for (k = 0; k < ndim; ++k) { + if (k < ndim-1) { + sprintf(buf, "%"PY_FORMAT_SIZE_T"d,", shape[k]); + } else { + sprintf(buf, "%"PY_FORMAT_SIZE_T"d)", shape[k]); + } + strcat(new_prefix, buf); + } + } + result = _ctypes_alloc_format_string(new_prefix, suffix); + PyMem_Free(new_prefix); + return result; +} + +/* PyCStructType_Type - a meta type/class. Creating a new class using this one as __metaclass__ will call the contructor StructUnionType_new. It replaces the tp_dict member with a new instance of StgDict, and initializes the C @@ -919,14 +959,21 @@ if (proto) { StgDictObject *itemdict = PyType_stgdict(proto); + const char *current_format; assert(itemdict); /* If itemdict->format is NULL, then this is a pointer to an incomplete type. We create a generic format string 'pointer to bytes' in this case. XXX Better would be to fix the format string later... */ - stgdict->format = _ctypes_alloc_format_string("&", - itemdict->format ? itemdict->format : "B"); + current_format = itemdict->format ? itemdict->format : "B"; + if (itemdict->shape != NULL) { + /* pointer to an array: the shape needs to be prefixed */ + stgdict->format = _ctypes_alloc_format_string_with_shape( + itemdict->ndim, itemdict->shape, "&", current_format); + } else { + stgdict->format = _ctypes_alloc_format_string("&", current_format); + } if (stgdict->format == NULL) { Py_DECREF((PyObject *)stgdict); return NULL; @@ -1328,7 +1375,6 @@ long length; Py_ssize_t itemsize, itemalign; - char buf[32]; typedict = PyTuple_GetItem(args, 2); if (!typedict) @@ -1364,13 +1410,7 @@ } assert(itemdict->format); - if (itemdict->format[0] == '(') { - sprintf(buf, "(%ld,", length); - stgdict->format = _ctypes_alloc_format_string(buf, itemdict->format+1); - } else { - sprintf(buf, "(%ld)", length); - stgdict->format = _ctypes_alloc_format_string(buf, itemdict->format); - } + stgdict->format = _ctypes_alloc_format_string(NULL, itemdict->format); if (stgdict->format == NULL) { Py_DECREF((PyObject *)stgdict); return NULL; @@ -3040,10 +3080,8 @@ PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob) { if (ob == NULL) { - Py_XDECREF(self->restype); - self->restype = NULL; - Py_XDECREF(self->checker); - self->checker = NULL; + Py_CLEAR(self->restype); + Py_CLEAR(self->checker); return 0; } if (ob != Py_None && !PyType_stgdict(ob) && !PyCallable_Check(ob)) { @@ -3086,10 +3124,8 @@ PyObject *converters; if (ob == NULL || ob == Py_None) { - Py_XDECREF(self->converters); - self->converters = NULL; - Py_XDECREF(self->argtypes); - self->argtypes = NULL; + Py_CLEAR(self->converters); + Py_CLEAR(self->argtypes); } else { converters = converters_from_argtypes(ob); if (!converters) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1687,9 +1687,9 @@ /* #define CHAR_ALIGN (sizeof(s_char) - sizeof(char)) #define SHORT_ALIGN (sizeof(s_short) - sizeof(short)) -#define INT_ALIGN (sizeof(s_int) - sizeof(int)) #define LONG_ALIGN (sizeof(s_long) - sizeof(long)) */ +#define INT_ALIGN (sizeof(s_int) - sizeof(int)) #define FLOAT_ALIGN (sizeof(s_float) - sizeof(float)) #define DOUBLE_ALIGN (sizeof(s_double) - sizeof(double)) #define LONGDOUBLE_ALIGN (sizeof(s_long_double) - sizeof(long double)) @@ -1731,8 +1731,8 @@ ffi_type ffi_type_uint16 = { 2, 2, FFI_TYPE_UINT16 }; ffi_type ffi_type_sint16 = { 2, 2, FFI_TYPE_SINT16 }; -ffi_type ffi_type_uint32 = { 4, 4, FFI_TYPE_UINT32 }; -ffi_type ffi_type_sint32 = { 4, 4, FFI_TYPE_SINT32 }; +ffi_type ffi_type_uint32 = { 4, INT_ALIGN, FFI_TYPE_UINT32 }; +ffi_type ffi_type_sint32 = { 4, INT_ALIGN, FFI_TYPE_SINT32 }; ffi_type ffi_type_uint64 = { 8, LONG_LONG_ALIGN, FFI_TYPE_UINT64 }; ffi_type ffi_type_sint64 = { 8, LONG_LONG_ALIGN, FFI_TYPE_SINT64 }; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -434,6 +434,9 @@ extern PyObject *PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr); extern char *_ctypes_alloc_format_string(const char *prefix, const char *suffix); +extern char *_ctypes_alloc_format_string_with_shape(int ndim, + const Py_ssize_t *shape, + const char *prefix, const char *suffix); extern int _ctypes_simple_instance(PyObject *obj); diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -518,7 +518,12 @@ sprintf(buf, "%s:%s:", fieldfmt, fieldname); ptr = stgdict->format; - stgdict->format = _ctypes_alloc_format_string(stgdict->format, buf); + if (dict->shape != NULL) { + stgdict->format = _ctypes_alloc_format_string_with_shape( + dict->ndim, dict->shape, stgdict->format, buf); + } else { + stgdict->format = _ctypes_alloc_format_string(stgdict->format, buf); + } PyMem_Free(ptr); PyMem_Free(buf); diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2739,10 +2739,10 @@ target->events = events; /* clear out existing events */ - Py_XDECREF(target->start_event_obj); target->start_event_obj = NULL; - Py_XDECREF(target->end_event_obj); target->end_event_obj = NULL; - Py_XDECREF(target->start_ns_event_obj); target->start_ns_event_obj = NULL; - Py_XDECREF(target->end_ns_event_obj); target->end_ns_event_obj = NULL; + Py_CLEAR(target->start_event_obj); + Py_CLEAR(target->end_event_obj); + Py_CLEAR(target->start_ns_event_obj); + Py_CLEAR(target->end_ns_event_obj); if (event_set == Py_None) { /* default is "end" only */ diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -37,6 +37,8 @@ /* EVP is the preferred interface to hashing in OpenSSL */ #include +#include +#include #define MUNCH_SIZE INT_MAX @@ -491,6 +493,225 @@ return ret_obj; } + +#if (OPENSSL_VERSION_NUMBER >= 0x10000000 && !defined(OPENSSL_NO_HMAC) \ + && !defined(OPENSSL_NO_SHA)) + +#define PY_PBKDF2_HMAC 1 + +/* Improved implementation of PKCS5_PBKDF2_HMAC() + * + * PKCS5_PBKDF2_HMAC_fast() hashes the password exactly one time instead of + * `iter` times. Today (2013) the iteration count is typically 100,000 or + * more. The improved algorithm is not subject to a Denial-of-Service + * vulnerability with overly large passwords. + * + * Also OpenSSL < 1.0 don't provide PKCS5_PBKDF2_HMAC(), only + * PKCS5_PBKDF2_SHA1. + */ +static int +PKCS5_PBKDF2_HMAC_fast(const char *pass, int passlen, + const unsigned char *salt, int saltlen, + int iter, const EVP_MD *digest, + int keylen, unsigned char *out) +{ + unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4]; + int cplen, j, k, tkeylen, mdlen; + unsigned long i = 1; + HMAC_CTX hctx_tpl, hctx; + + mdlen = EVP_MD_size(digest); + if (mdlen < 0) + return 0; + + HMAC_CTX_init(&hctx_tpl); + HMAC_CTX_init(&hctx); + p = out; + tkeylen = keylen; + if (!HMAC_Init_ex(&hctx_tpl, pass, passlen, digest, NULL)) { + HMAC_CTX_cleanup(&hctx_tpl); + return 0; + } + while(tkeylen) { + if(tkeylen > mdlen) + cplen = mdlen; + else + cplen = tkeylen; + /* We are unlikely to ever use more than 256 blocks (5120 bits!) + * but just in case... + */ + itmp[0] = (unsigned char)((i >> 24) & 0xff); + itmp[1] = (unsigned char)((i >> 16) & 0xff); + itmp[2] = (unsigned char)((i >> 8) & 0xff); + itmp[3] = (unsigned char)(i & 0xff); + if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) { + HMAC_CTX_cleanup(&hctx_tpl); + return 0; + } + if (!HMAC_Update(&hctx, salt, saltlen) + || !HMAC_Update(&hctx, itmp, 4) + || !HMAC_Final(&hctx, digtmp, NULL)) { + HMAC_CTX_cleanup(&hctx_tpl); + HMAC_CTX_cleanup(&hctx); + return 0; + } + HMAC_CTX_cleanup(&hctx); + memcpy(p, digtmp, cplen); + for (j = 1; j < iter; j++) { + if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) { + HMAC_CTX_cleanup(&hctx_tpl); + return 0; + } + if (!HMAC_Update(&hctx, digtmp, mdlen) + || !HMAC_Final(&hctx, digtmp, NULL)) { + HMAC_CTX_cleanup(&hctx_tpl); + HMAC_CTX_cleanup(&hctx); + return 0; + } + HMAC_CTX_cleanup(&hctx); + for (k = 0; k < cplen; k++) { + p[k] ^= digtmp[k]; + } + } + tkeylen-= cplen; + i++; + p+= cplen; + } + HMAC_CTX_cleanup(&hctx_tpl); + return 1; +} + +/* LCOV_EXCL_START */ +static PyObject * +_setException(PyObject *exc) +{ + unsigned long errcode; + const char *lib, *func, *reason; + + errcode = ERR_peek_last_error(); + if (!errcode) { + PyErr_SetString(exc, "unknown reasons"); + return NULL; + } + ERR_clear_error(); + + lib = ERR_lib_error_string(errcode); + func = ERR_func_error_string(errcode); + reason = ERR_reason_error_string(errcode); + + if (lib && func) { + PyErr_Format(exc, "[%s: %s] %s", lib, func, reason); + } + else if (lib) { + PyErr_Format(exc, "[%s] %s", lib, reason); + } + else { + PyErr_SetString(exc, reason); + } + return NULL; +} +/* LCOV_EXCL_STOP */ + +PyDoc_STRVAR(pbkdf2_hmac__doc__, +"pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None) -> key\n\ +\n\ +Password based key derivation function 2 (PKCS #5 v2.0) with HMAC as\n\ +pseudorandom function."); + +static PyObject * +pbkdf2_hmac(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"hash_name", "password", "salt", "iterations", + "dklen", NULL}; + PyObject *key_obj = NULL, *dklen_obj = Py_None; + char *name, *key; + Py_buffer password, salt; + long iterations, dklen; + int retval; + const EVP_MD *digest; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "ss*s*l|O:pbkdf2_hmac", + kwlist, &name, &password, &salt, + &iterations, &dklen_obj)) { + return NULL; + } + + digest = EVP_get_digestbyname(name); + if (digest == NULL) { + PyErr_SetString(PyExc_ValueError, "unsupported hash type"); + goto end; + } + + if (password.len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "password is too long."); + goto end; + } + + if (salt.len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "salt is too long."); + goto end; + } + + if (iterations < 1) { + PyErr_SetString(PyExc_ValueError, + "iteration value must be greater than 0."); + goto end; + } + if (iterations > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "iteration value is too great."); + goto end; + } + + if (dklen_obj == Py_None) { + dklen = EVP_MD_size(digest); + } else { + dklen = PyLong_AsLong(dklen_obj); + if ((dklen == -1) && PyErr_Occurred()) { + goto end; + } + } + if (dklen < 1) { + PyErr_SetString(PyExc_ValueError, + "key length must be greater than 0."); + goto end; + } + if (dklen > INT_MAX) { + /* INT_MAX is always smaller than dkLen max (2^32 - 1) * hLen */ + PyErr_SetString(PyExc_OverflowError, + "key length is too great."); + goto end; + } + + key_obj = PyBytes_FromStringAndSize(NULL, dklen); + if (key_obj == NULL) { + goto end; + } + key = PyBytes_AS_STRING(key_obj); + + Py_BEGIN_ALLOW_THREADS + retval = PKCS5_PBKDF2_HMAC_fast((char*)password.buf, (int)password.len, + (unsigned char *)salt.buf, (int)salt.len, + iterations, digest, dklen, + (unsigned char *)key); + Py_END_ALLOW_THREADS + + if (!retval) { + Py_CLEAR(key_obj); + _setException(PyExc_ValueError); + goto end; + } + + end: + PyBuffer_Release(&password); + PyBuffer_Release(&salt); + return key_obj; +} + +#endif + /* * This macro generates constructor function definitions for specific * hash algorithms. These constructors are much faster than calling @@ -558,6 +779,10 @@ CONSTRUCTOR_METH_DEF(sha384), CONSTRUCTOR_METH_DEF(sha512), #endif +#ifdef PY_PBKDF2_HMAC + {"pbkdf2_hmac", (PyCFunction)pbkdf2_hmac, METH_VARARGS|METH_KEYWORDS, + pbkdf2_hmac__doc__}, +#endif {NULL, NULL} /* Sentinel */ }; diff --git a/Modules/_heapqmodule.c b/Modules/_heapqmodule.c --- a/Modules/_heapqmodule.c +++ b/Modules/_heapqmodule.c @@ -86,7 +86,7 @@ static int _siftup(PyListObject *heap, Py_ssize_t pos) { - Py_ssize_t startpos, endpos, childpos, rightpos; + Py_ssize_t startpos, endpos, childpos, rightpos, limit; int cmp; PyObject *newitem, *tmp, *olditem; Py_ssize_t size; @@ -103,9 +103,10 @@ Py_INCREF(newitem); /* Bubble up the smaller child until hitting a leaf. */ - childpos = 2*pos + 1; /* leftmost child position */ - while (childpos < endpos) { + limit = endpos / 2; /* smallest pos that has no child */ + while (pos < limit) { /* Set childpos to index of smaller child. */ + childpos = 2*pos + 1; /* leftmost child position */ rightpos = childpos + 1; if (rightpos < endpos) { cmp = cmp_lt( @@ -131,7 +132,6 @@ PyList_SET_ITEM(heap, pos, tmp); Py_DECREF(olditem); pos = childpos; - childpos = 2*pos + 1; if (size != PyList_GET_SIZE(heap)) { PyErr_SetString(PyExc_RuntimeError, "list changed size during iteration"); @@ -439,7 +439,7 @@ static int _siftupmax(PyListObject *heap, Py_ssize_t pos) { - Py_ssize_t startpos, endpos, childpos, rightpos; + Py_ssize_t startpos, endpos, childpos, rightpos, limit; int cmp; PyObject *newitem, *tmp; @@ -454,9 +454,10 @@ Py_INCREF(newitem); /* Bubble up the smaller child until hitting a leaf. */ - childpos = 2*pos + 1; /* leftmost child position */ - while (childpos < endpos) { + limit = endpos / 2; /* smallest pos that has no child */ + while (pos < limit) { /* Set childpos to index of smaller child. */ + childpos = 2*pos + 1; /* leftmost child position */ rightpos = childpos + 1; if (rightpos < endpos) { cmp = cmp_lt( @@ -475,7 +476,6 @@ Py_DECREF(PyList_GET_ITEM(heap, pos)); PyList_SET_ITEM(heap, pos, tmp); pos = childpos; - childpos = 2*pos + 1; } /* The leaf at pos is empty now. Put newitem there, and bubble diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -303,7 +303,7 @@ int line_buffering; long isatty; - PyObject *raw, *modeobj = NULL, *buffer = NULL, *wrapper = NULL; + PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzzi:open", kwlist, &file, &mode, &buffering, @@ -416,6 +416,7 @@ "Osi", file, rawmode, closefd); if (raw == NULL) return NULL; + result = raw; modeobj = PyUnicode_FromString(mode); if (modeobj == NULL) @@ -474,7 +475,7 @@ } Py_DECREF(modeobj); - return raw; + return result; } /* wraps into a buffered file */ @@ -495,15 +496,16 @@ buffer = PyObject_CallFunction(Buffered_class, "Oi", raw, buffering); } - Py_CLEAR(raw); if (buffer == NULL) goto error; + result = buffer; + Py_DECREF(raw); /* if binary, returns the buffered file */ if (binary) { Py_DECREF(modeobj); - return buffer; + return result; } /* wraps into a TextIOWrapper */ @@ -512,20 +514,30 @@ buffer, encoding, errors, newline, line_buffering); - Py_CLEAR(buffer); if (wrapper == NULL) goto error; + result = wrapper; + Py_DECREF(buffer); if (PyObject_SetAttrString(wrapper, "mode", modeobj) < 0) goto error; Py_DECREF(modeobj); - return wrapper; + return result; error: - Py_XDECREF(raw); + if (result != NULL) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (PyObject_CallMethod(result, "close", NULL) != NULL) + PyErr_Restore(exc, val, tb); + else { + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + } + Py_DECREF(result); + } Py_XDECREF(modeobj); - Py_XDECREF(buffer); - Py_XDECREF(wrapper); return NULL; } diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -2129,9 +2129,14 @@ static PyObject * _forward_call(buffered *self, const char *name, PyObject *args) { - PyObject *func = PyObject_GetAttrString((PyObject *)self, name); - PyObject *ret; + PyObject *func, *ret; + if (self == NULL) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on uninitialized object"); + return NULL; + } + func = PyObject_GetAttrString((PyObject *)self, name); if (func == NULL) { PyErr_SetString(PyExc_AttributeError, name); return NULL; diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -549,14 +549,8 @@ } if (PyBytes_GET_SIZE(result) < (Py_ssize_t)newsize) { - if (_PyBytes_Resize(&result, newsize) < 0) { - if (total == 0) { - Py_DECREF(result); - return NULL; - } - PyErr_Clear(); - break; - } + if (_PyBytes_Resize(&result, newsize) < 0) + return NULL; /* result has been freed */ } Py_BEGIN_ALLOW_THREADS errno = 0; @@ -599,7 +593,6 @@ if (PyBytes_GET_SIZE(result) > total) { if (_PyBytes_Resize(&result, total) < 0) { /* This should never happen, but just in case */ - Py_DECREF(result); return NULL; } } @@ -656,10 +649,8 @@ } if (n != size) { - if (_PyBytes_Resize(&bytes, n) < 0) { - Py_DECREF(bytes); + if (_PyBytes_Resize(&bytes, n) < 0) return NULL; - } } return (PyObject *) bytes; diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -41,8 +41,8 @@ "bytes. bytearrays are accepted too, and in some cases (such as\n" "readinto) needed. Text I/O classes work with str data.\n" "\n" - "Note that calling any method (even inquiries) on a closed stream is\n" - "undefined. Implementations may raise IOError in this case.\n" + "Note that calling any method (except additional calls to close(),\n" + "which are ignored) on a closed stream should raise a ValueError.\n" "\n" "IOBase (and its subclasses) support the iterator protocol, meaning\n" "that an IOBase object can be iterated over yielding the lines in a\n" diff --git a/Modules/_json.c b/Modules/_json.c --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1468,6 +1468,10 @@ PyObject *res; char *str = PyString_AS_STRING(pystr); Py_ssize_t length = PyString_GET_SIZE(pystr); + if (idx < 0) { + PyErr_SetString(PyExc_ValueError, "idx cannot be negative"); + return NULL; + } if (idx >= length) { PyErr_SetNone(PyExc_StopIteration); return NULL; @@ -1555,6 +1559,10 @@ PyObject *res; Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr); Py_ssize_t length = PyUnicode_GET_SIZE(pystr); + if (idx < 0) { + PyErr_SetString(PyExc_ValueError, "idx cannot be negative"); + return NULL; + } if (idx >= length) { PyErr_SetNone(PyExc_StopIteration); return NULL; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -369,8 +369,7 @@ if (self->apsw_connection) { ret = PyObject_CallMethod(self->apsw_connection, "close", ""); Py_XDECREF(ret); - Py_XDECREF(self->apsw_connection); - self->apsw_connection = NULL; + Py_CLEAR(self->apsw_connection); self->db = NULL; } else { Py_BEGIN_ALLOW_THREADS diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -231,8 +231,7 @@ if (converter != Py_None) { Py_DECREF(converter); } - Py_XDECREF(self->row_cast_map); - self->row_cast_map = NULL; + Py_CLEAR(self->row_cast_map); return -1; } @@ -468,8 +467,7 @@ allow_8bit_chars = ((self->connection->text_factory != (PyObject*)&PyUnicode_Type) && (self->connection->text_factory != pysqlite_OptimizedUnicode)); - Py_XDECREF(self->next_row); - self->next_row = NULL; + Py_CLEAR(self->next_row); if (multiple) { /* executemany() */ @@ -896,8 +894,7 @@ if (!self->next_row) { if (self->statement) { (void)pysqlite_statement_reset(self->statement); - Py_DECREF(self->statement); - self->statement = NULL; + Py_CLEAR(self->statement); } return NULL; } diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c --- a/Modules/_sqlite/row.c +++ b/Modules/_sqlite/row.c @@ -64,9 +64,16 @@ return 0; } +PyObject* pysqlite_row_item(pysqlite_Row* self, Py_ssize_t idx) +{ + PyObject* item = PyTuple_GetItem(self->data, idx); + Py_XINCREF(item); + return item; +} + PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx) { - long _idx; + Py_ssize_t _idx; char* key; int nitems, i; char* compare_key; @@ -78,11 +85,17 @@ if (PyInt_Check(idx)) { _idx = PyInt_AsLong(idx); + if (_idx < 0) + _idx += PyTuple_GET_SIZE(self->data); item = PyTuple_GetItem(self->data, _idx); Py_XINCREF(item); return item; } else if (PyLong_Check(idx)) { - _idx = PyLong_AsLong(idx); + _idx = PyNumber_AsSsize_t(idx, PyExc_IndexError); + if (_idx == -1 && PyErr_Occurred()) + return NULL; + if (_idx < 0) + _idx += PyTuple_GET_SIZE(self->data); item = PyTuple_GetItem(self->data, _idx); Py_XINCREF(item); return item; @@ -199,6 +212,14 @@ /* mp_ass_subscript */ (objobjargproc)0, }; +static PySequenceMethods pysqlite_row_as_sequence = { + /* sq_length */ (lenfunc)pysqlite_row_length, + /* sq_concat */ 0, + /* sq_repeat */ 0, + /* sq_item */ (ssizeargfunc)pysqlite_row_item, +}; + + static PyMethodDef pysqlite_row_methods[] = { {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS, PyDoc_STR("Returns the keys of the row.")}, @@ -252,5 +273,6 @@ { pysqlite_RowType.tp_new = PyType_GenericNew; pysqlite_RowType.tp_as_mapping = &pysqlite_row_as_mapping; + pysqlite_RowType.tp_as_sequence = &pysqlite_row_as_sequence; return PyType_Ready(&pysqlite_RowType); } diff --git a/Modules/_sre.c b/Modules/_sre.c --- a/Modules/_sre.c +++ b/Modules/_sre.c @@ -1875,18 +1875,62 @@ PyObject_DEL(self); } +static int +check_args_size(const char *name, PyObject* args, PyObject* kw, int n) +{ + Py_ssize_t m = PyTuple_GET_SIZE(args) + (kw ? PyDict_Size(kw) : 0); + if (m <= n) + return 1; + PyErr_Format(PyExc_TypeError, + "%s() takes at most %d positional arguments (%zd given)", + name, n, m); + return 0; +} + +static PyObject* +fix_string_param(PyObject *string, PyObject *string2, const char *oldname) +{ + if (string2 != NULL) { + char buf[100]; + if (string != NULL) { + PyErr_Format(PyExc_TypeError, + "Argument given by name ('%s') and position (1)", + oldname); + return NULL; + } + sprintf(buf, "The '%s' keyword parameter name is deprecated. " + "Use 'string' instead.", oldname); + if (PyErr_Warn(PyExc_DeprecationWarning, buf) < 0) + return NULL; + return string2; + } + if (string == NULL) { + PyErr_SetString(PyExc_TypeError, + "Required argument 'string' (pos 1) not found"); + return NULL; + } + return string; +} + static PyObject* pattern_match(PatternObject* self, PyObject* args, PyObject* kw) { SRE_STATE state; int status; - PyObject* string; + PyObject *string = NULL, *string2 = NULL; Py_ssize_t start = 0; Py_ssize_t end = PY_SSIZE_T_MAX; - static char* kwlist[] = { "pattern", "pos", "endpos", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kw, "O|nn:match", kwlist, - &string, &start, &end)) + static char* kwlist[] = { "string", "pos", "endpos", "pattern", NULL }; + if (!check_args_size("match", args, kw, 3)) + return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|OnnO:match", kwlist, + &string, &start, &end, &string2)) + return NULL; + + string = fix_string_param(string, string2, "pattern"); + if (!string) return NULL; string = state_init(&state, self, string, start, end); @@ -1920,12 +1964,19 @@ SRE_STATE state; int status; - PyObject* string; + PyObject *string = NULL, *string2 = NULL; Py_ssize_t start = 0; Py_ssize_t end = PY_SSIZE_T_MAX; - static char* kwlist[] = { "pattern", "pos", "endpos", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kw, "O|nn:search", kwlist, - &string, &start, &end)) + static char* kwlist[] = { "string", "pos", "endpos", "pattern", NULL }; + if (!check_args_size("search", args, kw, 3)) + return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|OnnO:search", kwlist, + &string, &start, &end, &string2)) + return NULL; + + string = fix_string_param(string, string2, "pattern"); + if (!string) return NULL; string = state_init(&state, self, string, start, end); @@ -2055,12 +2106,19 @@ int status; Py_ssize_t i, b, e; - PyObject* string; + PyObject *string = NULL, *string2 = NULL; Py_ssize_t start = 0; Py_ssize_t end = PY_SSIZE_T_MAX; - static char* kwlist[] = { "source", "pos", "endpos", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kw, "O|nn:findall", kwlist, - &string, &start, &end)) + static char* kwlist[] = { "string", "pos", "endpos", "source", NULL }; + if (!check_args_size("findall", args, kw, 3)) + return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|OnnO:findall", kwlist, + &string, &start, &end, &string2)) + return NULL; + + string = fix_string_param(string, string2, "source"); + if (!string) return NULL; string = state_init(&state, self, string, start, end); @@ -2185,11 +2243,18 @@ Py_ssize_t i; void* last; - PyObject* string; + PyObject *string = NULL, *string2 = NULL; Py_ssize_t maxsplit = 0; - static char* kwlist[] = { "source", "maxsplit", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kw, "O|n:split", kwlist, - &string, &maxsplit)) + static char* kwlist[] = { "string", "maxsplit", "source", NULL }; + if (!check_args_size("split", args, kw, 2)) + return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|OnO:split", kwlist, + &string, &maxsplit, &string2)) + return NULL; + + string = fix_string_param(string, string2, "source"); + if (!string) return NULL; string = state_init(&state, self, string, 0, PY_SSIZE_T_MAX); diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -1021,6 +1021,16 @@ 0, /*tp_is_gc*/ }; +#if PY_SIZE_MAX > INT_MAX +#define CHECK_STRING_LENGTH(s) do { \ + if (s != NULL && strlen(s) >= INT_MAX) { \ + PyErr_SetString(PyExc_OverflowError, "string is too long"); \ + return NULL; \ + } } while(0) +#else +#define CHECK_STRING_LENGTH(s) +#endif + static Tcl_Obj* AsObj(PyObject *value) { @@ -1486,6 +1496,7 @@ if (!PyArg_ParseTuple(args, "s:eval", &script)) return NULL; + CHECK_STRING_LENGTH(script); CHECK_TCL_APPARTMENT; ENTER_TCL @@ -1532,6 +1543,7 @@ if (!PyArg_ParseTuple(args, "s:evalfile", &fileName)) return NULL; + CHECK_STRING_LENGTH(fileName); CHECK_TCL_APPARTMENT; ENTER_TCL @@ -1553,9 +1565,10 @@ PyObject *res = NULL; int err; - if (!PyArg_ParseTuple(args, "s", &script)) + if (!PyArg_ParseTuple(args, "s:record", &script)) return NULL; + CHECK_STRING_LENGTH(script); CHECK_TCL_APPARTMENT; ENTER_TCL @@ -1576,6 +1589,7 @@ if (!PyArg_ParseTuple(args, "s:adderrorinfo", &msg)) return NULL; + CHECK_STRING_LENGTH(msg); CHECK_TCL_APPARTMENT; ENTER_TCL @@ -1743,6 +1757,8 @@ if (!PyArg_ParseTuple(args, "ssO:setvar", &name1, &name2, &newValue)) return NULL; + CHECK_STRING_LENGTH(name1); + CHECK_STRING_LENGTH(name2); /* XXX must hold tcl lock already??? */ newval = AsObj(newValue); ENTER_TCL @@ -1788,6 +1804,7 @@ varname_converter, &name1, &name2)) return NULL; + CHECK_STRING_LENGTH(name2); ENTER_TCL tres = Tcl_GetVar2Ex(Tkapp_Interp(self), name1, name2, flags); ENTER_OVERLAP @@ -1831,6 +1848,8 @@ if (!PyArg_ParseTuple(args, "s|s:unsetvar", &name1, &name2)) return NULL; + CHECK_STRING_LENGTH(name1); + CHECK_STRING_LENGTH(name2); ENTER_TCL code = Tcl_UnsetVar2(Tkapp_Interp(self), name1, name2, flags); ENTER_OVERLAP @@ -1875,6 +1894,7 @@ } if (!PyArg_ParseTuple(args, "s:getint", &s)) return NULL; + CHECK_STRING_LENGTH(s); if (Tcl_GetInt(Tkapp_Interp(self), s, &v) == TCL_ERROR) return Tkinter_Error(self); return Py_BuildValue("i", v); @@ -1895,6 +1915,7 @@ } if (!PyArg_ParseTuple(args, "s:getdouble", &s)) return NULL; + CHECK_STRING_LENGTH(s); if (Tcl_GetDouble(Tkapp_Interp(self), s, &v) == TCL_ERROR) return Tkinter_Error(self); return Py_BuildValue("d", v); @@ -1915,6 +1936,7 @@ } if (!PyArg_ParseTuple(args, "s:getboolean", &s)) return NULL; + CHECK_STRING_LENGTH(s); if (Tcl_GetBoolean(Tkapp_Interp(self), s, &v) == TCL_ERROR) return Tkinter_Error(self); return PyBool_FromLong(v); @@ -1930,6 +1952,7 @@ if (!PyArg_ParseTuple(args, "s:exprstring", &s)) return NULL; + CHECK_STRING_LENGTH(s); CHECK_TCL_APPARTMENT; ENTER_TCL @@ -1954,6 +1977,7 @@ if (!PyArg_ParseTuple(args, "s:exprlong", &s)) return NULL; + CHECK_STRING_LENGTH(s); CHECK_TCL_APPARTMENT; ENTER_TCL @@ -1977,6 +2001,7 @@ if (!PyArg_ParseTuple(args, "s:exprdouble", &s)) return NULL; + CHECK_STRING_LENGTH(s); CHECK_TCL_APPARTMENT; PyFPE_START_PROTECT("Tkapp_ExprDouble", return 0) ENTER_TCL @@ -2001,6 +2026,7 @@ if (!PyArg_ParseTuple(args, "s:exprboolean", &s)) return NULL; + CHECK_STRING_LENGTH(s); CHECK_TCL_APPARTMENT; ENTER_TCL retval = Tcl_ExprBoolean(Tkapp_Interp(self), s, &v); @@ -2053,6 +2079,7 @@ if (!PyArg_ParseTuple(args, "et:splitlist", "utf-8", &list)) return NULL; + CHECK_STRING_LENGTH(list); if (Tcl_SplitList(Tkapp_Interp(self), list, &argc, &argv) == TCL_ERROR) { PyMem_Free(list); @@ -2114,6 +2141,7 @@ if (!PyArg_ParseTuple(args, "et:split", "utf-8", &list)) return NULL; + CHECK_STRING_LENGTH(list); v = Split(list); PyMem_Free(list); return v; @@ -2259,6 +2287,7 @@ if (!PyArg_ParseTuple(args, "sO:createcommand", &cmdName, &func)) return NULL; + CHECK_STRING_LENGTH(cmdName); if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, "command not callable"); return NULL; @@ -2322,6 +2351,7 @@ if (!PyArg_ParseTuple(args, "s:deletecommand", &cmdName)) return NULL; + CHECK_STRING_LENGTH(cmdName); #ifdef WITH_THREAD if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) { @@ -3130,6 +3160,10 @@ &interactive, &wantobjects, &wantTk, &sync, &use)) return NULL; + CHECK_STRING_LENGTH(screenName); + CHECK_STRING_LENGTH(baseName); + CHECK_STRING_LENGTH(className); + CHECK_STRING_LENGTH(use); return (PyObject *) Tkapp_New(screenName, baseName, className, interactive, wantobjects, wantTk, diff --git a/Modules/binascii.c b/Modules/binascii.c --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -320,12 +320,10 @@ } *ascii_data++ = '\n'; /* Append a courtesy newline */ - if (_PyString_Resize(&rv, + /* rv is cleared on error */ + (void)_PyString_Resize(&rv, (ascii_data - - (unsigned char *)PyString_AS_STRING(rv))) < 0) { - Py_DECREF(rv); - rv = NULL; - } + (unsigned char *)PyString_AS_STRING(rv))); PyBuffer_Release(&pbin); return rv; } @@ -452,10 +450,8 @@ ** string instead; _PyString_Resize() won't do this for us. */ if (bin_len > 0) { - if (_PyString_Resize(&rv, bin_len) < 0) { - Py_DECREF(rv); - rv = NULL; - } + /* rv is cleared on error */ + (void)_PyString_Resize(&rv, bin_len); } else { Py_DECREF(rv); @@ -522,12 +518,10 @@ } *ascii_data++ = '\n'; /* Append a courtesy newline */ - if (_PyString_Resize(&rv, + /* rv is cleared on error */ + (void)_PyString_Resize(&rv, (ascii_data - - (unsigned char *)PyString_AS_STRING(rv))) < 0) { - Py_DECREF(rv); - rv = NULL; - } + (unsigned char *)PyString_AS_STRING(rv))); PyBuffer_Release(&pbuf); return rv; } @@ -601,13 +595,10 @@ Py_DECREF(rv); return NULL; } + /* rv is cleared on error */ if (_PyString_Resize(&rv, (bin_data - - (unsigned char *)PyString_AS_STRING(rv))) < 0) { - Py_DECREF(rv); - rv = NULL; - } - if (rv) { + (unsigned char *)PyString_AS_STRING(rv))) == 0) { PyObject *rrv = Py_BuildValue("Oi", rv, done); PyBuffer_Release(&pascii); Py_DECREF(rv); @@ -672,12 +663,10 @@ } } } - if (_PyString_Resize(&rv, + /* rv is cleared on error */ + (void)_PyString_Resize(&rv, (out_data - - (unsigned char *)PyString_AS_STRING(rv))) < 0) { - Py_DECREF(rv); - rv = NULL; - } + (unsigned char *)PyString_AS_STRING(rv))); PyBuffer_Release(&pbuf); return rv; } @@ -729,12 +718,10 @@ leftchar <<= (6-leftbits); *ascii_data++ = table_b2a_hqx[leftchar & 0x3f]; } - if (_PyString_Resize(&rv, + /* rv is cleared on error */ + (void)_PyString_Resize(&rv, (ascii_data - - (unsigned char *)PyString_AS_STRING(rv))) < 0) { - Py_DECREF(rv); - rv = NULL; - } + (unsigned char *)PyString_AS_STRING(rv))); PyBuffer_Release(&pbin); return rv; } @@ -796,7 +783,7 @@ if ( --out_len_left < 0 ) { \ if ( out_len > PY_SSIZE_T_MAX / 2) return PyErr_NoMemory(); \ if (_PyString_Resize(&rv, 2*out_len) < 0) \ - { Py_DECREF(rv); PyBuffer_Release(&pin); return NULL; } \ + { PyBuffer_Release(&pin); return NULL; } \ out_data = (unsigned char *)PyString_AS_STRING(rv) \ + out_len; \ out_len_left = out_len-1; \ @@ -846,12 +833,10 @@ OUTBYTE(in_byte); } } - if (_PyString_Resize(&rv, + /* rv is cleared on error */ + (void)_PyString_Resize(&rv, (out_data - - (unsigned char *)PyString_AS_STRING(rv))) < 0) { - Py_DECREF(rv); - rv = NULL; - } + (unsigned char *)PyString_AS_STRING(rv))); PyBuffer_Release(&pin); return rv; } diff --git a/Modules/bz2module.c b/Modules/bz2module.c --- a/Modules/bz2module.c +++ b/Modules/bz2module.c @@ -732,7 +732,8 @@ } else { /* Grow the big buffer */ - _PyString_Resize(&big_buffer, buffersize); + if (_PyString_Resize(&big_buffer, buffersize)) + goto error; buffer = PyString_AS_STRING(big_buffer); } continue; diff --git a/Modules/cPickle.c b/Modules/cPickle.c --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -324,8 +324,7 @@ #define FREE_ARG_TUP(self) { \ if (Py_REFCNT(self->arg) > 1) { \ - Py_DECREF(self->arg); \ - self->arg=NULL; \ + Py_CLEAR(self->arg); \ } \ } diff --git a/Modules/cdmodule.c b/Modules/cdmodule.c --- a/Modules/cdmodule.c +++ b/Modules/cdmodule.c @@ -535,10 +535,8 @@ /* no sense in keeping the callbacks, so remove them */ for (i = 0; i < NCALLBACKS; i++) { - Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallback); - self->ob_cdcallbacks[i].ob_cdcallback = NULL; - Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg); - self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL; + Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallback); + Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallbackarg); } Py_INCREF(Py_None); @@ -588,11 +586,9 @@ CDremovecallback(self->ob_cdparser, (CDDATATYPES) type); - Py_XDECREF(self->ob_cdcallbacks[type].ob_cdcallback); - self->ob_cdcallbacks[type].ob_cdcallback = NULL; + Py_CLEAR(self->ob_cdcallbacks[type].ob_cdcallback); - Py_XDECREF(self->ob_cdcallbacks[type].ob_cdcallbackarg); - self->ob_cdcallbacks[type].ob_cdcallbackarg = NULL; + Py_CLEAR(self->ob_cdcallbacks[type].ob_cdcallbackarg); Py_INCREF(Py_None); return Py_None; @@ -668,10 +664,8 @@ int i; for (i = 0; i < NCALLBACKS; i++) { - Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallback); - self->ob_cdcallbacks[i].ob_cdcallback = NULL; - Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg); - self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL; + Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallback); + Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallbackarg); } CDdeleteparser(self->ob_cdparser); PyObject_Del(self); diff --git a/Modules/flmodule.c b/Modules/flmodule.c --- a/Modules/flmodule.c +++ b/Modules/flmodule.c @@ -97,10 +97,8 @@ { int i, n; - Py_XDECREF(g->ob_callback); - g->ob_callback = NULL; - Py_XDECREF(g->ob_callback_arg); - g->ob_callback_arg = NULL; + Py_CLEAR(g->ob_callback); + Py_CLEAR(g->ob_callback_arg); if (allgenerics == NULL) return; /* No objects known yet */ n = PyList_Size(allgenerics); @@ -132,10 +130,8 @@ /* The object is now unreachable for do_forms and check_forms, so delete it from the list of known objects */ - Py_XDECREF(g->ob_callback); - g->ob_callback = NULL; - Py_XDECREF(g->ob_callback_arg); - g->ob_callback_arg = NULL; + Py_CLEAR(g->ob_callback); + Py_CLEAR(g->ob_callback_arg); PyList_SetItem(allgenerics, i, (PyObject *)NULL); nfreeslots++; } diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -1241,19 +1241,22 @@ Py_ssize_t oldnext; PyObject *(*iternext)(PyObject *); + if (it == NULL) + return NULL; + iternext = *Py_TYPE(it)->tp_iternext; while (lz->cnt < lz->next) { item = iternext(it); if (item == NULL) - return NULL; + goto empty; Py_DECREF(item); lz->cnt++; } if (stop != -1 && lz->cnt >= stop) - return NULL; + goto empty; item = iternext(it); if (item == NULL) - return NULL; + goto empty; lz->cnt++; oldnext = lz->next; /* The (size_t) cast below avoids the danger of undefined @@ -1262,6 +1265,10 @@ if (lz->next < oldnext || (stop != -1 && lz->next > stop)) lz->next = stop; return item; + +empty: + Py_CLEAR(lz->it); + return NULL; } PyDoc_STRVAR(islice_doc, diff --git a/Modules/operator.c b/Modules/operator.c --- a/Modules/operator.c +++ b/Modules/operator.c @@ -235,6 +235,132 @@ #define spam2o(OP,ALTOP,DOC) {#OP, op_##OP, METH_O, PyDoc_STR(DOC)}, \ {#ALTOP, op_##OP, METH_O, PyDoc_STR(DOC)}, + + +/* compare_digest **********************************************************/ + +/* + * timing safe compare + * + * Returns 1 of the strings are equal. + * In case of len(a) != len(b) the function tries to keep the timing + * dependent on the length of b. CPU cache locally may still alter timing + * a bit. + */ +static int +_tscmp(const unsigned char *a, const unsigned char *b, + Py_ssize_t len_a, Py_ssize_t len_b) +{ + /* The volatile type declarations make sure that the compiler has no + * chance to optimize and fold the code in any way that may change + * the timing. + */ + volatile Py_ssize_t length; + volatile const unsigned char *left; + volatile const unsigned char *right; + Py_ssize_t i; + unsigned char result; + + /* loop count depends on length of b */ + length = len_b; + left = NULL; + right = b; + + /* don't use else here to keep the amount of CPU instructions constant, + * volatile forces re-evaluation + * */ + if (len_a == length) { + left = *((volatile const unsigned char**)&a); + result = 0; + } + if (len_a != length) { + left = b; + result = 1; + } + + for (i=0; i < length; i++) { + result |= *left++ ^ *right++; + } + + return (result == 0); +} + +PyDoc_STRVAR(compare_digest__doc__, +"compare_digest(a, b) -> bool\n" +"\n" +"Return 'a == b'. This function uses an approach designed to prevent\n" +"timing analysis, making it appropriate for cryptography.\n" +"a and b must both be of the same type: either str (ASCII only),\n" +"or any type that supports the buffer protocol (e.g. bytes).\n" +"\n" +"Note: If a and b are of different lengths, or if an error occurs,\n" +"a timing attack could theoretically reveal information about the\n" +"types and lengths of a and b--but not their values.\n"); + +static PyObject* +compare_digest(PyObject *self, PyObject *args) +{ + PyObject *a, *b; + int rc; + + if (!PyArg_ParseTuple(args, "OO:compare_digest", &a, &b)) { + return NULL; + } + + /* Unicode string */ + if (PyUnicode_Check(a) && PyUnicode_Check(b)) { + rc = _tscmp((const unsigned char *)PyUnicode_AS_DATA(a), + (const unsigned char *)PyUnicode_AS_DATA(b), + PyUnicode_GET_DATA_SIZE(a), + PyUnicode_GET_DATA_SIZE(b)); + } + /* fallback to buffer interface for bytes, bytesarray and other */ + else { + Py_buffer view_a; + Py_buffer view_b; + + if (PyObject_CheckBuffer(a) == 0 && PyObject_CheckBuffer(b) == 0) { + PyErr_Format(PyExc_TypeError, + "unsupported operand types(s) or combination of types: " + "'%.100s' and '%.100s'", + Py_TYPE(a)->tp_name, Py_TYPE(b)->tp_name); + return NULL; + } + + if (PyObject_GetBuffer(a, &view_a, PyBUF_SIMPLE) == -1) { + return NULL; + } + if (view_a.ndim > 1) { + PyErr_SetString(PyExc_BufferError, + "Buffer must be single dimension"); + PyBuffer_Release(&view_a); + return NULL; + } + + if (PyObject_GetBuffer(b, &view_b, PyBUF_SIMPLE) == -1) { + PyBuffer_Release(&view_a); + return NULL; + } + if (view_b.ndim > 1) { + PyErr_SetString(PyExc_BufferError, + "Buffer must be single dimension"); + PyBuffer_Release(&view_a); + PyBuffer_Release(&view_b); + return NULL; + } + + rc = _tscmp((const unsigned char*)view_a.buf, + (const unsigned char*)view_b.buf, + view_a.len, + view_b.len); + + PyBuffer_Release(&view_a); + PyBuffer_Release(&view_b); + } + + return PyBool_FromLong(rc); +} + static struct PyMethodDef operator_methods[] = { spam1o(isCallable, @@ -318,6 +444,8 @@ spam2(gt,__gt__, "gt(a, b) -- Same as a>b.") spam2(ge,__ge__, "ge(a, b) -- Same as a>=b.") + {"_compare_digest", (PyCFunction)compare_digest, METH_VARARGS, + compare_digest__doc__}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6581,8 +6581,12 @@ "close(fd)\n\n\ Close a file descriptor (for low level IO)."); -static PyObject * -posix_close(PyObject *self, PyObject *args) +/* +The underscore at end of function name avoids a name clash with the libc +function posix_close. +*/ +static PyObject * +posix_close_(PyObject *self, PyObject *args) { int fd, res; if (!PyArg_ParseTuple(args, "i:close", &fd)) @@ -6663,7 +6667,8 @@ PyDoc_STRVAR(posix_lseek__doc__, "lseek(fd, pos, how) -> newpos\n\n\ -Set the current position of a file descriptor."); +Set the current position of a file descriptor.\n\ +Return the new cursor position in bytes, starting from the beginning."); static PyObject * posix_lseek(PyObject *self, PyObject *args) @@ -6844,8 +6849,35 @@ PyMem_FREE(mode); return NULL; } - if (!_PyVerify_fd(fd)) + if (!_PyVerify_fd(fd)) { + PyMem_FREE(mode); return posix_error(); + } +#if defined(HAVE_FSTAT) && defined(S_IFDIR) && defined(EISDIR) + { + struct stat buf; + const char *msg; + PyObject *exc; + if (fstat(fd, &buf) == 0 && S_ISDIR(buf.st_mode)) { + PyMem_FREE(mode); + msg = strerror(EISDIR); + exc = PyObject_CallFunction(PyExc_IOError, "(isO)", + EISDIR, msg, ""); + if (exc) { + PyErr_SetObject(PyExc_IOError, exc); + Py_DECREF(exc); + } + return NULL; + } + } +#endif + /* The dummy filename used here must be kept in sync with the value + tested against in gzip.GzipFile.__init__() - see issue #13781. */ + f = PyFile_FromFile(NULL, "", orgmode, fclose); + if (f == NULL) { + PyMem_FREE(mode); + return NULL; + } Py_BEGIN_ALLOW_THREADS #if !defined(MS_WINDOWS) && defined(HAVE_FCNTL_H) if (mode[0] == 'a') { @@ -6868,11 +6900,9 @@ PyMem_FREE(mode); if (fp == NULL) return posix_error(); - /* The dummy filename used here must be kept in sync with the value - tested against in gzip.GzipFile.__init__() - see issue #13781. */ - f = PyFile_FromFile(fp, "", orgmode, fclose); - if (f != NULL) - PyFile_SetBufSize(f, bufsize); + /* We now know we will succeed, so initialize the file object. */ + ((PyFileObject *)f)->f_fp = fp; + PyFile_SetBufSize(f, bufsize); return f; } @@ -8960,7 +8990,7 @@ {"tcsetpgrp", posix_tcsetpgrp, METH_VARARGS, posix_tcsetpgrp__doc__}, #endif /* HAVE_TCSETPGRP */ {"open", posix_open, METH_VARARGS, posix_open__doc__}, - {"close", posix_close, METH_VARARGS, posix_close__doc__}, + {"close", posix_close_, METH_VARARGS, posix_close__doc__}, {"closerange", posix_closerange, METH_VARARGS, posix_closerange__doc__}, {"dup", posix_dup, METH_VARARGS, posix_dup__doc__}, {"dup2", posix_dup2, METH_VARARGS, posix_dup2__doc__}, diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -976,7 +976,7 @@ void *buf = XML_GetBuffer(self->itself, BUF_SIZE); if (buf == NULL) { Py_XDECREF(readmethod); - return PyErr_NoMemory(); + return get_parse_result(self, 0); } bytes_read = readinst(buf, BUF_SIZE, readmethod); diff --git a/Modules/readline.c b/Modules/readline.c --- a/Modules/readline.c +++ b/Modules/readline.c @@ -206,8 +206,7 @@ if (!PyArg_ParseTuple(args, buf, &function)) return NULL; if (function == Py_None) { - Py_XDECREF(*hook_var); - *hook_var = NULL; + Py_CLEAR(*hook_var); } else if (PyCallable_Check(function)) { PyObject *tmp = *hook_var; diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -71,8 +71,7 @@ { int i; for (i = 0; i < FD_SETSIZE + 1 && fd2obj[i].sentinel >= 0; i++) { - Py_XDECREF(fd2obj[i].obj); - fd2obj[i].obj = NULL; + Py_CLEAR(fd2obj[i].obj); } fd2obj[0].sentinel = -1; } diff --git a/Modules/stropmodule.c b/Modules/stropmodule.c --- a/Modules/stropmodule.c +++ b/Modules/stropmodule.c @@ -593,7 +593,7 @@ char* e; char* p; char* q; - Py_ssize_t i, j, old_j; + Py_ssize_t i, j; PyObject* out; char* string; Py_ssize_t stringlen; @@ -610,30 +610,29 @@ } /* First pass: determine size of output string */ - i = j = old_j = 0; /* j: current column; i: total of previous lines */ + i = j = 0; /* j: current column; i: total of previous lines */ e = string + stringlen; for (p = string; p < e; p++) { if (*p == '\t') { - j += tabsize - (j%tabsize); - if (old_j > j) { - PyErr_SetString(PyExc_OverflowError, - "new string is too long"); - return NULL; - } - old_j = j; + Py_ssize_t incr = tabsize - (j%tabsize); + if (j > PY_SSIZE_T_MAX - incr) + goto overflow; + j += incr; } else { + if (j > PY_SSIZE_T_MAX - 1) + goto overflow; j++; if (*p == '\n') { + if (i > PY_SSIZE_T_MAX - j) + goto overflow; i += j; j = 0; } } } - if ((i + j) < 0) { - PyErr_SetString(PyExc_OverflowError, "new string is too long"); - return NULL; - } + if (i > PY_SSIZE_T_MAX - j) + goto overflow; /* Second pass: create output string and fill it */ out = PyString_FromStringAndSize(NULL, i+j); @@ -658,6 +657,9 @@ } return out; + overflow: + PyErr_SetString(PyExc_OverflowError, "result is too long"); + return NULL; } diff --git a/Modules/svmodule.c b/Modules/svmodule.c --- a/Modules/svmodule.c +++ b/Modules/svmodule.c @@ -279,8 +279,7 @@ (void)svUnlockCaptureData(self->ob_svideo->ob_svideo, self->ob_capture); self->ob_capture = NULL; - Py_DECREF(self->ob_svideo); - self->ob_svideo = NULL; + Py_CLEAR(self->ob_svideo); } PyObject_Del(self); } diff --git a/Modules/zipimport.c b/Modules/zipimport.c --- a/Modules/zipimport.c +++ b/Modules/zipimport.c @@ -42,16 +42,10 @@ static PyObject *ZipImportError; static PyObject *zip_directory_cache = NULL; -static PyObject *zip_stat_cache = NULL; -/* posix.fstat or nt.fstat function. Used due to posixmodule.c's - * superior fstat implementation over libc's on Windows. */ -static PyObject *fstat_function = NULL; /* posix.fstat() or nt.fstat() */ /* forward decls */ -static FILE *fopen_rb_and_stat(char *path, PyObject **py_stat_p); -static FILE *safely_reopen_archive(ZipImporter *self, char **archive_p); -static PyObject *read_directory(FILE *fp, char *archive); -static PyObject *get_data(FILE *fp, char *archive, PyObject *toc_entry); +static PyObject *read_directory(char *archive); +static PyObject *get_data(char *archive, PyObject *toc_entry); static PyObject *get_module_code(ZipImporter *self, char *fullname, int *p_ispackage, char **p_modpath); @@ -66,34 +60,30 @@ static int zipimporter_init(ZipImporter *self, PyObject *args, PyObject *kwds) { - char *path_arg, *path, *p, *prefix, *path_buf; + char *path, *p, *prefix, buf[MAXPATHLEN+2]; size_t len; if (!_PyArg_NoKeywords("zipimporter()", kwds)) return -1; - if (!PyArg_ParseTuple(args, "s:zipimporter", &path_arg)) + if (!PyArg_ParseTuple(args, "s:zipimporter", + &path)) return -1; - len = strlen(path_arg); + len = strlen(path); if (len == 0) { PyErr_SetString(ZipImportError, "archive path is empty"); return -1; } if (len >= MAXPATHLEN) { - PyErr_SetString(ZipImportError, "archive path too long"); + PyErr_SetString(ZipImportError, + "archive path too long"); return -1; } - /* Room for the trailing \0 and room for an extra SEP if needed. */ - path_buf = (char *)PyMem_Malloc(len + 2); - if (path_buf == NULL) { - PyErr_SetString(PyExc_MemoryError, "unable to malloc path buffer"); - return -1; - } - strcpy(path_buf, path_arg); + strcpy(buf, path); #ifdef ALTSEP - for (p = path_buf; *p; p++) { + for (p = buf; *p; p++) { if (*p == ALTSEP) *p = SEP; } @@ -106,25 +96,25 @@ struct stat statbuf; int rv; - rv = stat(path_buf, &statbuf); + rv = stat(buf, &statbuf); if (rv == 0) { /* it exists */ if (S_ISREG(statbuf.st_mode)) /* it's a file */ - path = path_buf; + path = buf; break; } #else - if (object_exists(path_buf)) { + if (object_exists(buf)) { /* it exists */ - if (isfile(path_buf)) + if (isfile(buf)) /* it's a file */ - path = path_buf; + path = buf; break; } #endif /* back up one path element */ - p = strrchr(path_buf, SEP); + p = strrchr(buf, SEP); if (prefix != NULL) *prefix = SEP; if (p == NULL) @@ -136,43 +126,20 @@ PyObject *files; files = PyDict_GetItemString(zip_directory_cache, path); if (files == NULL) { - PyObject *zip_stat = NULL; - FILE *fp = fopen_rb_and_stat(path, &zip_stat); - if (fp == NULL) { - PyErr_Format(ZipImportError, "can't open Zip file: " - "'%.200s'", path); - Py_XDECREF(zip_stat); - goto error; - } - - if (Py_VerboseFlag) - PySys_WriteStderr("# zipimport: %s not cached, " - "reading TOC.\n", path); - - files = read_directory(fp, path); - fclose(fp); - if (files == NULL) { - Py_XDECREF(zip_stat); - goto error; - } + files = read_directory(buf); + if (files == NULL) + return -1; if (PyDict_SetItemString(zip_directory_cache, path, - files) != 0) { - Py_DECREF(files); - Py_XDECREF(zip_stat); - goto error; - } - if (zip_stat && PyDict_SetItemString(zip_stat_cache, path, - zip_stat) != 0) { - Py_DECREF(files); - Py_DECREF(zip_stat); - goto error; - } - Py_XDECREF(zip_stat); + files) != 0) + return -1; } + else + Py_INCREF(files); + self->files = files; } else { PyErr_SetString(ZipImportError, "not a Zip file"); - goto error; + return -1; } if (prefix == NULL) @@ -187,26 +154,33 @@ } } - self->archive = PyString_FromString(path); + self->archive = PyString_FromString(buf); if (self->archive == NULL) - goto error; + return -1; self->prefix = PyString_FromString(prefix); if (self->prefix == NULL) - goto error; + return -1; - PyMem_Free(path_buf); return 0; -error: - PyMem_Free(path_buf); - return -1; +} + +/* GC support. */ +static int +zipimporter_traverse(PyObject *obj, visitproc visit, void *arg) +{ + ZipImporter *self = (ZipImporter *)obj; + Py_VISIT(self->files); + return 0; } static void zipimporter_dealloc(ZipImporter *self) { + PyObject_GC_UnTrack(self); Py_XDECREF(self->archive); Py_XDECREF(self->prefix); + Py_XDECREF(self->files); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -286,7 +260,6 @@ char *subname, path[MAXPATHLEN + 1]; int len; struct st_zip_searchorder *zso; - PyObject *files; subname = get_subname(fullname); @@ -294,23 +267,9 @@ if (len < 0) return MI_ERROR; - files = PyDict_GetItem(zip_directory_cache, self->archive); - if (files == NULL) { - /* Some scoundrel has cleared zip_directory_cache out from - * beneath us. Try repopulating it once before giving up. */ - char *unused_archive_name; - FILE *fp = safely_reopen_archive(self, &unused_archive_name); - if (fp == NULL) - return MI_ERROR; - fclose(fp); - files = PyDict_GetItem(zip_directory_cache, self->archive); - if (files == NULL) - return MI_ERROR; - } - for (zso = zip_searchorder; *zso->suffix; zso++) { strcpy(path + len, zso->suffix); - if (PyDict_GetItemString(files, path) != NULL) { + if (PyDict_GetItemString(self->files, path) != NULL) { if (zso->type & IS_PACKAGE) return MI_PACKAGE; else @@ -460,12 +419,11 @@ zipimporter_get_data(PyObject *obj, PyObject *args) { ZipImporter *self = (ZipImporter *)obj; - char *path, *archive; - FILE *fp; + char *path; #ifdef ALTSEP char *p, buf[MAXPATHLEN + 1]; #endif - PyObject *toc_entry, *data, *files; + PyObject *toc_entry; Py_ssize_t len; if (!PyArg_ParseTuple(args, "s:zipimporter.get_data", &path)) @@ -490,25 +448,12 @@ path = path + len + 1; } - fp = safely_reopen_archive(self, &archive); - if (fp == NULL) - return NULL; - - files = PyDict_GetItem(zip_directory_cache, self->archive); - if (files == NULL) { - /* This should never happen as safely_reopen_archive() should - * have repopulated zip_directory_cache if needed. */ + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry == NULL) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, path); return NULL; } - toc_entry = PyDict_GetItemString(files, path); - if (toc_entry == NULL) { - PyErr_SetFromErrnoWithFilename(PyExc_IOError, path); - fclose(fp); - return NULL; - } - data = get_data(fp, archive, toc_entry); - fclose(fp); - return data; + return get_data(PyString_AsString(self->archive), toc_entry); } static PyObject * @@ -527,9 +472,8 @@ zipimporter_get_source(PyObject *obj, PyObject *args) { ZipImporter *self = (ZipImporter *)obj; - PyObject *toc_entry, *files; - FILE *fp; - char *fullname, *subname, path[MAXPATHLEN+1], *archive; + PyObject *toc_entry; + char *fullname, *subname, path[MAXPATHLEN+1]; int len; enum zi_module_info mi; @@ -557,26 +501,13 @@ else strcpy(path + len, ".py"); - fp = safely_reopen_archive(self, &archive); - if (fp == NULL) - return NULL; - - files = PyDict_GetItem(zip_directory_cache, self->archive); - if (files == NULL) { - /* This should never happen as safely_reopen_archive() should - * have repopulated zip_directory_cache if needed. */ - return NULL; - } - toc_entry = PyDict_GetItemString(files, path); - if (toc_entry != NULL) { - PyObject *data = get_data(fp, archive, toc_entry); - fclose(fp); - return data; - } - fclose(fp); + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry != NULL) + return get_data(PyString_AsString(self->archive), toc_entry); /* we have the module, but no source */ - Py_RETURN_NONE; + Py_INCREF(Py_None); + return Py_None; } PyDoc_STRVAR(doc_find_module, @@ -687,9 +618,10 @@ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ zipimporter_doc, /* tp_doc */ - 0, /* tp_traverse */ + zipimporter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -706,7 +638,7 @@ (initproc)zipimporter_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ + PyObject_GC_Del, /* tp_free */ }; @@ -730,139 +662,7 @@ } /* - fopen_rb_and_stat(path, &py_stat) -> FILE * - - Opens path in "rb" mode and populates the Python py_stat stat_result - with information about the opened file. *py_stat may not be changed - if there is no fstat_function or if fstat_function fails. - - Returns NULL and does nothing to *py_stat if the open failed. -*/ -static FILE * -fopen_rb_and_stat(char *path, PyObject **py_stat_p) -{ - FILE *fp; - assert(py_stat_p != NULL); - assert(*py_stat_p == NULL); - - fp = fopen(path, "rb"); - if (fp == NULL) { - return NULL; - } - - if (fstat_function) { - PyObject *stat_result = PyObject_CallFunction(fstat_function, - "i", fileno(fp)); - if (stat_result == NULL) { - PyErr_Clear(); /* We can function without it. */ - } else { - *py_stat_p = stat_result; - } - } - - return fp; -} - -/* Return 1 if objects a and b fail a Py_EQ test for an attr. */ -static int -compare_obj_attr_strings(PyObject *obj_a, PyObject *obj_b, char *attr_name) -{ - int problem = 0; - PyObject *attr_a = PyObject_GetAttrString(obj_a, attr_name); - PyObject *attr_b = PyObject_GetAttrString(obj_b, attr_name); - if (attr_a == NULL || attr_b == NULL) - problem = 1; - else - problem = (PyObject_RichCompareBool(attr_a, attr_b, Py_EQ) != 1); - Py_XDECREF(attr_a); - Py_XDECREF(attr_b); - return problem; -} - -/* - * Returns an open FILE * on success and sets *archive_p to point to - * a read only C string representation of the archive name (as a - * convenience for use in error messages). - * - * Returns NULL on error with the Python error context set. - */ -static FILE * -safely_reopen_archive(ZipImporter *self, char **archive_p) -{ - FILE *fp; - PyObject *stat_now = NULL; - char *archive; - - assert(archive_p != NULL); - *archive_p = PyString_AsString(self->archive); - if (*archive_p == NULL) - return NULL; - archive = *archive_p; - - fp = fopen_rb_and_stat(archive, &stat_now); - if (!fp) { - PyErr_Format(PyExc_IOError, - "zipimport: can not open file %s", archive); - Py_XDECREF(stat_now); - return NULL; - } - - if (stat_now != NULL) { - int problem = 0; - PyObject *files; - PyObject *prev_stat = PyDict_GetItemString(zip_stat_cache, archive); - /* Test stat_now vs the old cached stat on some key attributes. */ - if (prev_stat != NULL) { - problem = compare_obj_attr_strings(prev_stat, stat_now, - "st_ino"); - problem |= compare_obj_attr_strings(prev_stat, stat_now, - "st_size"); - problem |= compare_obj_attr_strings(prev_stat, stat_now, - "st_mtime"); - } else { - if (Py_VerboseFlag) - PySys_WriteStderr("# zipimport: no stat data for %s!\n", - archive); - problem = 1; - } - - if (problem) { - if (Py_VerboseFlag) - PySys_WriteStderr("# zipimport: %s modified since last" - " import, rereading TOC.\n", archive); - files = read_directory(fp, archive); - if (files == NULL) { - Py_DECREF(stat_now); - fclose(fp); - return NULL; - } - if (PyDict_SetItem(zip_directory_cache, self->archive, - files) != 0) { - Py_DECREF(files); - Py_DECREF(stat_now); - fclose(fp); - return NULL; - } - if (stat_now && PyDict_SetItem(zip_stat_cache, self->archive, - stat_now) != 0) { - Py_DECREF(files); - Py_DECREF(stat_now); - fclose(fp); - return NULL; - } - } - Py_DECREF(stat_now); - } else { - if (Py_VerboseFlag) - PySys_WriteStderr("# zipimport: os.fstat failed on the " - "open %s file.\n", archive); - } - - return fp; -} - -/* - read_directory(fp, archive) -> files dict (new reference) + read_directory(archive) -> files dict (new reference) Given a path to a Zip archive, build a dict, mapping file names (local to the archive, using SEP as a separator) to toc entries. @@ -883,9 +683,10 @@ data_size and file_offset are 0. */ static PyObject * -read_directory(FILE *fp, char *archive) +read_directory(char *archive) { PyObject *files = NULL; + FILE *fp; long compress, crc, data_size, file_size, file_offset, date, time; long header_offset, name_size, header_size, header_position; long i, l, count; @@ -895,7 +696,6 @@ char *p, endof_central_dir[22]; long arc_offset; /* offset from beginning of file to start of zip-archive */ - assert(fp != NULL); if (strlen(archive) > MAXPATHLEN) { PyErr_SetString(PyExc_OverflowError, "Zip path name is too long"); @@ -903,18 +703,28 @@ } strcpy(path, archive); + fp = fopen(archive, "rb"); + if (fp == NULL) { + PyErr_Format(ZipImportError, "can't open Zip file: " + "'%.200s'", archive); + return NULL; + } + if (fseek(fp, -22, SEEK_END) == -1) { + fclose(fp); PyErr_Format(ZipImportError, "can't read Zip file: %s", archive); return NULL; } header_position = ftell(fp); if (fread(endof_central_dir, 1, 22, fp) != 22) { + fclose(fp); PyErr_Format(ZipImportError, "can't read Zip file: " "'%.200s'", archive); return NULL; } if (get_long((unsigned char *)endof_central_dir) != 0x06054B50) { /* Bad: End of Central Dir signature */ + fclose(fp); PyErr_Format(ZipImportError, "not a Zip file: " "'%.200s'", archive); return NULL; @@ -983,15 +793,18 @@ goto error; count++; } + fclose(fp); if (Py_VerboseFlag) PySys_WriteStderr("# zipimport: found %ld names in %s\n", count, archive); return files; fseek_error: + fclose(fp); Py_XDECREF(files); PyErr_Format(ZipImportError, "can't read Zip file: %s", archive); return NULL; error: + fclose(fp); Py_XDECREF(files); return NULL; } @@ -1028,13 +841,14 @@ return decompress; } -/* Given a FILE* to a Zip file and a toc_entry, return the (uncompressed) +/* Given a path to a Zip file and a toc_entry, return the (uncompressed) data as a new reference. */ static PyObject * -get_data(FILE *fp, char *archive, PyObject *toc_entry) +get_data(char *archive, PyObject *toc_entry) { PyObject *raw_data, *data = NULL, *decompress; char *buf; + FILE *fp; int err; Py_ssize_t bytes_read = 0; long l; @@ -1048,8 +862,16 @@ return NULL; } + fp = fopen(archive, "rb"); + if (!fp) { + PyErr_Format(PyExc_IOError, + "zipimport: can not open file %s", archive); + return NULL; + } + /* Check to make sure the local file header is correct */ if (fseek(fp, file_offset, 0) == -1) { + fclose(fp); PyErr_Format(ZipImportError, "can't read Zip file: %s", archive); return NULL; } @@ -1060,9 +882,11 @@ PyErr_Format(ZipImportError, "bad local file header in %s", archive); + fclose(fp); return NULL; } if (fseek(fp, file_offset + 26, 0) == -1) { + fclose(fp); PyErr_Format(ZipImportError, "can't read Zip file: %s", archive); return NULL; } @@ -1074,6 +898,7 @@ raw_data = PyString_FromStringAndSize((char *)NULL, compress == 0 ? data_size : data_size + 1); if (raw_data == NULL) { + fclose(fp); return NULL; } buf = PyString_AsString(raw_data); @@ -1082,9 +907,11 @@ if (err == 0) { bytes_read = fread(buf, 1, data_size, fp); } else { + fclose(fp); PyErr_Format(ZipImportError, "can't read Zip file: %s", archive); return NULL; } + fclose(fp); if (err || bytes_read != data_size) { PyErr_SetString(PyExc_IOError, "zipimport: can't read data"); @@ -1258,18 +1085,12 @@ static time_t get_mtime_of_source(ZipImporter *self, char *path) { - PyObject *toc_entry, *files; + PyObject *toc_entry; time_t mtime = 0; Py_ssize_t lastchar = strlen(path) - 1; char savechar = path[lastchar]; path[lastchar] = '\0'; /* strip 'c' or 'o' from *.py[co] */ - files = PyDict_GetItem(zip_directory_cache, self->archive); - if (files == NULL) { - /* This should never happen as safely_reopen_archive() from - * our only caller repopulated zip_directory_cache if needed. */ - return 0; - } - toc_entry = PyDict_GetItemString(files, path); + toc_entry = PyDict_GetItemString(self->files, path); if (toc_entry != NULL && PyTuple_Check(toc_entry) && PyTuple_Size(toc_entry) == 8) { /* fetch the time stamp of the .py file for comparison @@ -1286,13 +1107,17 @@ /* Return the code object for the module named by 'fullname' from the Zip archive as a new reference. */ static PyObject * -get_code_from_data(char *archive, FILE *fp, int ispackage, - int isbytecode, time_t mtime, PyObject *toc_entry) +get_code_from_data(ZipImporter *self, int ispackage, int isbytecode, + time_t mtime, PyObject *toc_entry) { PyObject *data, *code; char *modpath; + char *archive = PyString_AsString(self->archive); - data = get_data(fp, archive, toc_entry); + if (archive == NULL) + return NULL; + + data = get_data(archive, toc_entry); if (data == NULL) return NULL; @@ -1318,8 +1143,6 @@ char *subname, path[MAXPATHLEN + 1]; int len; struct st_zip_searchorder *zso; - FILE *fp; - char *archive; subname = get_subname(fullname); @@ -1327,27 +1150,15 @@ if (len < 0) return NULL; - fp = safely_reopen_archive(self, &archive); - if (fp == NULL) - return NULL; - for (zso = zip_searchorder; *zso->suffix; zso++) { - PyObject *code = NULL, *files; + PyObject *code = NULL; strcpy(path + len, zso->suffix); if (Py_VerboseFlag > 1) PySys_WriteStderr("# trying %s%c%s\n", PyString_AsString(self->archive), SEP, path); - - files = PyDict_GetItem(zip_directory_cache, self->archive); - if (files == NULL) { - /* This should never happen as safely_reopen_archive() should - * have repopulated zip_directory_cache if needed; and the GIL - * is being held. */ - return NULL; - } - toc_entry = PyDict_GetItemString(files, path); + toc_entry = PyDict_GetItemString(self->files, path); if (toc_entry != NULL) { time_t mtime = 0; int ispackage = zso->type & IS_PACKAGE; @@ -1357,7 +1168,7 @@ mtime = get_mtime_of_source(self, path); if (p_ispackage != NULL) *p_ispackage = ispackage; - code = get_code_from_data(archive, fp, ispackage, + code = get_code_from_data(self, ispackage, isbytecode, mtime, toc_entry); if (code == Py_None) { @@ -1369,12 +1180,10 @@ if (code != NULL && p_modpath != NULL) *p_modpath = PyString_AsString( PyTuple_GetItem(toc_entry, 0)); - fclose(fp); return code; } } PyErr_Format(ZipImportError, "can't find module '%.200s'", fullname); - fclose(fp); return NULL; } @@ -1390,8 +1199,6 @@ subclass of ImportError, so it can be caught as ImportError, too.\n\ - _zip_directory_cache: a dict, mapping archive paths to zip directory\n\ info dicts, as used in zipimporter._files.\n\ -- _zip_stat_cache: a dict, mapping archive paths to stat_result\n\ - info for the .zip the last time anything was imported from it.\n\ \n\ It is usually not needed to use the zipimport module explicitly; it is\n\ used by the builtin import mechanism for sys.path items that are paths\n\ @@ -1440,7 +1247,6 @@ (PyObject *)&ZipImporter_Type) < 0) return; - Py_XDECREF(zip_directory_cache); /* Avoid embedded interpreter leaks. */ zip_directory_cache = PyDict_New(); if (zip_directory_cache == NULL) return; @@ -1448,34 +1254,4 @@ if (PyModule_AddObject(mod, "_zip_directory_cache", zip_directory_cache) < 0) return; - - Py_XDECREF(zip_stat_cache); /* Avoid embedded interpreter leaks. */ - zip_stat_cache = PyDict_New(); - if (zip_stat_cache == NULL) - return; - Py_INCREF(zip_stat_cache); - if (PyModule_AddObject(mod, "_zip_stat_cache", zip_stat_cache) < 0) - return; - - { - /* We cannot import "os" here as that is a .py/.pyc file that could - * live within a zipped up standard library. Import the posix or nt - * builtin that provides the fstat() function we want instead. */ - PyObject *os_like_module; - Py_CLEAR(fstat_function); /* Avoid embedded interpreter leaks. */ - os_like_module = PyImport_ImportModule("posix"); - if (os_like_module == NULL) { - PyErr_Clear(); - os_like_module = PyImport_ImportModule("nt"); - } - if (os_like_module != NULL) { - fstat_function = PyObject_GetAttrString(os_like_module, "fstat"); - Py_DECREF(os_like_module); - } - if (fstat_function == NULL) { - PyErr_Clear(); /* non-fatal, we'll go on without it. */ - if (Py_VerboseFlag) - PySys_WriteStderr("# zipimport unable to use os.fstat().\n"); - } - } } diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -994,10 +994,8 @@ *p++ = *quote_postfix++; } *p = '\0'; - if (_PyString_Resize(&v, (p - PyString_AS_STRING(v)))) { - Py_DECREF(v); - return NULL; - } + /* v is cleared on error */ + (void)_PyString_Resize(&v, (p - PyString_AS_STRING(v))); return v; } } diff --git a/Objects/exceptions.c b/Objects/exceptions.c --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1648,6 +1648,10 @@ PyObject *reason_str = NULL; PyObject *encoding_str = NULL; + if (!uself->object) + /* Not properly initialized. */ + return PyUnicode_FromString(""); + /* Get reason and encoding as strings, which they might not be if they've been modified after we were contructed. */ reason_str = PyObject_Str(uself->reason); @@ -1733,6 +1737,10 @@ PyObject *reason_str = NULL; PyObject *encoding_str = NULL; + if (!uself->object) + /* Not properly initialized. */ + return PyUnicode_FromString(""); + /* Get reason and encoding as strings, which they might not be if they've been modified after we were contructed. */ reason_str = PyObject_Str(uself->reason); @@ -1830,6 +1838,10 @@ PyObject *result = NULL; PyObject *reason_str = NULL; + if (!uself->object) + /* Not properly initialized. */ + return PyUnicode_FromString(""); + /* Get reason as a string, which it might not be if it's been modified after we were contructed. */ reason_str = PyObject_Str(uself->reason); diff --git a/Objects/fileobject.c b/Objects/fileobject.c --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -1941,13 +1941,13 @@ PyObject *v = PyList_GET_ITEM(list, i); if (!PyString_Check(v)) { const char *buffer; - if (((f->f_binary && - PyObject_AsReadBuffer(v, - (const void**)&buffer, - &len)) || - PyObject_AsCharBuffer(v, - &buffer, - &len))) { + int res; + if (f->f_binary) { + res = PyObject_AsReadBuffer(v, (const void**)&buffer, &len); + } else { + res = PyObject_AsCharBuffer(v, &buffer, &len); + } + if (res) { PyErr_SetString(PyExc_TypeError, "writelines() argument must be a sequence of strings"); goto error; diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -122,7 +122,8 @@ if (s[0] == '_' && s[1] != '_') { if (Py_VerboseFlag > 1) PySys_WriteStderr("# clear[1] %s\n", s); - PyDict_SetItem(d, key, Py_None); + if (PyDict_SetItem(d, key, Py_None) != 0) + PyErr_Clear(); } } } @@ -135,7 +136,8 @@ if (s[0] != '_' || strcmp(s, "__builtins__") != 0) { if (Py_VerboseFlag > 1) PySys_WriteStderr("# clear[2] %s\n", s); - PyDict_SetItem(d, key, Py_None); + if (PyDict_SetItem(d, key, Py_None) != 0) + PyErr_Clear(); } } } diff --git a/Objects/setobject.c b/Objects/setobject.c --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -1796,12 +1796,8 @@ PyObject *r1, *r2; if(!PyAnySet_Check(w)) { - if (op == Py_EQ) - Py_RETURN_FALSE; - if (op == Py_NE) - Py_RETURN_TRUE; - PyErr_SetString(PyExc_TypeError, "can only compare to a set"); - return NULL; + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } switch (op) { case Py_EQ: diff --git a/Objects/stringlib/formatter.h b/Objects/stringlib/formatter.h --- a/Objects/stringlib/formatter.h +++ b/Objects/stringlib/formatter.h @@ -180,8 +180,9 @@ Py_ssize_t consumed; int align_specified = 0; + int fill_char_specified = 0; - format->fill_char = '\0'; + format->fill_char = ' '; format->align = default_align; format->alternate = 0; format->sign = '\0'; @@ -195,6 +196,7 @@ if (end-ptr >= 2 && is_alignment_token(ptr[1])) { format->align = ptr[1]; format->fill_char = ptr[0]; + fill_char_specified = 1; align_specified = 1; ptr += 2; } @@ -218,7 +220,7 @@ } /* The special case for 0-padding (backwards compat) */ - if (format->fill_char == '\0' && end-ptr >= 1 && ptr[0] == '0') { + if (!fill_char_specified && end-ptr >= 1 && ptr[0] == '0') { format->fill_char = '0'; if (!align_specified) { format->align = '='; @@ -715,8 +717,7 @@ /* Write into that space. First the padding. */ p = fill_padding(STRINGLIB_STR(result), len, - format->fill_char=='\0'?' ':format->fill_char, - lpad, rpad); + format->fill_char, lpad, rpad); /* Then the source string. */ memcpy(p, STRINGLIB_STR(value), len * sizeof(STRINGLIB_CHAR)); @@ -893,8 +894,7 @@ /* Populate the memory. */ fill_number(STRINGLIB_STR(result), &spec, pnumeric_chars, n_digits, - prefix, format->fill_char == '\0' ? ' ' : format->fill_char, - &locale, format->type == 'X'); + prefix, format->fill_char, &locale, format->type == 'X'); done: Py_XDECREF(tmp); @@ -1048,8 +1048,7 @@ /* Populate the memory. */ fill_number(STRINGLIB_STR(result), &spec, p, n_digits, NULL, - format->fill_char == '\0' ? ' ' : format->fill_char, &locale, - 0); + format->fill_char, &locale, 0); done: PyMem_Free(buf); @@ -1265,8 +1264,7 @@ /* Populate the memory. First, the padding. */ p = fill_padding(STRINGLIB_STR(result), n_re_total + n_im_total + 1 + add_parens * 2, - format->fill_char=='\0' ? ' ' : format->fill_char, - lpad, rpad); + format->fill_char, lpad, rpad); if (add_parens) *p++ = '('; diff --git a/Objects/stringlib/transmogrify.h b/Objects/stringlib/transmogrify.h --- a/Objects/stringlib/transmogrify.h +++ b/Objects/stringlib/transmogrify.h @@ -15,7 +15,7 @@ { const char *e, *p; char *q; - size_t i, j; + Py_ssize_t i, j; PyObject *u; int tabsize = 8; @@ -25,34 +25,30 @@ /* First pass: determine size of output string */ i = j = 0; e = STRINGLIB_STR(self) + STRINGLIB_LEN(self); - for (p = STRINGLIB_STR(self); p < e; p++) + for (p = STRINGLIB_STR(self); p < e; p++) { if (*p == '\t') { if (tabsize > 0) { - j += tabsize - (j % tabsize); - if (j > PY_SSIZE_T_MAX) { - PyErr_SetString(PyExc_OverflowError, - "result is too long"); - return NULL; - } + Py_ssize_t incr = tabsize - (j % tabsize); + if (j > PY_SSIZE_T_MAX - incr) + goto overflow; + j += incr; } } else { + if (j > PY_SSIZE_T_MAX - 1) + goto overflow; j++; if (*p == '\n' || *p == '\r') { + if (i > PY_SSIZE_T_MAX - j) + goto overflow; i += j; j = 0; - if (i > PY_SSIZE_T_MAX) { - PyErr_SetString(PyExc_OverflowError, - "result is too long"); - return NULL; - } } } + } - if ((i + j) > PY_SSIZE_T_MAX) { - PyErr_SetString(PyExc_OverflowError, "result is too long"); - return NULL; - } + if (i > PY_SSIZE_T_MAX - j) + goto overflow; /* Second pass: create output string and fill it */ u = STRINGLIB_NEW(NULL, i + j); @@ -62,7 +58,7 @@ j = 0; q = STRINGLIB_STR(u); - for (p = STRINGLIB_STR(self); p < e; p++) + for (p = STRINGLIB_STR(self); p < e; p++) { if (*p == '\t') { if (tabsize > 0) { i = tabsize - (j % tabsize); @@ -77,8 +73,12 @@ if (*p == '\n' || *p == '\r') j = 0; } - + } + return u; + overflow: + PyErr_SetString(PyExc_OverflowError, "result too long"); + return NULL; } Py_LOCAL_INLINE(PyObject *) diff --git a/Objects/stringobject.c b/Objects/stringobject.c --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -748,8 +748,8 @@ UTF-8 bytes may follow. */ } } - if (p-buf < newlen && _PyString_Resize(&v, p - buf)) - goto failed; + if (p-buf < newlen) + _PyString_Resize(&v, p - buf); /* v is cleared on error */ return v; failed: Py_DECREF(v); @@ -3091,24 +3091,25 @@ i = 0; /* chars up to and including most recent \n or \r */ j = 0; /* chars since most recent \n or \r (use in tab calculations) */ e = PyString_AS_STRING(self) + PyString_GET_SIZE(self); /* end of input */ - for (p = PyString_AS_STRING(self); p < e; p++) - if (*p == '\t') { - if (tabsize > 0) { - incr = tabsize - (j % tabsize); - if (j > PY_SSIZE_T_MAX - incr) + for (p = PyString_AS_STRING(self); p < e; p++) { + if (*p == '\t') { + if (tabsize > 0) { + incr = tabsize - (j % tabsize); + if (j > PY_SSIZE_T_MAX - incr) + goto overflow1; + j += incr; + } + } + else { + if (j > PY_SSIZE_T_MAX - 1) goto overflow1; - j += incr; - } - } - else { - if (j > PY_SSIZE_T_MAX - 1) - goto overflow1; - j++; - if (*p == '\n' || *p == '\r') { - if (i > PY_SSIZE_T_MAX - j) - goto overflow1; - i += j; - j = 0; + j++; + if (*p == '\n' || *p == '\r') { + if (i > PY_SSIZE_T_MAX - j) + goto overflow1; + i += j; + j = 0; + } } } @@ -3124,25 +3125,26 @@ q = PyString_AS_STRING(u); /* next output char */ qe = PyString_AS_STRING(u) + PyString_GET_SIZE(u); /* end of output */ - for (p = PyString_AS_STRING(self); p < e; p++) - if (*p == '\t') { - if (tabsize > 0) { - i = tabsize - (j % tabsize); - j += i; - while (i--) { - if (q >= qe) - goto overflow2; - *q++ = ' '; + for (p = PyString_AS_STRING(self); p < e; p++) { + if (*p == '\t') { + if (tabsize > 0) { + i = tabsize - (j % tabsize); + j += i; + while (i--) { + if (q >= qe) + goto overflow2; + *q++ = ' '; + } } } - } - else { - if (q >= qe) - goto overflow2; - *q++ = *p; - j++; - if (*p == '\n' || *p == '\r') - j = 0; + else { + if (q >= qe) + goto overflow2; + *q++ = *p; + j++; + if (*p == '\n' || *p == '\r') + j = 0; + } } return u; diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -881,8 +881,7 @@ _Py_ForgetReference((PyObject *) v); /* DECREF items deleted by shrinkage */ for (i = newsize; i < oldsize; i++) { - Py_XDECREF(v->ob_item[i]); - v->ob_item[i] = NULL; + Py_CLEAR(v->ob_item[i]); } sv = PyObject_GC_Resize(PyTupleObject, v, newsize); if (sv == NULL) { @@ -928,8 +927,7 @@ #if PyTuple_MAXSAVESIZE > 0 /* empty tuples are used all over the place and applications may * rely on the fact that an empty tuple is a singleton. */ - Py_XDECREF(free_list[0]); - free_list[0] = NULL; + Py_CLEAR(free_list[0]); (void)PyTuple_ClearFreeList(); #endif diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -3520,8 +3520,7 @@ goto onError; return; onError: - Py_DECREF(*exceptionObject); - *exceptionObject = NULL; + Py_CLEAR(*exceptionObject); } } @@ -4826,8 +4825,7 @@ goto onError; return; onError: - Py_DECREF(*exceptionObject); - *exceptionObject = NULL; + Py_CLEAR(*exceptionObject); } } @@ -5620,7 +5618,7 @@ PyObject *item; Py_ssize_t i; - fseq = PySequence_Fast(seq, ""); + fseq = PySequence_Fast(seq, "can only join an iterable"); if (fseq == NULL) { return NULL; } diff --git a/PC/_subprocess.c b/PC/_subprocess.c --- a/PC/_subprocess.c +++ b/PC/_subprocess.c @@ -367,7 +367,8 @@ vsize + 1 + 1; if (totalsize > PyString_GET_SIZE(out)) { int offset = p - PyString_AS_STRING(out); - _PyString_Resize(&out, totalsize + 1024); + if (_PyString_Resize(&out, totalsize + 1024)) + goto exit; p = PyString_AS_STRING(out) + offset; } memcpy(p, PyString_AS_STRING(key), ksize); @@ -383,7 +384,7 @@ _PyString_Resize(&out, p - PyString_AS_STRING(out)); /* PyObject_Print(out, stdout, 0); */ - +exit: Py_XDECREF(keys); Py_XDECREF(values); diff --git a/PC/_winreg.c b/PC/_winreg.c --- a/PC/_winreg.c +++ b/PC/_winreg.c @@ -888,7 +888,7 @@ else { void *src_buf; PyBufferProcs *pb = value->ob_type->tp_as_buffer; - if (pb==NULL) { + if (pb == NULL || pb->bf_getreadbuffer == NULL) { PyErr_Format(PyExc_TypeError, "Objects of type '%s' can not " "be used as binary registry values", @@ -896,9 +896,11 @@ return FALSE; } *retDataSize = (*pb->bf_getreadbuffer)(value, 0, &src_buf); - *retDataBuf = (BYTE *)PyMem_NEW(char, - *retDataSize); - if (*retDataBuf==NULL){ + if (*retDataSize < 0) { + return FALSE; + } + *retDataBuf = (BYTE *)PyMem_NEW(char, *retDataSize); + if (*retDataBuf == NULL){ PyErr_NoMemory(); return FALSE; } @@ -948,8 +950,10 @@ fixupMultiSZ(str, retDataBuf, retDataSize); obData = PyList_New(s); - if (obData == NULL) + if (obData == NULL) { + free(str); return NULL; + } for (index = 0; index < s; index++) { size_t len = _mbstrlen(str[index]); @@ -957,6 +961,7 @@ PyErr_SetString(PyExc_OverflowError, "registry string is too long for a Python string"); Py_DECREF(obData); + free(str); return NULL; } PyList_SetItem(obData, diff --git a/PCbuild/build_ssl.py b/PCbuild/build_ssl.py --- a/PCbuild/build_ssl.py +++ b/PCbuild/build_ssl.py @@ -207,9 +207,9 @@ # Now run make. if arch == "amd64": - rc = os.system(r"ml64 -c -Foms\uptable.obj ms\uptable.asm") + rc = os.system("nasm -f win64 -DNEAR -Ox -g ms\\uptable.asm") if rc: - print("ml64 assembler has failed.") + print("nasm assembler has failed.") sys.exit(rc) shutil.copy(r"crypto\buildinf_%s.h" % arch, r"crypto\buildinf.h") diff --git a/PCbuild/pyproject.vsprops b/PCbuild/pyproject.vsprops --- a/PCbuild/pyproject.vsprops +++ b/PCbuild/pyproject.vsprops @@ -82,7 +82,7 @@ /> filename, tok->lineno + 1); PyErr_SetString(PyExc_SyntaxError, buf); return error_ret(tok); @@ -1500,15 +1500,24 @@ } while (isdigit(c)); } if (c == 'e' || c == 'E') { - exponent: + int e; + exponent: + e = c; /* Exponent part */ c = tok_nextc(tok); - if (c == '+' || c == '-') + if (c == '+' || c == '-') { c = tok_nextc(tok); - if (!isdigit(c)) { - tok->done = E_TOKEN; + if (!isdigit(c)) { + tok->done = E_TOKEN; + tok_backup(tok, c); + return ERRORTOKEN; + } + } else if (!isdigit(c)) { tok_backup(tok, c); - return ERRORTOKEN; + tok_backup(tok, e); + *p_start = tok->start; + *p_end = tok->cur; + return NUMBER; } do { c = tok_nextc(tok); diff --git a/Python/ast.c b/Python/ast.c --- a/Python/ast.c +++ b/Python/ast.c @@ -37,7 +37,7 @@ static expr_ty ast_for_call(struct compiling *, const node *, expr_ty); static PyObject *parsenumber(struct compiling *, const char *); -static PyObject *parsestr(struct compiling *, const char *); +static PyObject *parsestr(struct compiling *, const node *n, const char *); static PyObject *parsestrplus(struct compiling *, const node *n); #ifndef LINENO @@ -3444,13 +3444,14 @@ * parsestr parses it, and returns the decoded Python string object. */ static PyObject * -parsestr(struct compiling *c, const char *s) +parsestr(struct compiling *c, const node *n, const char *s) { - size_t len; + size_t len, i; int quote = Py_CHARMASK(*s); int rawmode = 0; int need_encoding; int unicode = c->c_future_unicode; + int bytes = 0; if (isalpha(quote) || quote == '_') { if (quote == 'u' || quote == 'U') { @@ -3460,6 +3461,7 @@ if (quote == 'b' || quote == 'B') { quote = *++s; unicode = 0; + bytes = 1; } if (quote == 'r' || quote == 'R') { quote = *++s; @@ -3489,6 +3491,16 @@ return NULL; } } + if (Py_Py3kWarningFlag && bytes) { + for (i = 0; i < len; i++) { + if ((unsigned char)s[i] > 127) { + if (!ast_warn(c, n, + "non-ascii bytes literals not supported in 3.x")) + return NULL; + break; + } + } + } #ifdef Py_USING_UNICODE if (unicode || Py_UnicodeFlag) { return decode_unicode(c, s, len, rawmode, c->c_encoding); @@ -3531,11 +3543,11 @@ PyObject *v; int i; REQ(CHILD(n, 0), STRING); - if ((v = parsestr(c, STR(CHILD(n, 0)))) != NULL) { + if ((v = parsestr(c, n, STR(CHILD(n, 0)))) != NULL) { /* String literal concatenation */ for (i = 1; i < NCH(n); i++) { PyObject *s; - s = parsestr(c, STR(CHILD(n, i))); + s = parsestr(c, n, STR(CHILD(n, i))); if (s == NULL) goto onError; if (PyString_Check(v) && PyString_Check(s)) { diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1323,7 +1323,7 @@ PyDoc_STRVAR(len_doc, "len(object) -> integer\n\ \n\ -Return the number of items of a sequence or mapping."); +Return the number of items of a sequence or collection."); static PyObject * diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -355,6 +355,12 @@ if (interpreter_lock) { int err = errno; PyThread_acquire_lock(interpreter_lock, 1); + /* _Py_Finalizing is protected by the GIL */ + if (_Py_Finalizing && tstate != _Py_Finalizing) { + PyThread_release_lock(interpreter_lock); + PyThread_exit_thread(); + assert(0); /* unreachable */ + } errno = err; } #endif @@ -1018,6 +1024,13 @@ /* Other threads may run now */ PyThread_acquire_lock(interpreter_lock, 1); + + /* Check if we should make a quick exit. */ + if (_Py_Finalizing && _Py_Finalizing != tstate) { + PyThread_release_lock(interpreter_lock); + PyThread_exit_thread(); + } + if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); @@ -3240,8 +3253,7 @@ if (co->co_flags & CO_GENERATOR) { /* Don't need to keep the reference to f_back, it will be set * when the generator is resumed. */ - Py_XDECREF(f->f_back); - f->f_back = NULL; + Py_CLEAR(f->f_back); PCALL(PCALL_GENERATOR); diff --git a/Python/compile.c b/Python/compile.c --- a/Python/compile.c +++ b/Python/compile.c @@ -3483,12 +3483,16 @@ target_depth = depth; if (instr->i_opcode == FOR_ITER) { target_depth = depth-2; - } else if (instr->i_opcode == SETUP_FINALLY || - instr->i_opcode == SETUP_EXCEPT) { + } + else if (instr->i_opcode == SETUP_FINALLY || + instr->i_opcode == SETUP_EXCEPT) { target_depth = depth+3; if (target_depth > maxdepth) maxdepth = target_depth; } + else if (instr->i_opcode == JUMP_IF_TRUE_OR_POP || + instr->i_opcode == JUMP_IF_FALSE_OR_POP) + depth = depth - 1; maxdepth = stackdepth_walk(c, instr->i_target, target_depth, maxdepth); if (instr->i_opcode == JUMP_ABSOLUTE || diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -315,9 +315,9 @@ Py_FatalError( "PyThreadState_DeleteCurrent: no current tstate"); _PyThreadState_Current = NULL; - tstate_delete_common(tstate); if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); + tstate_delete_common(tstate); PyEval_ReleaseLock(); } #endif /* WITH_THREAD */ diff --git a/Python/pythonrun.c b/Python/pythonrun.c --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -91,6 +91,8 @@ int Py_NoUserSiteDirectory = 0; /* for -s and site.py */ int Py_HashRandomizationFlag = 0; /* for -R and PYTHONHASHSEED */ +PyThreadState *_Py_Finalizing = NULL; + /* Hack to force loading of object files */ int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) = \ @@ -163,6 +165,7 @@ if (initialized) return; initialized = 1; + _Py_Finalizing = NULL; if ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0') Py_DebugFlag = add_flag(Py_DebugFlag, p); @@ -422,12 +425,16 @@ * the threads created via Threading. */ call_sys_exitfunc(); - initialized = 0; /* Get current thread state and interpreter pointer */ tstate = PyThreadState_GET(); interp = tstate->interp; + /* Remaining threads (e.g. daemon threads) will automatically exit + after taking the GIL (in PyEval_RestoreThread()). */ + _Py_Finalizing = tstate; + initialized = 0; + /* Disable signal handling */ PyOS_FiniInterrupts(); diff --git a/Python/sysmodule.c b/Python/sysmodule.c --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -367,8 +367,7 @@ result = call_trampoline(tstate, callback, frame, what, arg); if (result == NULL) { PyEval_SetTrace(NULL, NULL); - Py_XDECREF(frame->f_trace); - frame->f_trace = NULL; + Py_CLEAR(frame->f_trace); return -1; } if (result != Py_None) { diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -242,9 +242,9 @@ PyThread_exit_thread(void) { dprintf(("PyThread_exit_thread called\n")); - if (!initialized) { + if (!initialized) exit(0); - } + pthread_exit(0); } #ifdef USE_SEMAPHORES diff --git a/README b/README --- a/README +++ b/README @@ -1,4 +1,4 @@ -This is Python version 2.7.6 +This is Python version 2.7.7 ============================ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, diff --git a/Tools/buildbot/external-amd64.bat b/Tools/buildbot/external-amd64.bat --- a/Tools/buildbot/external-amd64.bat +++ b/Tools/buildbot/external-amd64.bat @@ -5,16 +5,24 @@ call "%VS90COMNTOOLS%\..\..\VC\vcvarsall.bat" x86_amd64 if not exist tcltk64\bin\tcl85g.dll ( - cd tcl-8.5.2.1\win + cd tcl-8.5.15.0\win nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 clean all nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 install cd ..\.. ) if not exist tcltk64\bin\tk85g.dll ( - cd tk-8.5.2.0\win - nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.5.2.1 clean - nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.5.2.1 all - nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.5.2.1 install + cd tk-8.5.15.0\win + nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.5.15.0 clean + nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.5.15.0 all + nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 TCLDIR=..\..\tcl-8.5.15.0 install cd ..\.. ) + +if not exist tcltk64\lib\tix8.4.3\tix84g.dll ( + cd tix-8.4.3.5\win + nmake -f python.mak DEBUG=1 MACHINE=AMD64 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk64 clean + nmake -f python.mak DEBUG=1 MACHINE=AMD64 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk64 all + nmake -f python.mak DEBUG=1 MACHINE=AMD64 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk64 install + cd ..\.. +) \ No newline at end of file diff --git a/Tools/buildbot/external-common.bat b/Tools/buildbot/external-common.bat --- a/Tools/buildbot/external-common.bat +++ b/Tools/buildbot/external-common.bat @@ -10,13 +10,20 @@ @rem if exist tcl8.4.12 rd /s/q tcl8.4.12 @rem if exist tcl8.4.16 rd /s/q tcl8.4.16 @rem if exist tcl-8.4.18.1 rd /s/q tcl-8.4.18.1 + at rem if exist tcl-8.5.2.1 rd /s/q tcl-8.5.2.1 + at rem if exist tcl-8.5.15.0 rd /s/q tcl-8.5.15.0 @rem if exist tk8.4.12 rd /s/q tk8.4.12 @rem if exist tk8.4.16 rd /s/q tk8.4.16 @rem if exist tk-8.4.18.1 rd /s/q tk-8.4.18.1 + at rem if exist tk-8.5.2.0 rd /s/q tk-8.5.2.0 + at rem if exist tk-8.5.15.0 rd /s/q tk-8.5.15.0 + at rem if exist tix-8.4.3.5 rd /s/q tix-8.4.3.5 @rem if exist db-4.4.20 rd /s/q db-4.4.20 @rem if exist db-4.7.25.0 rd /s/q db-4.7.25.0 @rem if exist openssl-0.9.8y rd /s/q openssl-0.9.8y - at rem if exist sqlite-3.6.21 rd /s/q sqlite-3.6.21 + at rem if exist openssl-1.0.1g rd /s/q openssl-1.0.1g + at rem if exist openssl-1.0.1h rd /s/q openssl-1.0.1h + at rem if exist sqlite-3.6.21 rd /s/q sqlite-3.6.21 @rem bzip if not exist bzip2-1.0.6 ( @@ -29,14 +36,16 @@ if not exist db-4.7.25.0 svn export http://svn.python.org/projects/external/db-4.7.25.0 @rem OpenSSL -if not exist openssl-0.9.8y svn export http://svn.python.org/projects/external/openssl-0.9.8y +if exist openssl-1.0.1g rd /s/q openssl-1.0.1g +if not exist openssl-1.0.1h svn export http://svn.python.org/projects/external/openssl-1.0.1h @rem tcl/tk -if not exist tcl-8.5.2.1 ( - rd /s/q tcltk tcltk64 - svn export http://svn.python.org/projects/external/tcl-8.5.2.1 +if not exist tcl-8.5.15.0 ( + rd /s/q tcltk tcltk64 tcl-8.5.2.1 tk-8.5.2.0 + svn export http://svn.python.org/projects/external/tcl-8.5.15.0 ) -if not exist tk-8.5.2.0 svn export http://svn.python.org/projects/external/tk-8.5.2.0 +if not exist tk-8.5.15.0 svn export http://svn.python.org/projects/external/tk-8.5.15.0 +if not exist tix-8.4.3.5 svn export http://svn.python.org/projects/external/tix-8.4.3.5 @rem sqlite3 if not exist sqlite-3.6.21 ( diff --git a/Tools/buildbot/external.bat b/Tools/buildbot/external.bat --- a/Tools/buildbot/external.bat +++ b/Tools/buildbot/external.bat @@ -6,16 +6,24 @@ if not exist tcltk\bin\tcl85g.dll ( @rem all and install need to be separate invocations, otherwise nmakehlp is not found on install - cd tcl-8.5.2.1\win - nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 DEBUG=1 INSTALLDIR=..\..\tcltk clean all + cd tcl-8.5.15.0\win + nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 DEBUG=1 INSTALLDIR=..\..\tcltk clean all nmake -f makefile.vc DEBUG=1 INSTALLDIR=..\..\tcltk install cd ..\.. ) if not exist tcltk\bin\tk85g.dll ( - cd tk-8.5.2.0\win - nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.5.2.1 clean - nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.5.2.1 all - nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.5.2.1 install + cd tk-8.5.15.0\win + nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.5.15.0 clean + nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.5.15.0 all + nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 OPTS=noxp DEBUG=1 INSTALLDIR=..\..\tcltk TCLDIR=..\..\tcl-8.5.15.0 install cd ..\.. ) + +if not exist tcltk\lib\tix8.4.3\tix84g.dll ( + cd tix-8.4.3.5\win + nmake -f python.mak DEBUG=1 MACHINE=IX86 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk clean + nmake -f python.mak DEBUG=1 MACHINE=IX86 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk all + nmake -f python.mak DEBUG=1 MACHINE=IX86 COMPILERFLAGS=-DWINVER=0x0500 TCL_DIR=..\..\tcl-8.5.15.0 TK_DIR=..\..\tk-8.5.15.0 INSTALL_DIR=..\..\tcltk install + cd ..\.. +) diff --git a/Tools/msi/msi.py b/Tools/msi/msi.py --- a/Tools/msi/msi.py +++ b/Tools/msi/msi.py @@ -445,6 +445,10 @@ ("SetDLLDirToTarget", 'DLLDIR=""', 751), ]) + # Prepend TARGETDIR to the system path, and remove it on uninstall. + add_data(db, "Environment", + [("PathAddition", "=-*Path", "[TARGETDIR];[~]", "REGISTRY.path")]) + # Execute Sequences add_data(db, "InstallExecuteSequence", [("InitialTargetDir", 'TARGETDIR=""', 750), @@ -668,11 +672,11 @@ c=features.xbutton("Advanced", "Advanced", None, 0.30) c.event("SpawnDialog", "AdvancedDlg") - c=features.text("ItemDescription", 140, 180, 210, 30, 3, + c=features.text("ItemDescription", 140, 180, 210, 40, 3, "Multiline description of the currently selected item.") c.mapping("SelectionDescription","Text") - c=features.text("ItemSize", 140, 210, 210, 45, 3, + c=features.text("ItemSize", 140, 225, 210, 33, 3, "The size of the currently selected item.") c.mapping("SelectionSize", "Text") @@ -826,7 +830,7 @@ # (i.e. additional Python libraries) need to follow the parent feature. # Features that have no advertisement trigger (e.g. the test suite) # must not support advertisement - global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature, private_crt + global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature, private_crt, prepend_path default_feature = Feature(db, "DefaultFeature", "Python", "Python Interpreter and Libraries", 1, directory = "TARGETDIR") @@ -851,6 +855,15 @@ testsuite = Feature(db, "Testsuite", "Test suite", "Python test suite (Lib/test/)", 11, parent = default_feature, attributes=2|8) + # prepend_path is an additional feature which is to be off by default. + # Since the default level for the above features is 1, this needs to be + # at least level higher. + prepend_path = Feature(db, "PrependPath", "Add python.exe to Path", + "Prepend [TARGETDIR] to the system Path variable. " + "This allows you to type 'python' into a command " + "prompt without needing the full path.", 13, + parent = default_feature, attributes=2|8, + level=2) def extract_msvcr90(): # Find the redistributable files @@ -1022,8 +1035,12 @@ lib.add_file("zipdir.zip") if dir=='tests' and parent.physical=='distutils': lib.add_file("Setup.sample") + if dir=='audiodata': + lib.glob("*.*") if dir=='decimaltestdata': lib.glob("*.decTest") + if dir=='imghdrdata': + lib.glob("*.*") if dir=='xmltestdata': lib.glob("*.xml") lib.add_file("test.xml.out") @@ -1034,6 +1051,7 @@ lib.add_file("idle.bat") if dir=="Icons": lib.glob("*.gif") + lib.glob("*.ico") lib.add_file("idle.icns") if dir=="command" and parent.physical=="distutils": lib.glob("wininst*.exe") @@ -1168,6 +1186,8 @@ "InstallPath"), ("REGISTRY.doc", msilib.gen_uuid(), "TARGETDIR", registry_component, None, "Documentation"), + ("REGISTRY.path", msilib.gen_uuid(), "TARGETDIR", registry_component, None, + None), ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", registry_component, None, None)] + tcldata) # See "FeatureComponents Table". @@ -1184,6 +1204,7 @@ add_data(db, "FeatureComponents", [(default_feature.id, "REGISTRY"), (htmlfiles.id, "REGISTRY.doc"), + (prepend_path.id, "REGISTRY.path"), (ext_feature.id, "REGISTRY.def")] + tcldata ) diff --git a/configure b/configure --- a/configure +++ b/configure @@ -5326,7 +5326,7 @@ if test "$enable_framework" then LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' - RUNSHARED=DYLD_FRAMEWORK_PATH="`pwd`:$DYLD_FRAMEWORK_PATH" + RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} BLDLIBRARY='' else BLDLIBRARY='$(LDLIBRARY)' @@ -5348,13 +5348,13 @@ SunOS*) LDLIBRARY='libpython$(VERSION).so' BLDLIBRARY='-Wl,-R,$(LIBDIR) -L. -lpython$(VERSION)' - RUNSHARED=LD_LIBRARY_PATH=`pwd`:${LD_LIBRARY_PATH} + RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} INSTSONAME="$LDLIBRARY".$SOVERSION ;; Linux*|GNU*|NetBSD*|FreeBSD*|DragonFly*|OpenBSD*) LDLIBRARY='libpython$(VERSION).so' BLDLIBRARY='-L. -lpython$(VERSION)' - RUNSHARED=LD_LIBRARY_PATH=`pwd`:${LD_LIBRARY_PATH} + RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} case $ac_sys_system in FreeBSD*) SOVERSION=`echo $SOVERSION|cut -d "." -f 1` @@ -5372,12 +5372,12 @@ ;; esac BLDLIBRARY='-Wl,+b,$(LIBDIR) -L. -lpython$(VERSION)' - RUNSHARED=SHLIB_PATH=`pwd`:${SHLIB_PATH} + RUNSHARED=SHLIB_PATH=`pwd`${SHLIB_PATH:+:${SHLIB_PATH}} ;; OSF*) LDLIBRARY='libpython$(VERSION).so' BLDLIBRARY='-rpath $(LIBDIR) -L. -lpython$(VERSION)' - RUNSHARED=LD_LIBRARY_PATH=`pwd`:${LD_LIBRARY_PATH} + RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} ;; atheos*) LDLIBRARY='libpython$(VERSION).so' @@ -5387,11 +5387,11 @@ Darwin*) LDLIBRARY='libpython$(VERSION).dylib' BLDLIBRARY='-L. -lpython$(VERSION)' - RUNSHARED='DYLD_LIBRARY_PATH=`pwd`:${DYLD_LIBRARY_PATH}' + RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; AIX*) LDLIBRARY='libpython$(VERSION).so' - RUNSHARED=LIBPATH=`pwd`:${LIBPATH} + RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} ;; esac @@ -6651,9 +6651,9 @@ fi -for ac_header in asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ +for ac_header in asm/types.h conio.h direct.h dlfcn.h errno.h \ fcntl.h grp.h \ -ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ +ieeefp.h io.h langinfo.h libintl.h poll.h process.h pthread.h \ shadow.h signal.h stdint.h stropts.h termios.h thread.h \ unistd.h utime.h \ sys/audioio.h sys/bsdtty.h sys/epoll.h sys/event.h sys/file.h sys/loadavg.h \ @@ -6884,25 +6884,6 @@ fi -# On Solaris, term.h requires curses.h -for ac_header in term.h -do : - ac_fn_c_check_header_compile "$LINENO" "term.h" "ac_cv_header_term_h" " -#ifdef HAVE_CURSES_H -#include -#endif - -" -if test "x$ac_cv_header_term_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_TERM_H 1 -_ACEOF - -fi - -done - - # On Linux, netlink.h requires asm/types.h for ac_header in linux/netlink.h do : @@ -14132,6 +14113,43 @@ fi +# first curses configure check +ac_save_cppflags="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS -I/usr/include/ncursesw" + +for ac_header in curses.h ncurses.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# On Solaris, term.h requires curses.h +for ac_header in term.h +do : + ac_fn_c_check_header_compile "$LINENO" "term.h" "ac_cv_header_term_h" " +#ifdef HAVE_CURSES_H +#include +#endif + +" +if test "x$ac_cv_header_term_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_TERM_H 1 +_ACEOF + +fi + +done + + # On HP/UX 11.0, mvwdelch is a block with a return statement { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether mvwdelch is an expression" >&5 $as_echo_n "checking whether mvwdelch is an expression... " >&6; } @@ -14285,6 +14303,8 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# last curses configure check +CPPFLAGS=$ac_save_cppflags { $as_echo "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 $as_echo "$as_me: checking for device files" >&6;} diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -860,7 +860,7 @@ if test "$enable_framework" then LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' - RUNSHARED=DYLD_FRAMEWORK_PATH="`pwd`:$DYLD_FRAMEWORK_PATH" + RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} BLDLIBRARY='' else BLDLIBRARY='$(LDLIBRARY)' @@ -880,13 +880,13 @@ SunOS*) LDLIBRARY='libpython$(VERSION).so' BLDLIBRARY='-Wl,-R,$(LIBDIR) -L. -lpython$(VERSION)' - RUNSHARED=LD_LIBRARY_PATH=`pwd`:${LD_LIBRARY_PATH} + RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} INSTSONAME="$LDLIBRARY".$SOVERSION ;; Linux*|GNU*|NetBSD*|FreeBSD*|DragonFly*|OpenBSD*) LDLIBRARY='libpython$(VERSION).so' BLDLIBRARY='-L. -lpython$(VERSION)' - RUNSHARED=LD_LIBRARY_PATH=`pwd`:${LD_LIBRARY_PATH} + RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} case $ac_sys_system in FreeBSD*) SOVERSION=`echo $SOVERSION|cut -d "." -f 1` @@ -904,12 +904,12 @@ ;; esac BLDLIBRARY='-Wl,+b,$(LIBDIR) -L. -lpython$(VERSION)' - RUNSHARED=SHLIB_PATH=`pwd`:${SHLIB_PATH} + RUNSHARED=SHLIB_PATH=`pwd`${SHLIB_PATH:+:${SHLIB_PATH}} ;; OSF*) LDLIBRARY='libpython$(VERSION).so' BLDLIBRARY='-rpath $(LIBDIR) -L. -lpython$(VERSION)' - RUNSHARED=LD_LIBRARY_PATH=`pwd`:${LD_LIBRARY_PATH} + RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} ;; atheos*) LDLIBRARY='libpython$(VERSION).so' @@ -919,11 +919,11 @@ Darwin*) LDLIBRARY='libpython$(VERSION).dylib' BLDLIBRARY='-L. -lpython$(VERSION)' - RUNSHARED='DYLD_LIBRARY_PATH=`pwd`:${DYLD_LIBRARY_PATH}' + RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; AIX*) LDLIBRARY='libpython$(VERSION).so' - RUNSHARED=LIBPATH=`pwd`:${LIBPATH} + RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} ;; esac @@ -1508,9 +1508,9 @@ # checks for header files AC_HEADER_STDC -AC_CHECK_HEADERS(asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ +AC_CHECK_HEADERS(asm/types.h conio.h direct.h dlfcn.h errno.h \ fcntl.h grp.h \ -ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ +ieeefp.h io.h langinfo.h libintl.h poll.h process.h pthread.h \ shadow.h signal.h stdint.h stropts.h termios.h thread.h \ unistd.h utime.h \ sys/audioio.h sys/bsdtty.h sys/epoll.h sys/event.h sys/file.h sys/loadavg.h \ @@ -1523,13 +1523,6 @@ AC_HEADER_DIRENT AC_HEADER_MAJOR -# On Solaris, term.h requires curses.h -AC_CHECK_HEADERS(term.h,,,[ -#ifdef HAVE_CURSES_H -#include -#endif -]) - # On Linux, netlink.h requires asm/types.h AC_CHECK_HEADERS(linux/netlink.h,,,[ #ifdef HAVE_ASM_TYPES_H @@ -4296,6 +4289,19 @@ [Define if you have struct stat.st_mtimensec]) fi +# first curses configure check +ac_save_cppflags="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS -I/usr/include/ncursesw" + +AC_CHECK_HEADERS(curses.h ncurses.h) + +# On Solaris, term.h requires curses.h +AC_CHECK_HEADERS(term.h,,,[ +#ifdef HAVE_CURSES_H +#include +#endif +]) + # On HP/UX 11.0, mvwdelch is a block with a return statement AC_MSG_CHECKING(whether mvwdelch is an expression) AC_CACHE_VAL(ac_cv_mvwdelch_is_expression, @@ -4350,6 +4356,8 @@ AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no)] ) +# last curses configure check +CPPFLAGS=$ac_save_cppflags AC_MSG_NOTICE([checking for device files]) diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -1187,7 +1187,6 @@ include_dirs=["Modules/_sqlite", sqlite_incdir], library_dirs=sqlite_libdir, - runtime_library_dirs=sqlite_libdir, extra_link_args=sqlite_extra_link_args, libraries=["sqlite3",])) else: @@ -1340,13 +1339,17 @@ # Curses support, requiring the System V version of curses, often # provided by the ncurses library. panel_library = 'panel' + curses_incs = None if curses_library.startswith('ncurses'): if curses_library == 'ncursesw': # Bug 1464056: If _curses.so links with ncursesw, # _curses_panel.so must link with panelw. panel_library = 'panelw' curses_libs = [curses_library] + curses_incs = find_file('curses.h', inc_dirs, + [os.path.join(d, 'ncursesw') for d in inc_dirs]) exts.append( Extension('_curses', ['_cursesmodule.c'], + include_dirs = curses_incs, libraries = curses_libs) ) elif curses_library == 'curses' and host_platform != 'darwin': # OSX has an old Berkeley curses, not good enough for @@ -1367,6 +1370,7 @@ if (module_enabled(exts, '_curses') and self.compiler.find_library_file(lib_dirs, panel_library)): exts.append( Extension('_curses_panel', ['_curses_panel.c'], + include_dirs = curses_incs, libraries = [panel_library] + curses_libs) ) else: missing.append('_curses_panel') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 23:55:39 2014 From: python-checkins at python.org (ezio.melotti) Date: Thu, 19 Jun 2014 23:55:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzIxNjkwOiBmaXgg?= =?utf-8?q?a_couple_of_links_in_the_docs_of_the_re_module=2E__Noticed_by_J?= =?utf-8?q?ulian?= Message-ID: <3gvcQg5wX8z7Lk4@mail.python.org> http://hg.python.org/cpython/rev/88a1f3cf4ed9 changeset: 91279:88a1f3cf4ed9 branch: 2.7 user: Ezio Melotti date: Fri Jun 20 00:47:11 2014 +0300 summary: #21690: fix a couple of links in the docs of the re module. Noticed by Julian Gilbey. files: Doc/library/re.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/re.rst b/Doc/library/re.rst --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -433,8 +433,8 @@ .. function:: compile(pattern, flags=0) Compile a regular expression pattern into a regular expression object, which - can be used for matching using its :func:`match` and :func:`search` methods, - described below. + can be used for matching using its :func:`~RegexObject.match` and + :func:`~RegexObject.search` methods, described below. The expression's behaviour can be modified by specifying a *flags* value. Values can be any of the following variables, combined using bitwise OR (the -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 23:55:41 2014 From: python-checkins at python.org (ezio.melotti) Date: Thu, 19 Jun 2014 23:55:41 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogIzIxNjkwOiBmaXgg?= =?utf-8?q?a_couple_of_links_in_the_docs_of_the_re_module=2E__Noticed_by_J?= =?utf-8?q?ulian?= Message-ID: <3gvcQj0fzfz7Lk0@mail.python.org> http://hg.python.org/cpython/rev/9090348a920d changeset: 91280:9090348a920d branch: 3.4 parent: 91275:aaee7df990a2 user: Ezio Melotti date: Fri Jun 20 00:52:11 2014 +0300 summary: #21690: fix a couple of links in the docs of the re module. Noticed by Julian Gilbey. files: Doc/library/re.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/re.rst b/Doc/library/re.rst --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -458,8 +458,8 @@ .. function:: compile(pattern, flags=0) Compile a regular expression pattern into a regular expression object, which - can be used for matching using its :func:`match` and :func:`search` methods, - described below. + can be used for matching using its :func:`~regex.match` and + :func:`~regex.search` methods, described below. The expression's behaviour can be modified by specifying a *flags* value. Values can be any of the following variables, combined using bitwise OR (the -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 19 23:55:42 2014 From: python-checkins at python.org (ezio.melotti) Date: Thu, 19 Jun 2014 23:55:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogIzIxNjkwOiBtZXJnZSB3aXRoIDMuNC4=?= Message-ID: <3gvcQk2QWxz7LkF@mail.python.org> http://hg.python.org/cpython/rev/590ad80784bf changeset: 91281:590ad80784bf parent: 91276:493d1ae3227b parent: 91280:9090348a920d user: Ezio Melotti date: Fri Jun 20 00:55:10 2014 +0300 summary: #21690: merge with 3.4. files: Doc/library/re.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/re.rst b/Doc/library/re.rst --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -458,8 +458,8 @@ .. function:: compile(pattern, flags=0) Compile a regular expression pattern into a regular expression object, which - can be used for matching using its :func:`match` and :func:`search` methods, - described below. + can be used for matching using its :func:`~regex.match` and + :func:`~regex.search` methods, described below. The expression's behaviour can be modified by specifying a *flags* value. Values can be any of the following variables, combined using bitwise OR (the -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 01:18:28 2014 From: python-checkins at python.org (ezio.melotti) Date: Fri, 20 Jun 2014 01:18:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?devguide=3A_=2320062=3A_replace_emacs?= =?utf-8?q?_page_of_the_devguide_with_a_link_to_the_PythonEditors?= Message-ID: <3gvfGD69pmz7Ljm@mail.python.org> http://hg.python.org/devguide/rev/a79c7088ed10 changeset: 700:a79c7088ed10 user: Ezio Melotti date: Fri Jun 20 02:18:11 2014 +0300 summary: #20062: replace emacs page of the devguide with a link to the PythonEditors wiki page. Initial patch by Albert Looney. files: emacs.rst | 32 -------------------------------- index.rst | 4 ++-- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/emacs.rst b/emacs.rst deleted file mode 100644 --- a/emacs.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. _emacs: - -============= -Emacs support -============= - -If you want to edit Python code in Emacs, you should download python-mode.el -and install it somewhere on your load-path. See the project page to download: -https://launchpad.net/python-mode - -While Emacs comes with a python.el file, it is not recommended. -python-mode.el is maintained by core Python developers and is generally -considered more Python programmer friendly. For example, python-mode.el -includes a killer feature called `pdbtrack` which allows you to set a pdb -breakpoint in your code, run your program in an Emacs shell buffer, and do gud -style debugging when the breakpoint is hit. - -python-mode.el is compatible with both GNU Emacs from the FSF, and XEmacs. - -For more information and bug reporting, see the above project page. For help, -development, or discussions, see the python-mode mailing list: -http://mail.python.org/mailman/listinfo/python-mode - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 78 - coding: utf-8 - End: diff --git a/index.rst b/index.rst --- a/index.rst +++ b/index.rst @@ -169,10 +169,11 @@ * :doc:`grammar` * :doc:`compiler` * Tool support - * :doc:`emacs` * :doc:`gdb` * :doc:`clang` * Various tools with configuration files as found in the `Misc directory`_ + * Information about editors and their configurations can be found in the + `wiki `_ * `python.org maintenance`_ * :ref:`Search this guide ` @@ -206,7 +207,6 @@ stdlibchanges langchanges experts - emacs gdb grammar compiler -- Repository URL: http://hg.python.org/devguide From python-checkins at python.org Fri Jun 20 01:23:51 2014 From: python-checkins at python.org (ezio.melotti) Date: Fri, 20 Jun 2014 01:23:51 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?devguide=3A_=2320915=3A_add_pip_exper?= =?utf-8?q?ts_to_the_expert_list=2E?= Message-ID: <3gvfNR1BtPz7Ljm@mail.python.org> http://hg.python.org/devguide/rev/ae349e31b56e changeset: 701:ae349e31b56e user: Ezio Melotti date: Fri Jun 20 02:23:31 2014 +0300 summary: #20915: add pip experts to the expert list. files: experts.rst | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/experts.rst b/experts.rst --- a/experts.rst +++ b/experts.rst @@ -101,6 +101,7 @@ dummy_threading brett.cannon email barry, r.david.murray* encodings lemburg, loewis +ensurepip ncoghlan, dstufft enum eli.bendersky*, barry, ethan.furman* errno exceptions @@ -324,6 +325,7 @@ networking giampaolo.rodola, pitrou object model benjamin.peterson packaging tarek, lemburg, alexis, eric.araujo, dstufft +pip ncoghlan, dstufft, pmoore, Marcus.Smith py3 transition benjamin.peterson release management tarek, lemburg, benjamin.peterson, barry, loewis, gvanrossum, anthonybaxter, eric.araujo, ned.deily, -- Repository URL: http://hg.python.org/devguide From python-checkins at python.org Fri Jun 20 02:28:04 2014 From: python-checkins at python.org (ezio.melotti) Date: Fri, 20 Jun 2014 02:28:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?devguide=3A_Add_more_experts=2E?= Message-ID: <3gvgpX6DWwz7Ljg@mail.python.org> http://hg.python.org/devguide/rev/5f83306c6836 changeset: 702:5f83306c6836 user: Ezio Melotti date: Fri Jun 20 03:27:48 2014 +0300 summary: Add more experts. files: experts.rst | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/experts.rst b/experts.rst --- a/experts.rst +++ b/experts.rst @@ -105,6 +105,7 @@ enum eli.bendersky*, barry, ethan.furman* errno exceptions +faulthandler haypo fcntl filecmp fileinput @@ -207,7 +208,7 @@ sqlite3 ghaering ssl janssen, pitrou, giampaolo.rodola, christian.heimes, dstufft stat christian.heimes -statistics +statistics steven.daprano string georg.brandl* stringprep struct mark.dickinson, meador.inge @@ -233,6 +234,7 @@ tokenize meador.inge trace belopolsky traceback georg.brandl* +tracemalloc haypo tty turtle gregorlingl types -- Repository URL: http://hg.python.org/devguide From python-checkins at python.org Fri Jun 20 10:38:46 2014 From: python-checkins at python.org (senthil.kumaran) Date: Fri, 20 Jun 2014 10:38:46 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_issue_20091_-_?= =?utf-8?q?index_entry_for_=5F=5Fmain=5F=5F_in_runpy_docs=2E?= Message-ID: <3gvthk4f2Sz7Ljv@mail.python.org> http://hg.python.org/cpython/rev/d641c096b1f5 changeset: 91282:d641c096b1f5 branch: 2.7 parent: 91279:88a1f3cf4ed9 user: Senthil Kumaran date: Fri Jun 20 01:36:58 2014 -0700 summary: issue 20091 - index entry for __main__ in runpy docs. files: Doc/library/runpy.rst | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst --- a/Doc/library/runpy.rst +++ b/Doc/library/runpy.rst @@ -22,6 +22,9 @@ .. function:: run_module(mod_name, init_globals=None, run_name=None, alter_sys=False) + .. index:: + module: __main__ + Execute the code of the specified module and return the resulting module globals dictionary. The module's code is first located using the standard import mechanism (refer to :pep:`302` for details) and then executed in a @@ -77,6 +80,9 @@ .. function:: run_path(file_path, init_globals=None, run_name=None) + .. index:: + module: __main__ + Execute the code at the named filesystem location and return the resulting module globals dictionary. As with a script name supplied to the CPython command line, the supplied path may refer to a Python source file, a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 10:38:47 2014 From: python-checkins at python.org (senthil.kumaran) Date: Fri, 20 Jun 2014 10:38:47 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_issue_20091_-_?= =?utf-8?q?index_entry_for_=5F=5Fmain=5F=5F_in_runpy_docs=2E?= Message-ID: <3gvthl6NDVz7LkC@mail.python.org> http://hg.python.org/cpython/rev/1727dcfff233 changeset: 91283:1727dcfff233 branch: 3.4 parent: 91280:9090348a920d user: Senthil Kumaran date: Fri Jun 20 01:37:53 2014 -0700 summary: issue 20091 - index entry for __main__ in runpy docs. files: Doc/library/runpy.rst | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst --- a/Doc/library/runpy.rst +++ b/Doc/library/runpy.rst @@ -28,6 +28,9 @@ .. function:: run_module(mod_name, init_globals=None, run_name=None, alter_sys=False) + .. index:: + module: __main__ + Execute the code of the specified module and return the resulting module globals dictionary. The module's code is first located using the standard import mechanism (refer to :pep:`302` for details) and then executed in a @@ -87,6 +90,9 @@ .. function:: run_path(file_path, init_globals=None, run_name=None) + .. index:: + module: __main__ + Execute the code at the named filesystem location and return the resulting module globals dictionary. As with a script name supplied to the CPython command line, the supplied path may refer to a Python source file, a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 10:38:49 2014 From: python-checkins at python.org (senthil.kumaran) Date: Fri, 20 Jun 2014 10:38:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E4?= Message-ID: <3gvthn0hJTz7Lkb@mail.python.org> http://hg.python.org/cpython/rev/fd9f7bdd7472 changeset: 91284:fd9f7bdd7472 parent: 91281:590ad80784bf parent: 91283:1727dcfff233 user: Senthil Kumaran date: Fri Jun 20 01:38:37 2014 -0700 summary: merge from 3.4 issue 20091 - index entry for __main__ in runpy docs. files: Doc/library/runpy.rst | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst --- a/Doc/library/runpy.rst +++ b/Doc/library/runpy.rst @@ -28,6 +28,9 @@ .. function:: run_module(mod_name, init_globals=None, run_name=None, alter_sys=False) + .. index:: + module: __main__ + Execute the code of the specified module and return the resulting module globals dictionary. The module's code is first located using the standard import mechanism (refer to :pep:`302` for details) and then executed in a @@ -87,6 +90,9 @@ .. function:: run_path(file_path, init_globals=None, run_name=None) + .. index:: + module: __main__ + Execute the code at the named filesystem location and return the resulting module globals dictionary. As with a script name supplied to the CPython command line, the supplied path may refer to a Python source file, a -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Fri Jun 20 11:11:35 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 20 Jun 2014 11:11:35 +0200 Subject: [Python-checkins] Daily reference leaks (590ad80784bf): sum=13 Message-ID: results for 590ad80784bf on branch "default" -------------------------------------------- test_asyncio leaked [4, 0, 0] memory blocks, sum=4 test_collections leaked [0, 2, 2] references, sum=4 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [-2, 0, 0] references, sum=-2 test_site leaked [-2, 0, 0] memory blocks, sum=-2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogEhwfP3', '-x'] From python-checkins at python.org Fri Jun 20 16:21:11 2014 From: python-checkins at python.org (zach.ware) Date: Fri, 20 Jun 2014 16:21:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Simplify_a_badly_written_c?= =?utf-8?q?ondition=2E?= Message-ID: <3gw2Hq5lk0z7Ljg@mail.python.org> http://hg.python.org/cpython/rev/2abe48ac614a changeset: 91285:2abe48ac614a user: Zachary Ware date: Fri Jun 20 09:20:37 2014 -0500 summary: Simplify a badly written condition. files: PCbuild/prepare_ssl.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/PCbuild/prepare_ssl.py b/PCbuild/prepare_ssl.py --- a/PCbuild/prepare_ssl.py +++ b/PCbuild/prepare_ssl.py @@ -186,7 +186,7 @@ ssl_dir = sys.argv[1] - if not os.path.exists(ssl_dir) and os.path.isdir(ssl_dir): + if not os.path.isdir(ssl_dir): print(ssl_dir, "is not an existing directory!") sys.exit(1) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 17:35:32 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 20 Jun 2014 17:35:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbywgVHVs?= =?utf-8?q?ip_issue_105=3A_in_debug_mode=2C_log_callbacks_taking_more_than?= =?utf-8?q?_100_ms?= Message-ID: <3gw3xc6CZCz7LkC@mail.python.org> http://hg.python.org/cpython/rev/5f2341bfed17 changeset: 91286:5f2341bfed17 branch: 3.4 parent: 91283:1727dcfff233 user: Victor Stinner date: Fri Jun 20 17:34:15 2014 +0200 summary: asyncio, Tulip issue 105: in debug mode, log callbacks taking more than 100 ms to be executed. files: Lib/asyncio/base_events.py | 32 +++++++++- Lib/test/test_asyncio/test_base_events.py | 28 +++++++++ 2 files changed, 55 insertions(+), 5 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 @@ -17,6 +17,7 @@ import collections import concurrent.futures import heapq +import inspect import logging import socket import subprocess @@ -37,6 +38,15 @@ _MAX_WORKERS = 5 +def _format_handle(handle): + cb = handle._callback + if inspect.ismethod(cb) and isinstance(cb.__self__, tasks.Task): + # format the task + return repr(cb.__self__) + else: + return str(handle) + + class _StopError(BaseException): """Raised to stop the event loop.""" @@ -128,6 +138,9 @@ self._clock_resolution = time.get_clock_info('monotonic').resolution self._exception_handler = None self._debug = False + # In debug mode, if the execution of a callback or a step of a task + # exceed this duration in seconds, the slow callback/task is logged. + self.slow_callback_duration = 0.1 def __repr__(self): return ('<%s running=%s closed=%s debug=%s>' @@ -823,16 +836,16 @@ if logger.isEnabledFor(logging.INFO): t0 = self.time() event_list = self._selector.select(timeout) - t1 = self.time() - if t1-t0 >= 1: + dt = self.time() - t0 + if dt >= 1: level = logging.INFO else: level = logging.DEBUG if timeout is not None: logger.log(level, 'poll %.3f took %.3f seconds', - timeout, t1-t0) + timeout, dt) else: - logger.log(level, 'poll took %.3f seconds', t1-t0) + logger.log(level, 'poll took %.3f seconds', dt) else: event_list = self._selector.select(timeout) self._process_events(event_list) @@ -855,7 +868,16 @@ ntodo = len(self._ready) for i in range(ntodo): handle = self._ready.popleft() - if not handle._cancelled: + if handle._cancelled: + continue + if self._debug: + t0 = self.time() + handle._run() + dt = self.time() - t0 + if dt >= self.slow_callback_duration: + logger.warning('Executing %s took %.3f seconds', + _format_handle(handle), dt) + else: handle._run() handle = None # Needed to break cycles when an exception occurs. 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 @@ -969,6 +969,34 @@ with self.assertRaises(TypeError): self.loop.run_in_executor(None, coroutine_function) + @mock.patch('asyncio.base_events.logger') + def test_log_slow_callbacks(self, m_logger): + def stop_loop_cb(loop): + loop.stop() + + @asyncio.coroutine + def stop_loop_coro(loop): + yield from () + loop.stop() + + asyncio.set_event_loop(self.loop) + self.loop.set_debug(True) + self.loop.slow_callback_duration = 0.0 + + # slow callback + self.loop.call_soon(stop_loop_cb, self.loop) + self.loop.run_forever() + fmt, *args = m_logger.warning.call_args[0] + self.assertRegex(fmt % tuple(args), + "^Executing Handle.*stop_loop_cb.* took .* seconds$") + + # slow task + asyncio.async(stop_loop_coro(self.loop), loop=self.loop) + self.loop.run_forever() + fmt, *args = m_logger.warning.call_args[0] + self.assertRegex(fmt % tuple(args), + "^Executing Task.*stop_loop_coro.* took .* seconds$") + if __name__ == '__main__': unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 17:35:34 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 20 Jun 2014 17:35:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=2C_Tulip_issue_105=3A_in_debug?= =?utf-8?q?_mode=2C_log_callbacks_taking_more?= Message-ID: <3gw3xf1xzbz7LkS@mail.python.org> http://hg.python.org/cpython/rev/ab6209ce77b6 changeset: 91287:ab6209ce77b6 parent: 91285:2abe48ac614a parent: 91286:5f2341bfed17 user: Victor Stinner date: Fri Jun 20 17:34:44 2014 +0200 summary: (Merge 3.4) asyncio, Tulip issue 105: in debug mode, log callbacks taking more than 100 ms to be executed. files: Lib/asyncio/base_events.py | 32 +++++++++- Lib/test/test_asyncio/test_base_events.py | 28 +++++++++ 2 files changed, 55 insertions(+), 5 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 @@ -17,6 +17,7 @@ import collections import concurrent.futures import heapq +import inspect import logging import socket import subprocess @@ -37,6 +38,15 @@ _MAX_WORKERS = 5 +def _format_handle(handle): + cb = handle._callback + if inspect.ismethod(cb) and isinstance(cb.__self__, tasks.Task): + # format the task + return repr(cb.__self__) + else: + return str(handle) + + class _StopError(BaseException): """Raised to stop the event loop.""" @@ -128,6 +138,9 @@ self._clock_resolution = time.get_clock_info('monotonic').resolution self._exception_handler = None self._debug = False + # In debug mode, if the execution of a callback or a step of a task + # exceed this duration in seconds, the slow callback/task is logged. + self.slow_callback_duration = 0.1 def __repr__(self): return ('<%s running=%s closed=%s debug=%s>' @@ -823,16 +836,16 @@ if logger.isEnabledFor(logging.INFO): t0 = self.time() event_list = self._selector.select(timeout) - t1 = self.time() - if t1-t0 >= 1: + dt = self.time() - t0 + if dt >= 1: level = logging.INFO else: level = logging.DEBUG if timeout is not None: logger.log(level, 'poll %.3f took %.3f seconds', - timeout, t1-t0) + timeout, dt) else: - logger.log(level, 'poll took %.3f seconds', t1-t0) + logger.log(level, 'poll took %.3f seconds', dt) else: event_list = self._selector.select(timeout) self._process_events(event_list) @@ -855,7 +868,16 @@ ntodo = len(self._ready) for i in range(ntodo): handle = self._ready.popleft() - if not handle._cancelled: + if handle._cancelled: + continue + if self._debug: + t0 = self.time() + handle._run() + dt = self.time() - t0 + if dt >= self.slow_callback_duration: + logger.warning('Executing %s took %.3f seconds', + _format_handle(handle), dt) + else: handle._run() handle = None # Needed to break cycles when an exception occurs. 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 @@ -969,6 +969,34 @@ with self.assertRaises(TypeError): self.loop.run_in_executor(None, coroutine_function) + @mock.patch('asyncio.base_events.logger') + def test_log_slow_callbacks(self, m_logger): + def stop_loop_cb(loop): + loop.stop() + + @asyncio.coroutine + def stop_loop_coro(loop): + yield from () + loop.stop() + + asyncio.set_event_loop(self.loop) + self.loop.set_debug(True) + self.loop.slow_callback_duration = 0.0 + + # slow callback + self.loop.call_soon(stop_loop_cb, self.loop) + self.loop.run_forever() + fmt, *args = m_logger.warning.call_args[0] + self.assertRegex(fmt % tuple(args), + "^Executing Handle.*stop_loop_cb.* took .* seconds$") + + # slow task + asyncio.async(stop_loop_coro(self.loop), loop=self.loop) + self.loop.run_forever() + fmt, *args = m_logger.warning.call_args[0] + self.assertRegex(fmt % tuple(args), + "^Executing Task.*stop_loop_coro.* took .* seconds$") + if __name__ == '__main__': unittest.main() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 20:59:54 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 20 Jun 2014 20:59:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNzY4?= =?utf-8?q?=3A_fix_type_in_test=5Fpydoc=2C_patch_by_Claudiu_Popa=2E?= Message-ID: <3gw8TQ2z71z7Ljq@mail.python.org> http://hg.python.org/cpython/rev/b0c850121ded changeset: 91288:b0c850121ded branch: 2.7 parent: 91282:d641c096b1f5 user: Terry Jan Reedy date: Fri Jun 20 14:59:07 2014 -0400 summary: Issue #21768: fix type in test_pydoc, patch by Claudiu Popa. files: Lib/test/test_pydoc.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -585,7 +585,7 @@ try: pydoc.render_doc(name) except ImportError: - self.fail('finding the doc of {!r} failed'.format(o)) + self.fail('finding the doc of {!r} failed'.format(name)) for name in ('not__builtin__', 'strrr', 'strr.translate', 'str.trrrranslate', '__builtin__.strrr', -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 20:59:55 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 20 Jun 2014 20:59:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzY4?= =?utf-8?q?=3A_fix_type_in_test=5Fpydoc=2C_patch_by_Claudiu_Popa=2E?= Message-ID: <3gw8TR4jPJz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/64f6e66d6e7a changeset: 91289:64f6e66d6e7a branch: 3.4 parent: 91286:5f2341bfed17 user: Terry Jan Reedy date: Fri Jun 20 14:59:11 2014 -0400 summary: Issue #21768: fix type in test_pydoc, patch by Claudiu Popa. files: Lib/test/test_pydoc.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -746,7 +746,7 @@ try: pydoc.render_doc(name) except ImportError: - self.fail('finding the doc of {!r} failed'.format(o)) + self.fail('finding the doc of {!r} failed'.format(name)) for name in ('notbuiltins', 'strrr', 'strr.translate', 'str.trrrranslate', 'builtins.strrr', -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 20:59:56 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 20 Jun 2014 20:59:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gw8TS64F0z7LkR@mail.python.org> http://hg.python.org/cpython/rev/c32fc8ccbf30 changeset: 91290:c32fc8ccbf30 parent: 91287:ab6209ce77b6 parent: 91289:64f6e66d6e7a user: Terry Jan Reedy date: Fri Jun 20 14:59:27 2014 -0400 summary: Merge with 3.4 files: Lib/test/test_pydoc.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -746,7 +746,7 @@ try: pydoc.render_doc(name) except ImportError: - self.fail('finding the doc of {!r} failed'.format(o)) + self.fail('finding the doc of {!r} failed'.format(name)) for name in ('notbuiltins', 'strrr', 'strr.translate', 'str.trrrranslate', 'builtins.strrr', -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 21:17:25 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 20 Jun 2014 21:17:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzY4?= =?utf-8?q?=3A_fix_NameError_in_test=5Fpydescr=2E_Patch_by_Claudiu_Popa=2E?= Message-ID: <3gw8sd26J8z7LjY@mail.python.org> http://hg.python.org/cpython/rev/ec0aae4df38b changeset: 91291:ec0aae4df38b branch: 3.4 parent: 91289:64f6e66d6e7a user: Terry Jan Reedy date: Fri Jun 20 15:16:35 2014 -0400 summary: Issue #21768: fix NameError in test_pydescr. Patch by Claudiu Popa. files: Lib/test/test_descr.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 @@ -1149,7 +1149,7 @@ except (TypeError, UnicodeEncodeError): pass else: - raise TestFailed("[chr(128)] slots not caught") + self.fail("[chr(128)] slots not caught") # Test leaks class Counted(object): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 21:17:26 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 20 Jun 2014 21:17:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gw8sf4sWQz7Ljv@mail.python.org> http://hg.python.org/cpython/rev/b7f2d30e93a8 changeset: 91292:b7f2d30e93a8 parent: 91290:c32fc8ccbf30 parent: 91291:ec0aae4df38b user: Terry Jan Reedy date: Fri Jun 20 15:17:01 2014 -0400 summary: Merge with 3.4 files: Lib/test/test_descr.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 @@ -1149,7 +1149,7 @@ except (TypeError, UnicodeEncodeError): pass else: - raise TestFailed("[chr(128)] slots not caught") + self.fail("[chr(128)] slots not caught") # Test leaks class Counted(object): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 23:03:54 2014 From: python-checkins at python.org (charles-francois.natali) Date: Fri, 20 Jun 2014 23:03:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNDkx?= =?utf-8?q?=3A_SocketServer=3A_Fix_a_race_condition_in_child_processes_rea?= =?utf-8?q?ping=2E?= Message-ID: <3gwCDV2l84z7LjY@mail.python.org> http://hg.python.org/cpython/rev/aa5e3f7a5501 changeset: 91293:aa5e3f7a5501 branch: 2.7 parent: 91288:b0c850121ded user: Charles-Fran?ois Natali date: Fri Jun 20 22:03:08 2014 +0100 summary: Issue #21491: SocketServer: Fix a race condition in child processes reaping. files: Lib/SocketServer.py | 56 +++++++++++++++++--------------- Misc/NEWS | 2 + 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/Lib/SocketServer.py b/Lib/SocketServer.py --- a/Lib/SocketServer.py +++ b/Lib/SocketServer.py @@ -513,35 +513,37 @@ def collect_children(self): """Internal routine to wait for children that have exited.""" - if self.active_children is None: return + if self.active_children is None: + return + + # If we're above the max number of children, wait and reap them until + # we go back below threshold. Note that we use waitpid(-1) below to be + # able to collect children in size() syscalls instead + # of size(): the downside is that this might reap children + # which we didn't spawn, which is why we only resort to this when we're + # above max_children. while len(self.active_children) >= self.max_children: - # XXX: This will wait for any child process, not just ones - # spawned by this library. This could confuse other - # libraries that expect to be able to wait for their own - # children. try: - pid, status = os.waitpid(0, 0) - except os.error: - pid = None - if pid not in self.active_children: continue - self.active_children.remove(pid) + pid, _ = os.waitpid(-1, 0) + self.active_children.discard(pid) + except OSError as e: + if e.errno == errno.ECHILD: + # we don't have any children, we're done + self.active_children.clear() + elif e.errno != errno.EINTR: + break - # XXX: This loop runs more system calls than it ought - # to. There should be a way to put the active_children into a - # process group and then use os.waitpid(-pgid) to wait for any - # of that set, but I couldn't find a way to allocate pgids - # that couldn't collide. - for child in self.active_children: + # Now reap all defunct children. + for pid in self.active_children.copy(): try: - pid, status = os.waitpid(child, os.WNOHANG) - except os.error: - pid = None - if not pid: continue - try: - self.active_children.remove(pid) - except ValueError, e: - raise ValueError('%s. x=%d and list=%r' % (e.message, pid, - self.active_children)) + pid, _ = os.waitpid(pid, os.WNOHANG) + # if the child hasn't exited yet, pid will be 0 and ignored by + # discard() below + self.active_children.discard(pid) + except OSError as e: + if e.errno == errno.ECHILD: + # someone else reaped it + self.active_children.discard(pid) def handle_timeout(self): """Wait for zombies after self.timeout seconds of inactivity. @@ -557,8 +559,8 @@ if pid: # Parent process if self.active_children is None: - self.active_children = [] - self.active_children.append(pid) + self.active_children = set() + self.active_children.add(pid) self.close_request(request) #close handle in parent process return else: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,8 @@ Library ------- +- Issue #21491: SocketServer: Fix a race condition in child processes reaping. + - Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 23:42:39 2014 From: python-checkins at python.org (charles-francois.natali) Date: Fri, 20 Jun 2014 23:42:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNDkx?= =?utf-8?q?=3A_socketserver=3A_Fix_a_race_condition_in_child_processes_rea?= =?utf-8?q?ping=2E?= Message-ID: <3gwD5C62c4z7Ljj@mail.python.org> http://hg.python.org/cpython/rev/2a7375bd09f9 changeset: 91294:2a7375bd09f9 branch: 3.4 parent: 91291:ec0aae4df38b user: Charles-Fran?ois Natali date: Fri Jun 20 22:37:35 2014 +0100 summary: Issue #21491: socketserver: Fix a race condition in child processes reaping. files: Lib/socketserver.py | 54 +++++++++++++++++--------------- Misc/NEWS | 2 + 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Lib/socketserver.py b/Lib/socketserver.py --- a/Lib/socketserver.py +++ b/Lib/socketserver.py @@ -523,35 +523,39 @@ def collect_children(self): """Internal routine to wait for children that have exited.""" - if self.active_children is None: return + if self.active_children is None: + return + + # If we're above the max number of children, wait and reap them until + # we go back below threshold. Note that we use waitpid(-1) below to be + # able to collect children in size() syscalls instead + # of size(): the downside is that this might reap children + # which we didn't spawn, which is why we only resort to this when we're + # above max_children. while len(self.active_children) >= self.max_children: - # XXX: This will wait for any child process, not just ones - # spawned by this library. This could confuse other - # libraries that expect to be able to wait for their own - # children. try: - pid, status = os.waitpid(0, 0) + pid, _ = os.waitpid(-1, 0) + self.active_children.discard(pid) + except InterruptedError: + pass + except ChildProcessError: + # we don't have any children, we're done + self.active_children.clear() except OSError: - pid = None - if pid not in self.active_children: continue - self.active_children.remove(pid) + break - # XXX: This loop runs more system calls than it ought - # to. There should be a way to put the active_children into a - # process group and then use os.waitpid(-pgid) to wait for any - # of that set, but I couldn't find a way to allocate pgids - # that couldn't collide. - for child in self.active_children: + # Now reap all defunct children. + for pid in self.active_children.copy(): try: - pid, status = os.waitpid(child, os.WNOHANG) + pid, _ = os.waitpid(pid, os.WNOHANG) + # if the child hasn't exited yet, pid will be 0 and ignored by + # discard() below + self.active_children.discard(pid) + except ChildProcessError: + # someone else reaped it + self.active_children.discard(pid) except OSError: - pid = None - if not pid: continue - try: - self.active_children.remove(pid) - except ValueError as e: - raise ValueError('%s. x=%d and list=%r' % (e.message, pid, - self.active_children)) + pass def handle_timeout(self): """Wait for zombies after self.timeout seconds of inactivity. @@ -573,8 +577,8 @@ if pid: # Parent process if self.active_children is None: - self.active_children = [] - self.active_children.append(pid) + self.active_children = set() + self.active_children.add(pid) self.close_request(request) return else: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,8 @@ Library ------- +- Issue #21491: socketserver: Fix a race condition in child processes reaping. + - Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 23:42:41 2014 From: python-checkins at python.org (charles-francois.natali) Date: Fri, 20 Jun 2014 23:42:41 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogTWVyZ2Uu?= Message-ID: <3gwD5F1xZXz7Ljy@mail.python.org> http://hg.python.org/cpython/rev/5958a89fb1ed changeset: 91295:5958a89fb1ed parent: 91281:590ad80784bf parent: 91294:2a7375bd09f9 user: Charles-Fran?ois Natali date: Fri Jun 20 22:41:21 2014 +0100 summary: Merge. files: Doc/library/runpy.rst | 6 ++ Lib/asyncio/base_events.py | 32 +++++++++- Lib/test/test_asyncio/test_base_events.py | 28 +++++++++ Lib/test/test_descr.py | 2 +- Lib/test/test_pydoc.py | 2 +- PCbuild/prepare_ssl.py | 2 +- 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst --- a/Doc/library/runpy.rst +++ b/Doc/library/runpy.rst @@ -28,6 +28,9 @@ .. function:: run_module(mod_name, init_globals=None, run_name=None, alter_sys=False) + .. index:: + module: __main__ + Execute the code of the specified module and return the resulting module globals dictionary. The module's code is first located using the standard import mechanism (refer to :pep:`302` for details) and then executed in a @@ -87,6 +90,9 @@ .. function:: run_path(file_path, init_globals=None, run_name=None) + .. index:: + module: __main__ + Execute the code at the named filesystem location and return the resulting module globals dictionary. As with a script name supplied to the CPython command line, the supplied path may refer to a Python source file, a 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 @@ -17,6 +17,7 @@ import collections import concurrent.futures import heapq +import inspect import logging import socket import subprocess @@ -37,6 +38,15 @@ _MAX_WORKERS = 5 +def _format_handle(handle): + cb = handle._callback + if inspect.ismethod(cb) and isinstance(cb.__self__, tasks.Task): + # format the task + return repr(cb.__self__) + else: + return str(handle) + + class _StopError(BaseException): """Raised to stop the event loop.""" @@ -128,6 +138,9 @@ self._clock_resolution = time.get_clock_info('monotonic').resolution self._exception_handler = None self._debug = False + # In debug mode, if the execution of a callback or a step of a task + # exceed this duration in seconds, the slow callback/task is logged. + self.slow_callback_duration = 0.1 def __repr__(self): return ('<%s running=%s closed=%s debug=%s>' @@ -823,16 +836,16 @@ if logger.isEnabledFor(logging.INFO): t0 = self.time() event_list = self._selector.select(timeout) - t1 = self.time() - if t1-t0 >= 1: + dt = self.time() - t0 + if dt >= 1: level = logging.INFO else: level = logging.DEBUG if timeout is not None: logger.log(level, 'poll %.3f took %.3f seconds', - timeout, t1-t0) + timeout, dt) else: - logger.log(level, 'poll took %.3f seconds', t1-t0) + logger.log(level, 'poll took %.3f seconds', dt) else: event_list = self._selector.select(timeout) self._process_events(event_list) @@ -855,7 +868,16 @@ ntodo = len(self._ready) for i in range(ntodo): handle = self._ready.popleft() - if not handle._cancelled: + if handle._cancelled: + continue + if self._debug: + t0 = self.time() + handle._run() + dt = self.time() - t0 + if dt >= self.slow_callback_duration: + logger.warning('Executing %s took %.3f seconds', + _format_handle(handle), dt) + else: handle._run() handle = None # Needed to break cycles when an exception occurs. 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 @@ -969,6 +969,34 @@ with self.assertRaises(TypeError): self.loop.run_in_executor(None, coroutine_function) + @mock.patch('asyncio.base_events.logger') + def test_log_slow_callbacks(self, m_logger): + def stop_loop_cb(loop): + loop.stop() + + @asyncio.coroutine + def stop_loop_coro(loop): + yield from () + loop.stop() + + asyncio.set_event_loop(self.loop) + self.loop.set_debug(True) + self.loop.slow_callback_duration = 0.0 + + # slow callback + self.loop.call_soon(stop_loop_cb, self.loop) + self.loop.run_forever() + fmt, *args = m_logger.warning.call_args[0] + self.assertRegex(fmt % tuple(args), + "^Executing Handle.*stop_loop_cb.* took .* seconds$") + + # slow task + asyncio.async(stop_loop_coro(self.loop), loop=self.loop) + self.loop.run_forever() + fmt, *args = m_logger.warning.call_args[0] + self.assertRegex(fmt % tuple(args), + "^Executing Task.*stop_loop_coro.* took .* seconds$") + if __name__ == '__main__': unittest.main() 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 @@ -1149,7 +1149,7 @@ except (TypeError, UnicodeEncodeError): pass else: - raise TestFailed("[chr(128)] slots not caught") + self.fail("[chr(128)] slots not caught") # Test leaks class Counted(object): diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -746,7 +746,7 @@ try: pydoc.render_doc(name) except ImportError: - self.fail('finding the doc of {!r} failed'.format(o)) + self.fail('finding the doc of {!r} failed'.format(name)) for name in ('notbuiltins', 'strrr', 'strr.translate', 'str.trrrranslate', 'builtins.strrr', diff --git a/PCbuild/prepare_ssl.py b/PCbuild/prepare_ssl.py --- a/PCbuild/prepare_ssl.py +++ b/PCbuild/prepare_ssl.py @@ -186,7 +186,7 @@ ssl_dir = sys.argv[1] - if not os.path.exists(ssl_dir) and os.path.isdir(ssl_dir): + if not os.path.isdir(ssl_dir): print(ssl_dir, "is not an existing directory!") sys.exit(1) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 23:42:42 2014 From: python-checkins at python.org (charles-francois.natali) Date: Fri, 20 Jun 2014 23:42:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?b?KTogTWVyZ2Uu?= Message-ID: <3gwD5G3SyYz7LkD@mail.python.org> http://hg.python.org/cpython/rev/4a0c5adfc027 changeset: 91296:4a0c5adfc027 parent: 91295:5958a89fb1ed parent: 91292:b7f2d30e93a8 user: Charles-Fran?ois Natali date: Fri Jun 20 22:41:46 2014 +0100 summary: Merge. files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 23:49:51 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 20 Jun 2014 23:49:51 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzcw?= =?utf-8?q?=3A_Call_function_instead_of_module=2E_Patch_by_Claudiu_Popa=2E?= Message-ID: <3gwDFW0lR0z7Ljj@mail.python.org> http://hg.python.org/cpython/rev/108a23d02b84 changeset: 91297:108a23d02b84 branch: 3.4 parent: 91294:2a7375bd09f9 user: Terry Jan Reedy date: Fri Jun 20 17:49:10 2014 -0400 summary: Issue #21770: Call function instead of module. Patch by Claudiu Popa. files: Lib/test/script_helper.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py --- a/Lib/test/script_helper.py +++ b/Lib/test/script_helper.py @@ -155,8 +155,8 @@ script_name = make_script(zip_dir, script_basename, source) unlink.append(script_name) if compiled: - init_name = py_compile(init_name, doraise=True) - script_name = py_compile(script_name, doraise=True) + init_name = py_compile.compile(init_name, doraise=True) + script_name = py_compile.compile(script_name, doraise=True) unlink.extend((init_name, script_name)) pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)] script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 23:49:52 2014 From: python-checkins at python.org (terry.reedy) Date: Fri, 20 Jun 2014 23:49:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gwDFX21Zrz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/04c8d7d337b4 changeset: 91298:04c8d7d337b4 parent: 91296:4a0c5adfc027 parent: 91297:108a23d02b84 user: Terry Jan Reedy date: Fri Jun 20 17:49:25 2014 -0400 summary: Merge with 3.4 files: Lib/test/script_helper.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py --- a/Lib/test/script_helper.py +++ b/Lib/test/script_helper.py @@ -155,8 +155,8 @@ script_name = make_script(zip_dir, script_basename, source) unlink.append(script_name) if compiled: - init_name = py_compile(init_name, doraise=True) - script_name = py_compile(script_name, doraise=True) + init_name = py_compile.compile(init_name, doraise=True) + script_name = py_compile.compile(script_name, doraise=True) unlink.extend((init_name, script_name)) pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)] script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 23:51:22 2014 From: python-checkins at python.org (charles-francois.natali) Date: Fri, 20 Jun 2014 23:51:22 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321491=3A_socketse?= =?utf-8?q?rver=3A_Fix_a_race_condition_in_child_processes_reaping=2E?= Message-ID: <3gwDHG3xDHz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/ae0b572ced20 changeset: 91299:ae0b572ced20 parent: 91296:4a0c5adfc027 user: Charles-Fran?ois Natali date: Fri Jun 20 22:49:26 2014 +0100 summary: Issue #21491: socketserver: Fix a race condition in child processes reaping. files: Lib/socketserver.py | 54 +++++++++++++++++--------------- Misc/NEWS | 2 + 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Lib/socketserver.py b/Lib/socketserver.py --- a/Lib/socketserver.py +++ b/Lib/socketserver.py @@ -539,35 +539,39 @@ def collect_children(self): """Internal routine to wait for children that have exited.""" - if self.active_children is None: return + if self.active_children is None: + return + + # If we're above the max number of children, wait and reap them until + # we go back below threshold. Note that we use waitpid(-1) below to be + # able to collect children in size() syscalls instead + # of size(): the downside is that this might reap children + # which we didn't spawn, which is why we only resort to this when we're + # above max_children. while len(self.active_children) >= self.max_children: - # XXX: This will wait for any child process, not just ones - # spawned by this library. This could confuse other - # libraries that expect to be able to wait for their own - # children. try: - pid, status = os.waitpid(0, 0) + pid, _ = os.waitpid(-1, 0) + self.active_children.discard(pid) + except InterruptedError: + pass + except ChildProcessError: + # we don't have any children, we're done + self.active_children.clear() except OSError: - pid = None - if pid not in self.active_children: continue - self.active_children.remove(pid) + break - # XXX: This loop runs more system calls than it ought - # to. There should be a way to put the active_children into a - # process group and then use os.waitpid(-pgid) to wait for any - # of that set, but I couldn't find a way to allocate pgids - # that couldn't collide. - for child in self.active_children: + # Now reap all defunct children. + for pid in self.active_children.copy(): try: - pid, status = os.waitpid(child, os.WNOHANG) + pid, _ = os.waitpid(pid, os.WNOHANG) + # if the child hasn't exited yet, pid will be 0 and ignored by + # discard() below + self.active_children.discard(pid) + except ChildProcessError: + # someone else reaped it + self.active_children.discard(pid) except OSError: - pid = None - if not pid: continue - try: - self.active_children.remove(pid) - except ValueError as e: - raise ValueError('%s. x=%d and list=%r' % (e.message, pid, - self.active_children)) + pass def handle_timeout(self): """Wait for zombies after self.timeout seconds of inactivity. @@ -589,8 +593,8 @@ if pid: # Parent process if self.active_children is None: - self.active_children = [] - self.active_children.append(pid) + self.active_children = set() + self.active_children.add(pid) self.close_request(request) return else: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,8 @@ Library ------- +- Issue #21491: socketserver: Fix a race condition in child processes reaping. + - Issue #21719: Added the ``st_file_attributes`` field to os.stat_result on Windows. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 23:51:23 2014 From: python-checkins at python.org (charles-francois.natali) Date: Fri, 20 Jun 2014 23:51:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?b?KTogTWVyZ2Uu?= Message-ID: <3gwDHH5dgxz7Lk4@mail.python.org> http://hg.python.org/cpython/rev/f23edd061d89 changeset: 91300:f23edd061d89 parent: 91299:ae0b572ced20 parent: 91298:04c8d7d337b4 user: Charles-Fran?ois Natali date: Fri Jun 20 22:50:48 2014 +0100 summary: Merge. files: Lib/test/script_helper.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py --- a/Lib/test/script_helper.py +++ b/Lib/test/script_helper.py @@ -155,8 +155,8 @@ script_name = make_script(zip_dir, script_basename, source) unlink.append(script_name) if compiled: - init_name = py_compile(init_name, doraise=True) - script_name = py_compile(script_name, doraise=True) + init_name = py_compile.compile(init_name, doraise=True) + script_name = py_compile.compile(script_name, doraise=True) unlink.extend((init_name, script_name)) pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)] script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 20 23:57:41 2014 From: python-checkins at python.org (charles-francois.natali) Date: Fri, 20 Jun 2014 23:57:41 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogRml4IHRlc3RfcHlk?= =?utf-8?q?oc_failure_introduced_by_e89c39125892=2E_Patch_by_Berker_Peksag?= =?utf-8?q?=2E?= Message-ID: <3gwDQY1ff7z7LjY@mail.python.org> http://hg.python.org/cpython/rev/20e65501cfa4 changeset: 91301:20e65501cfa4 branch: 2.7 parent: 91293:aa5e3f7a5501 user: Charles-Fran?ois Natali date: Fri Jun 20 22:57:19 2014 +0100 summary: Fix test_pydoc failure introduced by e89c39125892. Patch by Berker Peksag. files: Lib/test/test_pydoc.py | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -14,8 +14,8 @@ import test.test_support from collections import namedtuple from test.script_helper import assert_python_ok -from test.test_support import ( - TESTFN, rmtree, reap_children, captured_stdout, captured_stderr) +from test.test_support import (TESTFN, rmtree, reap_children, captured_stdout, + captured_stderr, requires_docstrings) from test import pydoc_mod @@ -300,6 +300,7 @@ class PydocDocTest(unittest.TestCase): + @requires_docstrings @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_html_doc(self): @@ -317,6 +318,7 @@ print_diffs(expected_html, result) self.fail("outputs are not equal, see diff above") + @requires_docstrings @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_text_doc(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 21 00:01:32 2014 From: python-checkins at python.org (charles-francois.natali) Date: Sat, 21 Jun 2014 00:01:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogRml4IHRlc3RfcHlk?= =?utf-8?q?oc_failure_introduced_by_7aa72075d440=2E_Patch_by_Berker_Peksag?= =?utf-8?q?=2E?= Message-ID: <3gwDW010j2z7LjY@mail.python.org> http://hg.python.org/cpython/rev/7b336447b9b7 changeset: 91302:7b336447b9b7 branch: 3.4 parent: 91294:2a7375bd09f9 user: Charles-Fran?ois Natali date: Fri Jun 20 22:59:12 2014 +0100 summary: Fix test_pydoc failure introduced by 7aa72075d440. Patch by Berker Peksag. files: Lib/test/test_pydoc.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -402,6 +402,7 @@ "Docstrings are omitted with -O2 and above") @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') + @requires_docstrings def test_html_doc(self): result, doc_loc = get_pydoc_html(pydoc_mod) mod_file = inspect.getabsfile(pydoc_mod) @@ -421,6 +422,7 @@ "Docstrings are omitted with -O2 and above") @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') + @requires_docstrings def test_text_doc(self): result, doc_loc = get_pydoc_text(pydoc_mod) expected_text = expected_text_pattern % ( @@ -495,6 +497,7 @@ 'Docstrings are omitted with -O2 and above') @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') + @requires_docstrings def test_help_output_redirect(self): # issue 940286, if output is set in Helper, then all output from # Helper.help should be redirected -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 21 00:01:33 2014 From: python-checkins at python.org (charles-francois.natali) Date: Sat, 21 Jun 2014 00:01:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy40IC0+IDMuNCk6?= =?utf-8?q?_Merge=2E?= Message-ID: <3gwDW12l1Gz7LjY@mail.python.org> http://hg.python.org/cpython/rev/9c5e9e2e0a09 changeset: 91303:9c5e9e2e0a09 branch: 3.4 parent: 91302:7b336447b9b7 parent: 91297:108a23d02b84 user: Charles-Fran?ois Natali date: Fri Jun 20 22:59:32 2014 +0100 summary: Merge. files: Lib/test/script_helper.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py --- a/Lib/test/script_helper.py +++ b/Lib/test/script_helper.py @@ -155,8 +155,8 @@ script_name = make_script(zip_dir, script_basename, source) unlink.append(script_name) if compiled: - init_name = py_compile(init_name, doraise=True) - script_name = py_compile(script_name, doraise=True) + init_name = py_compile.compile(init_name, doraise=True) + script_name = py_compile.compile(script_name, doraise=True) unlink.extend((init_name, script_name)) pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)] script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 21 00:01:34 2014 From: python-checkins at python.org (charles-francois.natali) Date: Sat, 21 Jun 2014 00:01:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Fix_test=5Fpydoc_failure_introduced_by_cddb17c4975e=2E_P?= =?utf-8?q?atch_by_Berker_Peksag=2E?= Message-ID: <3gwDW24X4zz7LkQ@mail.python.org> http://hg.python.org/cpython/rev/3d9e35137641 changeset: 91304:3d9e35137641 parent: 91300:f23edd061d89 parent: 91303:9c5e9e2e0a09 user: Charles-Fran?ois Natali date: Fri Jun 20 23:00:22 2014 +0100 summary: Fix test_pydoc failure introduced by cddb17c4975e. Patch by Berker Peksag. files: Lib/test/test_pydoc.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -402,6 +402,7 @@ "Docstrings are omitted with -O2 and above") @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') + @requires_docstrings def test_html_doc(self): result, doc_loc = get_pydoc_html(pydoc_mod) mod_file = inspect.getabsfile(pydoc_mod) @@ -421,6 +422,7 @@ "Docstrings are omitted with -O2 and above") @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') + @requires_docstrings def test_text_doc(self): result, doc_loc = get_pydoc_text(pydoc_mod) expected_text = expected_text_pattern % ( @@ -495,6 +497,7 @@ 'Docstrings are omitted with -O2 and above') @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') + @requires_docstrings def test_help_output_redirect(self): # issue 940286, if output is set in Helper, then all output from # Helper.help should be redirected -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Jun 21 09:04:58 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 21 Jun 2014 09:04:58 +0200 Subject: [Python-checkins] Daily reference leaks (3d9e35137641): sum=9 Message-ID: results for 3d9e35137641 on branch "default" -------------------------------------------- test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [0, 2, -2] references, sum=0 test_site leaked [0, 2, -2] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogvmXPHM', '-x'] From root at python.org Sat Jun 21 10:05:26 2014 From: root at python.org (Cron Daemon) Date: Sat, 21 Jun 2014 10:05:26 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From python-checkins at python.org Sat Jun 21 13:19:48 2014 From: python-checkins at python.org (nick.coghlan) Date: Sat, 21 Jun 2014 13:19:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?devguide=3A_Note_Python_2=2E7_What=27?= =?utf-8?q?s_New_maintenance_changes?= Message-ID: <3gwZD46C9Xz7Ljq@mail.python.org> http://hg.python.org/devguide/rev/645c79611f32 changeset: 703:645c79611f32 user: Nick Coghlan date: Sat Jun 21 21:19:39 2014 +1000 summary: Note Python 2.7 What's New maintenance changes files: committing.rst | 15 ++++++++++++--- 1 files changed, 12 insertions(+), 3 deletions(-) diff --git a/committing.rst b/committing.rst --- a/committing.rst +++ b/committing.rst @@ -140,14 +140,16 @@ http://www.python.org/psf/contrib/contrib-form/ -NEWS Entries ------------- +What's New and NEWS Entries +--------------------------- Almost all changes made to the code base deserve an entry in ``Misc/NEWS``. If the change is particularly interesting for end users (e.g. new features, significant improvements, or backwards-incompatible changes), an entry in the ``What's New in Python`` document (in ``Doc/whatsnew/``) should be added -as well. There are two notable exceptions to this general principle, and they +as well. + +There are two notable exceptions to this general principle, and they both relate to changes that *already* have a NEWS entry, and have not yet been included in any formal release (including alpha and beta releases). These exceptions are: @@ -161,6 +163,13 @@ and the original NEWS entry remains valid, then no additional entry is needed. +Needing a What's New entry almost always means that a change is *not* +suitable for inclusion in a maintenance release. A small number of +exceptions have been made for Python 2.7 due to the long support period - +when implemented, these changes *must* be noted in the "New Additions in +Python 2.7 Maintenance Releases" section of the Python 2.7 What's New +document. + New NEWS entries are customarily added at or near the top of their respective sections, so that entries within a section appear in approximate order from newest to oldest. However, this is customary and not a -- Repository URL: http://hg.python.org/devguide From python-checkins at python.org Sat Jun 21 13:59:35 2014 From: python-checkins at python.org (giampaolo.rodola) Date: Sat, 21 Jun 2014 13:59:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=236916=3A_raise_a_depreca?= =?utf-8?q?tion_warning_if_using_asynchat=2Efifo?= Message-ID: <3gwb5z1pfHz7LjW@mail.python.org> http://hg.python.org/cpython/rev/233168a2a656 changeset: 91305:233168a2a656 parent: 91175:42a645d74e9d user: Giampaolo Rodola' date: Sat Jun 21 13:58:30 2014 +0200 summary: #6916: raise a deprecation warning if using asynchat.fifo files: Lib/asynchat.py | 3 +++ Lib/test/test_asynchat.py | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Lib/asynchat.py b/Lib/asynchat.py --- a/Lib/asynchat.py +++ b/Lib/asynchat.py @@ -275,6 +275,9 @@ class fifo: def __init__ (self, list=None): + import warnings + warnings.warn('fifo class will be removed in Python 3.6', + DeprecationWarning, stacklevel=2) if not list: self.list = deque() else: diff --git a/Lib/test/test_asynchat.py b/Lib/test/test_asynchat.py --- a/Lib/test/test_asynchat.py +++ b/Lib/test/test_asynchat.py @@ -8,6 +8,7 @@ import asyncore, asynchat, socket, time import unittest import sys +import warnings try: import threading except ImportError: @@ -260,7 +261,9 @@ class TestFifo(unittest.TestCase): def test_basic(self): - f = asynchat.fifo() + with warnings.catch_warnings(record=True) as w: + f = asynchat.fifo() + assert issubclass(w[0].category, DeprecationWarning) f.push(7) f.push(b'a') self.assertEqual(len(f), 2) @@ -275,7 +278,9 @@ self.assertEqual(f.pop(), (0, None)) def test_given_list(self): - f = asynchat.fifo([b'x', 17, 3]) + with warnings.catch_warnings(record=True) as w: + f = asynchat.fifo([b'x', 17, 3]) + assert issubclass(w[0].category, DeprecationWarning) self.assertEqual(len(f), 3) self.assertEqual(f.pop(), (1, b'x')) self.assertEqual(f.pop(), (1, 17)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 21 13:59:37 2014 From: python-checkins at python.org (giampaolo.rodola) Date: Sat, 21 Jun 2014 13:59:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge_heads?= Message-ID: <3gwb6151mfz7LlH@mail.python.org> http://hg.python.org/cpython/rev/fdfa15a9243c changeset: 91306:fdfa15a9243c parent: 91305:233168a2a656 parent: 91304:3d9e35137641 user: Giampaolo Rodola' date: Sat Jun 21 13:59:25 2014 +0200 summary: merge heads files: Doc/howto/functional.rst | 2 +- Doc/library/asyncio-eventloop.rst | 11 +- Doc/library/asyncio-subprocess.rst | 16 +- Doc/library/exceptions.rst | 7 +- Doc/library/functions.rst | 3 +- Doc/library/inspect.rst | 16 + Doc/library/io.rst | 6 + Doc/library/os.rst | 20 +- Doc/library/ossaudiodev.rst | 2 +- Doc/library/re.rst | 4 +- Doc/library/runpy.rst | 6 + Doc/library/stat.rst | 28 +- Doc/whatsnew/3.5.rst | 15 + Include/genobject.h | 8 + Lib/asyncio/base_events.py | 53 ++- Lib/asyncio/queues.py | 6 +- Lib/asyncio/selector_events.py | 54 ++- Lib/asyncio/tasks.py | 12 +- Lib/asyncio/test_utils.py | 18 + Lib/distutils/command/upload.py | 9 +- Lib/distutils/tests/test_upload.py | 16 +- Lib/heapq.py | 32 +- Lib/http/server.py | 2 +- Lib/idlelib/HyperParser.py | 174 ++++---- Lib/idlelib/ParenMatch.py | 14 +- Lib/idlelib/idle_test/test_hyperparser.py | 191 ++++++++++ Lib/idlelib/idle_test/test_parenmatch.py | 109 +++++ Lib/os.py | 12 +- Lib/posixpath.py | 1 - Lib/socketserver.py | 54 +- Lib/stat.py | 23 + Lib/test/script_helper.py | 4 +- Lib/test/test_asyncio/test_base_events.py | 68 ++- Lib/test/test_asyncio/test_events.py | 14 +- Lib/test/test_asyncio/test_futures.py | 25 +- Lib/test/test_asyncio/test_locks.py | 68 +-- Lib/test/test_asyncio/test_proactor_events.py | 7 +- Lib/test/test_asyncio/test_queues.py | 47 +- Lib/test/test_asyncio/test_selector_events.py | 26 +- Lib/test/test_asyncio/test_streams.py | 5 +- Lib/test/test_asyncio/test_subprocess.py | 10 +- Lib/test/test_asyncio/test_tasks.py | 152 ++++--- Lib/test/test_asyncio/test_unix_events.py | 40 +- Lib/test/test_asyncio/test_windows_events.py | 9 +- Lib/test/test_deque.py | 5 + Lib/test/test_descr.py | 2 +- Lib/test/test_enum.py | 4 +- Lib/test/test_generators.py | 39 ++ Lib/test/test_grammar.py | 25 + Lib/test/test_heapq.py | 4 +- Lib/test/test_httpservers.py | 5 + Lib/test/test_minidom.py | 7 + Lib/test/test_os.py | 22 + Lib/test/test_pydoc.py | 5 +- Lib/test/test_stat.py | 29 + Lib/test/test_subprocess.py | 38 +- Lib/test/test_sys.py | 2 +- Lib/xml/dom/minidom.py | 2 +- Misc/ACKS | 3 + Misc/NEWS | 34 + Modules/_heapqmodule.c | 98 ++-- Modules/_io/textio.c | 6 +- Modules/_stat.c | 36 + Modules/posixmodule.c | 16 + Objects/exceptions.c | 135 +++++++ Objects/genobject.c | 90 +++- PCbuild/prepare_ssl.py | 2 +- Python/bltinmodule.c | 2 +- Python/ceval.c | 37 +- 69 files changed, 1526 insertions(+), 521 deletions(-) diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -583,7 +583,7 @@ Because ``yield`` will often be returning ``None``, you should always check for this case. Don't just use its value in expressions unless you're sure that the -:meth:`~generator.send` method will be the only method used resume your +:meth:`~generator.send` method will be the only method used to resume your generator function. In addition to :meth:`~generator.send`, there are two other methods on 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 @@ -311,11 +311,10 @@ .. method:: BaseEventLoop.create_server(protocol_factory, host=None, port=None, \*, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None) - A :ref:`coroutine ` method which creates a TCP server bound to - host and port. + Create a TCP server bound to host and port. Return an + :class:`AbstractServer` object which can be used to stop the service. - The return value is a :class:`AbstractServer` object which can be used to stop - the service. + This method is a :ref:`coroutine `. If *host* is an empty string or None all interfaces are assumed and a list of multiple sockets will be returned (most likely @@ -588,10 +587,14 @@ Get the debug mode (:class:`bool`) of the event loop, ``False`` by default. + .. versionadded:: 3.4.2 + .. method:: BaseEventLoop.set_debug(enabled: bool) Set the debug mode of the event loop. + .. versionadded:: 3.4.2 + .. seealso:: The :ref:`Develop with asyncio ` section. 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 @@ -22,8 +22,8 @@ .. function:: create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) - Run the shell command *cmd* given as a string. Return a :class:`~asyncio.subprocess.Process` - instance. + Run the shell command *cmd*. See :meth:`BaseEventLoop.subprocess_shell` for + parameters. Return a :class:`~asyncio.subprocess.Process` instance. The optional *limit* parameter sets the buffer limit passed to the :class:`StreamReader`. @@ -32,7 +32,8 @@ .. function:: create_subprocess_exec(\*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds) - Create a subprocess. Return a :class:`~asyncio.subprocess.Process` instance. + Create a subprocess. See :meth:`BaseEventLoop.subprocess_exec` for + parameters. Return a :class:`~asyncio.subprocess.Process` instance. The optional *limit* parameter sets the buffer limit passed to the :class:`StreamReader`. @@ -50,7 +51,9 @@ .. method:: BaseEventLoop.subprocess_exec(protocol_factory, \*args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, \*\*kwargs) - Create a subprocess from one or more string arguments, where the first string + Create a subprocess from one or more string arguments (character strings or + bytes strings encoded to the :ref:`filesystem encoding + `), where the first string specifies the program to execute, and the remaining strings specify the program's arguments. (Thus, together the string arguments form the ``sys.argv`` value of the program, assuming it is a Python script.) This is @@ -94,8 +97,9 @@ .. method:: BaseEventLoop.subprocess_shell(protocol_factory, cmd, \*, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, \*\*kwargs) - Create a subprocess from *cmd*, which is a string using the platform's - "shell" syntax. This is similar to the standard library + Create a subprocess from *cmd*, which is a character string or a bytes + string encoded to the :ref:`filesystem encoding `, + using the platform's "shell" syntax. This is similar to the standard library :class:`subprocess.Popen` class called with ``shell=True``. See :meth:`~BaseEventLoop.subprocess_exec` for more details about diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -274,9 +274,10 @@ Raised when the result of an arithmetic operation is too large to be represented. This cannot occur for integers (which would rather raise - :exc:`MemoryError` than give up). Because of the lack of standardization of - floating point exception handling in C, most floating point operations also - aren't checked. + :exc:`MemoryError` than give up). However, for historical reasons, + OverflowError is sometimes raised for integers that are outside a required + range. Because of the lack of standardization of floating point exception + handling in C, most floating point operations are not checked. .. exception:: ReferenceError diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -742,7 +742,8 @@ .. function:: len(s) Return the length (the number of items) of an object. The argument may be a - sequence (string, tuple or list) or a mapping (dictionary). + sequence (such as a string, bytes, tuple, list, or range) or a collection + (such as a dictionary, set, or frozen set). .. _func-list: diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -159,6 +159,16 @@ | | | arguments and local | | | | variables | +-----------+-----------------+---------------------------+ +| generator | __name__ | name | ++-----------+-----------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-----------------+---------------------------+ +| | gi_frame | frame | ++-----------+-----------------+---------------------------+ +| | gi_running | is the generator running? | ++-----------+-----------------+---------------------------+ +| | gi_code | code | ++-----------+-----------------+---------------------------+ | builtin | __doc__ | documentation string | +-----------+-----------------+---------------------------+ | | __name__ | original name of this | @@ -169,6 +179,12 @@ | | | ``None`` | +-----------+-----------------+---------------------------+ +.. versionchanged:: 3.5 + + Add ``__qualname__`` attribute to generators. The ``__name__`` attribute of + generators is now set from the function name, instead of the code name, and + it can now be modified. + .. function:: getmembers(object[, predicate]) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -353,6 +353,12 @@ is usual for each of the lines provided to have a line separator at the end. + .. method:: __del__() + + Prepare for object destruction. :class:`IOBase` provides a default + implementation of this method that calls the instance's + :meth:`~IOBase.close` method. + .. class:: RawIOBase diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -65,6 +65,7 @@ .. _os-filenames: +.. _filesystem-encoding: File Names, Command Line Arguments, and Environment Variables ------------------------------------------------------------- @@ -1094,7 +1095,8 @@ .. note:: - For a higher-level version of this see :mod:`socket.socket.sendfile`. + For a higher-level wrapper of :func:`sendfile`, see + :mod:`socket.socket.sendfile`. .. versionadded:: 3.3 @@ -1903,6 +1905,11 @@ * :attr:`st_creator` * :attr:`st_type` + On Windows systems, the following attribute is also available: + + * :attr:`st_file_attributes` - Windows file attribute bits (see the + ``FILE_ATTRIBUTE_*`` constants in the :mod:`stat` module) + .. note:: The exact meaning and resolution of the :attr:`st_atime`, @@ -1956,6 +1963,9 @@ and the :attr:`st_atime_ns`, :attr:`st_mtime_ns`, and :attr:`st_ctime_ns` members. + .. versionadded:: 3.5 + Added the :attr:`st_file_attributes` member on Windows. + .. function:: stat_float_times([newvalue]) @@ -2235,9 +2245,11 @@ If optional argument *topdown* is ``True`` or not specified, the triple for a directory is generated before the triples for any of its subdirectories - (directories are generated top-down). If *topdown* is ``False``, the triple for a - directory is generated after the triples for all of its subdirectories - (directories are generated bottom-up). + (directories are generated top-down). If *topdown* is ``False``, the triple + for a directory is generated after the triples for all of its subdirectories + (directories are generated bottom-up). No matter the value of *topdown*, the + list of subdirectories is retrieved before the tuples for the directory and + its subdirectories are generated. When *topdown* is ``True``, the caller can modify the *dirnames* list in-place (perhaps using :keyword:`del` or slice assignment), and :func:`walk` will only diff --git a/Doc/library/ossaudiodev.rst b/Doc/library/ossaudiodev.rst --- a/Doc/library/ossaudiodev.rst +++ b/Doc/library/ossaudiodev.rst @@ -407,7 +407,7 @@ (silent) to 100 (full volume). If the control is monophonic, a 2-tuple is still returned, but both volumes are the same. - Raises :exc:`OSSAudioError` if an invalid control was is specified, or + Raises :exc:`OSSAudioError` if an invalid control is specified, or :exc:`OSError` if an unsupported control is specified. diff --git a/Doc/library/re.rst b/Doc/library/re.rst --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -458,8 +458,8 @@ .. function:: compile(pattern, flags=0) Compile a regular expression pattern into a regular expression object, which - can be used for matching using its :func:`match` and :func:`search` methods, - described below. + can be used for matching using its :func:`~regex.match` and + :func:`~regex.search` methods, described below. The expression's behaviour can be modified by specifying a *flags* value. Values can be any of the following variables, combined using bitwise OR (the diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst --- a/Doc/library/runpy.rst +++ b/Doc/library/runpy.rst @@ -28,6 +28,9 @@ .. function:: run_module(mod_name, init_globals=None, run_name=None, alter_sys=False) + .. index:: + module: __main__ + Execute the code of the specified module and return the resulting module globals dictionary. The module's code is first located using the standard import mechanism (refer to :pep:`302` for details) and then executed in a @@ -87,6 +90,9 @@ .. function:: run_path(file_path, init_globals=None, run_name=None) + .. index:: + module: __main__ + Execute the code at the named filesystem location and return the resulting module globals dictionary. As with a script name supplied to the CPython command line, the supplied path may refer to a Python source file, a diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst --- a/Doc/library/stat.rst +++ b/Doc/library/stat.rst @@ -126,7 +126,7 @@ if __name__ == '__main__': walktree(sys.argv[1], visitfile) -An additional utility function is provided to covert a file's mode in a human +An additional utility function is provided to convert a file's mode in a human readable string: .. function:: filemode(mode) @@ -399,3 +399,29 @@ The file is a snapshot file. See the \*BSD or Mac OS systems man page :manpage:`chflags(2)` for more information. + +On Windows, the following file attribute constants are available for use when +testing bits in the ``st_file_attributes`` member returned by :func:`os.stat`. +See the `Windows API documentation +`_ +for more detail on the meaning of these constants. + +.. data:: FILE_ATTRIBUTE_ARCHIVE + FILE_ATTRIBUTE_COMPRESSED + FILE_ATTRIBUTE_DEVICE + FILE_ATTRIBUTE_DIRECTORY + FILE_ATTRIBUTE_ENCRYPTED + FILE_ATTRIBUTE_HIDDEN + FILE_ATTRIBUTE_INTEGRITY_STREAM + FILE_ATTRIBUTE_NORMAL + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED + FILE_ATTRIBUTE_NO_SCRUB_DATA + FILE_ATTRIBUTE_OFFLINE + FILE_ATTRIBUTE_READONLY + FILE_ATTRIBUTE_REPARSE_POINT + FILE_ATTRIBUTE_SPARSE_FILE + FILE_ATTRIBUTE_SYSTEM + FILE_ATTRIBUTE_TEMPORARY + FILE_ATTRIBUTE_VIRTUAL + + .. versionadded:: 3.5 diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -176,6 +176,15 @@ network objects from existing addresses (contributed by Peter Moody and Antoine Pitrou in :issue:`16531`). +os +-- + +* :class:`os.stat_result` now has a ``st_file_attributes`` field on Windows, + containing the ``dwFileAttributes`` member of the + ``BY_HANDLE_FILE_INFORMATION`` structure returned by + ``GetFileInformationByHandle()`` (contributed by Ben Hoyt in + :issue:`21719`). + shutil ------ @@ -304,6 +313,12 @@ or :exc:`ssl.SSLWantWriteError` on a non-blocking socket if the operation would block. Previously, it would return 0. See :issue:`20951`. +* The ``__name__`` attribute of generator is now set from the function name, + instead of being set from the code name. Use ``gen.gi_code.co_name`` to + retrieve the code name. Generators also have a new ``__qualname__`` + attribute, the qualified name, which is now used for the representation + of a generator (``repr(gen)``). See :issue:`21205`. + Changes in the C API -------------------- diff --git a/Include/genobject.h b/Include/genobject.h --- a/Include/genobject.h +++ b/Include/genobject.h @@ -25,6 +25,12 @@ /* List of weak reference. */ PyObject *gi_weakreflist; + + /* Name of the generator. */ + PyObject *gi_name; + + /* Qualified name of the generator. */ + PyObject *gi_qualname; } PyGenObject; PyAPI_DATA(PyTypeObject) PyGen_Type; @@ -33,6 +39,8 @@ #define PyGen_CheckExact(op) (Py_TYPE(op) == &PyGen_Type) PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *); +PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *, + PyObject *name, PyObject *qualname); PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *); PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); PyObject *_PyGen_Send(PyGenObject *, PyObject *); 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 @@ -17,6 +17,7 @@ import collections import concurrent.futures import heapq +import inspect import logging import socket import subprocess @@ -37,6 +38,15 @@ _MAX_WORKERS = 5 +def _format_handle(handle): + cb = handle._callback + if inspect.ismethod(cb) and isinstance(cb.__self__, tasks.Task): + # format the task + return repr(cb.__self__) + else: + return str(handle) + + class _StopError(BaseException): """Raised to stop the event loop.""" @@ -128,6 +138,9 @@ self._clock_resolution = time.get_clock_info('monotonic').resolution self._exception_handler = None self._debug = False + # In debug mode, if the execution of a callback or a step of a task + # exceed this duration in seconds, the slow callback/task is logged. + self.slow_callback_duration = 0.1 def __repr__(self): return ('<%s running=%s closed=%s debug=%s>' @@ -320,7 +333,7 @@ "than the current one") def call_soon_threadsafe(self, callback, *args): - """XXX""" + """Like call_soon(), but thread safe.""" handle = self._call_soon(callback, args, check_loop=False) self._write_to_self() return handle @@ -358,7 +371,17 @@ def create_connection(self, protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None): - """XXX""" + """Connect to a TCP server. + + Create a streaming transport connection to a given Internet host and + port: socket family AF_INET or socket.AF_INET6 depending on host (or + family if specified), socket type SOCK_STREAM. protocol_factory must be + a callable returning a protocol instance. + + This method is a coroutine which will try to establish the connection + in the background. When successful, the coroutine returns a + (transport, protocol) pair. + """ if server_hostname is not None and not ssl: raise ValueError('server_hostname is only meaningful with ssl') @@ -557,7 +580,12 @@ backlog=100, ssl=None, reuse_address=None): - """XXX""" + """Create a TCP server bound to host and port. + + Return an AbstractServer object which can be used to stop the service. + + This method is a coroutine. + """ if isinstance(ssl, bool): raise TypeError('ssl argument must be an SSLContext or None') if host is not None or port is not None: @@ -808,16 +836,16 @@ if logger.isEnabledFor(logging.INFO): t0 = self.time() event_list = self._selector.select(timeout) - t1 = self.time() - if t1-t0 >= 1: + dt = self.time() - t0 + if dt >= 1: level = logging.INFO else: level = logging.DEBUG if timeout is not None: logger.log(level, 'poll %.3f took %.3f seconds', - timeout, t1-t0) + timeout, dt) else: - logger.log(level, 'poll took %.3f seconds', t1-t0) + logger.log(level, 'poll took %.3f seconds', dt) else: event_list = self._selector.select(timeout) self._process_events(event_list) @@ -840,7 +868,16 @@ ntodo = len(self._ready) for i in range(ntodo): handle = self._ready.popleft() - if not handle._cancelled: + if handle._cancelled: + continue + if self._debug: + t0 = self.time() + handle._run() + dt = self.time() - t0 + if dt >= self.slow_callback_duration: + logger.warning('Executing %s took %.3f seconds', + _format_handle(handle), dt) + else: handle._run() handle = None # Needed to break cycles when an exception occurs. diff --git a/Lib/asyncio/queues.py b/Lib/asyncio/queues.py --- a/Lib/asyncio/queues.py +++ b/Lib/asyncio/queues.py @@ -105,7 +105,7 @@ if self._maxsize <= 0: return False else: - return self.qsize() == self._maxsize + return self.qsize() >= self._maxsize @coroutine def put(self, item): @@ -126,7 +126,7 @@ self._put(item) getter.set_result(self._get()) - elif self._maxsize > 0 and self._maxsize == self.qsize(): + elif self._maxsize > 0 and self._maxsize <= self.qsize(): waiter = futures.Future(loop=self._loop) self._putters.append((item, waiter)) @@ -152,7 +152,7 @@ self._put(item) getter.set_result(self._get()) - elif self._maxsize > 0 and self._maxsize == self.qsize(): + elif self._maxsize > 0 and self._maxsize <= self.qsize(): raise QueueFull else: self._put(item) 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 @@ -83,10 +83,15 @@ self.add_reader(self._ssock.fileno(), self._read_from_self) def _read_from_self(self): - try: - self._ssock.recv(1) - except (BlockingIOError, InterruptedError): - pass + while True: + try: + data = self._ssock.recv(4096) + if not data: + break + except InterruptedError: + continue + except BlockingIOError: + break def _write_to_self(self): # This may be called from a different thread, possibly after @@ -221,7 +226,14 @@ return False def sock_recv(self, sock, n): - """XXX""" + """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 is a coroutine. + """ fut = futures.Future(loop=self) self._sock_recv(fut, False, sock, n) return fut @@ -248,7 +260,16 @@ fut.set_result(data) def sock_sendall(self, sock, data): - """XXX""" + """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 processed by the receiving end of the connection. + + This method is a coroutine. + """ fut = futures.Future(loop=self) if data: self._sock_sendall(fut, False, sock, data) @@ -280,7 +301,16 @@ self.add_writer(fd, self._sock_sendall, fut, True, sock, data) def sock_connect(self, sock, address): - """XXX""" + """Connect to a remote socket at address. + + The address must be already resolved to avoid the trap of hanging the + entire event loop when the address requires doing a DNS lookup. For + example, it must be an IP address, not an hostname, for AF_INET and + AF_INET6 address families. Use getaddrinfo() to resolve the hostname + asynchronously. + + This method is a coroutine. + """ fut = futures.Future(loop=self) try: base_events._check_resolved_address(sock, address) @@ -313,7 +343,15 @@ fut.set_result(None) def sock_accept(self, sock): - """XXX""" + """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 is a coroutine. + """ fut = futures.Future(loop=self) self._sock_accept(fut, False, sock) return fut diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -32,12 +32,12 @@ _DEBUG = (not sys.flags.ignore_environment and bool(os.environ.get('PYTHONASYNCIODEBUG'))) +_PY35 = (sys.version_info >= (3, 5)) + class CoroWrapper: # Wrapper for coroutine in _DEBUG mode. - __slots__ = ['gen', 'func', '__name__', '__doc__', '__weakref__'] - def __init__(self, gen, func): assert inspect.isgenerator(gen), gen self.gen = gen @@ -111,8 +111,10 @@ @functools.wraps(func) def wrapper(*args, **kwds): w = CoroWrapper(coro(*args, **kwds), func) - w.__name__ = coro.__name__ - w.__doc__ = coro.__doc__ + w.__name__ = func.__name__ + if _PY35: + w.__qualname__ = func.__qualname__ + w.__doc__ = func.__doc__ return w wrapper._is_coroutine = True # For iscoroutinefunction(). @@ -190,7 +192,7 @@ i = len(res) text = self._coro.__name__ coro = self._coro - if inspect.isgenerator(coro): + if iscoroutine(coro): filename = coro.gi_code.co_filename if coro.gi_frame is not None: text += ' at %s:%s' % (filename, coro.gi_frame.f_lineno) 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 @@ -11,6 +11,7 @@ import tempfile import threading import time +import unittest from unittest import mock from http.server import HTTPServer @@ -379,3 +380,20 @@ if source is None: raise ValueError("unable to get the source of %r" % (func,)) return source + + +class TestCase(unittest.TestCase): + def set_event_loop(self, loop, *, cleanup=True): + assert loop is not None + # ensure that the event loop is passed explicitly in asyncio + events.set_event_loop(None) + if cleanup: + self.addCleanup(loop.close) + + def new_test_loop(self, gen=None): + loop = TestLoop(gen) + self.set_event_loop(loop) + return loop + + def tearDown(self): + events.set_event_loop(None) diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py --- a/Lib/distutils/command/upload.py +++ b/Lib/distutils/command/upload.py @@ -12,7 +12,7 @@ from base64 import standard_b64encode from urllib.request import urlopen, Request, HTTPError from urllib.parse import urlparse -from distutils.errors import DistutilsOptionError +from distutils.errors import DistutilsError, DistutilsOptionError from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log @@ -184,7 +184,7 @@ reason = result.msg except OSError as e: self.announce(str(e), log.ERROR) - return + raise except HTTPError as e: status = e.code reason = e.msg @@ -193,8 +193,9 @@ self.announce('Server response (%s): %s' % (status, reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (status, reason), - log.ERROR) + msg = 'Upload failed (%s): %s' % (status, reason) + self.announce(msg, log.ERROR) + raise DistutilsError(msg) if self.show_response: text = self._read_pypi_response(result) msg = '\n'.join(('-' * 75, text, '-' * 75)) diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py --- a/Lib/distutils/tests/test_upload.py +++ b/Lib/distutils/tests/test_upload.py @@ -6,6 +6,7 @@ from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution +from distutils.errors import DistutilsError from distutils.log import INFO from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase @@ -41,13 +42,14 @@ class FakeOpen(object): - def __init__(self, url): + def __init__(self, url, msg=None, code=None): self.url = url if not isinstance(url, str): self.req = url else: self.req = None - self.msg = 'OK' + self.msg = msg or 'OK' + self.code = code or 200 def getheader(self, name, default=None): return { @@ -58,7 +60,7 @@ return b'xyzzy' def getcode(self): - return 200 + return self.code class uploadTestCase(PyPIRCCommandTestCase): @@ -68,13 +70,15 @@ self.old_open = upload_mod.urlopen upload_mod.urlopen = self._urlopen self.last_open = None + self.next_msg = None + self.next_code = None def tearDown(self): upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() def _urlopen(self, url): - self.last_open = FakeOpen(url) + self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) return self.last_open def test_finalize_options(self): @@ -135,6 +139,10 @@ results = self.get_logs(INFO) self.assertIn('xyzzy\n', results[-1]) + def test_upload_fails(self): + self.next_msg = "Not Found" + self.next_code = 404 + self.assertRaises(DistutilsError, self.test_upload) def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/Lib/heapq.py b/Lib/heapq.py --- a/Lib/heapq.py +++ b/Lib/heapq.py @@ -311,16 +311,6 @@ heap[pos] = newitem _siftdown_max(heap, startpos, pos) -# If available, use C implementation -try: - from _heapq import * -except ImportError: - pass -try: - from _heapq import _heapreplace_max -except ImportError: - pass - def merge(*iterables, key=None, reverse=False): '''Merge multiple sorted inputs into a single sorted output. @@ -474,7 +464,7 @@ Equivalent to: sorted(iterable, key=key)[:n] """ - # Short-cut for n==1 is to use min() when len(iterable)>0 + # Short-cut for n==1 is to use min() if n == 1: it = iter(iterable) sentinel = object() @@ -537,7 +527,7 @@ Equivalent to: sorted(iterable, key=key, reverse=True)[:n] """ - # Short-cut for n==1 is to use max() when len(iterable)>0 + # Short-cut for n==1 is to use max() if n == 1: it = iter(iterable) sentinel = object() @@ -592,6 +582,24 @@ result.sort(reverse=True) return [r[2] for r in result] +# If available, use C implementation +try: + from _heapq import * +except ImportError: + pass +try: + from _heapq import _heapreplace_max +except ImportError: + pass +try: + from _heapq import _heapify_max +except ImportError: + pass +try: + from _heapq import _heappop_max +except ImportError: + pass + if __name__ == "__main__": diff --git a/Lib/http/server.py b/Lib/http/server.py --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -977,7 +977,7 @@ (and the next character is a '/' or the end of the string). """ - collapsed_path = _url_collapse_path(self.path) + collapsed_path = _url_collapse_path(urllib.parse.unquote(self.path)) dir_sep = collapsed_path.find('/', 1) head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] if head in self.cgi_directories: diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py --- a/Lib/idlelib/HyperParser.py +++ b/Lib/idlelib/HyperParser.py @@ -1,11 +1,8 @@ -""" -HyperParser -=========== -This module defines the HyperParser class, which provides advanced parsing -abilities for the ParenMatch and other extensions. -The HyperParser uses PyParser. PyParser is intended mostly to give information -on the proper indentation of code. HyperParser gives some information on the -structure of code, used by extensions to help the user. +"""Provide advanced parsing abilities for ParenMatch and other extensions. + +HyperParser uses PyParser. PyParser mostly gives information on the +proper indentation of code. HyperParser gives additional information on +the structure of code. """ import string @@ -15,9 +12,7 @@ class HyperParser: def __init__(self, editwin, index): - """Initialize the HyperParser to analyze the surroundings of the given - index. - """ + "To initialize, analyze the surroundings of the given index." self.editwin = editwin self.text = text = editwin.text @@ -33,9 +28,10 @@ startat = max(lno - context, 1) startatindex = repr(startat) + ".0" stopatindex = "%d.end" % lno - # We add the newline because PyParse requires a newline at end. - # We add a space so that index won't be at end of line, so that - # its status will be the same as the char before it, if should. + # We add the newline because PyParse requires a newline + # at end. We add a space so that index won't be at end + # of line, so that its status will be the same as the + # char before it, if should. parser.set_str(text.get(startatindex, stopatindex)+' \n') bod = parser.find_good_parse_start( editwin._build_char_in_string_func(startatindex)) @@ -49,122 +45,131 @@ else: startatindex = "1.0" stopatindex = "%d.end" % lno - # We add the newline because PyParse requires a newline at end. - # We add a space so that index won't be at end of line, so that - # its status will be the same as the char before it, if should. + # We add the newline because PyParse requires it. We add a + # space so that index won't be at end of line, so that its + # status will be the same as the char before it, if should. parser.set_str(text.get(startatindex, stopatindex)+' \n') parser.set_lo(0) - # We want what the parser has, except for the last newline and space. + # We want what the parser has, minus the last newline and space. self.rawtext = parser.str[:-2] - # As far as I can see, parser.str preserves the statement we are in, - # so that stopatindex can be used to synchronize the string with the - # text box indices. + # Parser.str apparently preserves the statement we are in, so + # that stopatindex can be used to synchronize the string with + # the text box indices. self.stopatindex = stopatindex self.bracketing = parser.get_last_stmt_bracketing() - # find which pairs of bracketing are openers. These always correspond - # to a character of rawtext. - self.isopener = [i>0 and self.bracketing[i][1] > self.bracketing[i-1][1] + # find which pairs of bracketing are openers. These always + # correspond to a character of rawtext. + self.isopener = [i>0 and self.bracketing[i][1] > + self.bracketing[i-1][1] for i in range(len(self.bracketing))] self.set_index(index) def set_index(self, index): - """Set the index to which the functions relate. Note that it must be - in the same statement. + """Set the index to which the functions relate. + + The index must be in the same statement. """ - indexinrawtext = \ - len(self.rawtext) - len(self.text.get(index, self.stopatindex)) + indexinrawtext = (len(self.rawtext) - + len(self.text.get(index, self.stopatindex))) if indexinrawtext < 0: - raise ValueError("The index given is before the analyzed statement") + raise ValueError("Index %s precedes the analyzed statement" + % index) self.indexinrawtext = indexinrawtext # find the rightmost bracket to which index belongs self.indexbracket = 0 - while self.indexbracket < len(self.bracketing)-1 and \ - self.bracketing[self.indexbracket+1][0] < self.indexinrawtext: + while (self.indexbracket < len(self.bracketing)-1 and + self.bracketing[self.indexbracket+1][0] < self.indexinrawtext): self.indexbracket += 1 - if self.indexbracket < len(self.bracketing)-1 and \ - self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and \ - not self.isopener[self.indexbracket+1]: + if (self.indexbracket < len(self.bracketing)-1 and + self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and + not self.isopener[self.indexbracket+1]): self.indexbracket += 1 def is_in_string(self): - """Is the index given to the HyperParser is in a string?""" + """Is the index given to the HyperParser in a string?""" # The bracket to which we belong should be an opener. # If it's an opener, it has to have a character. - return self.isopener[self.indexbracket] and \ - self.rawtext[self.bracketing[self.indexbracket][0]] in ('"', "'") + return (self.isopener[self.indexbracket] and + self.rawtext[self.bracketing[self.indexbracket][0]] + in ('"', "'")) def is_in_code(self): - """Is the index given to the HyperParser is in a normal code?""" - return not self.isopener[self.indexbracket] or \ - self.rawtext[self.bracketing[self.indexbracket][0]] not in \ - ('#', '"', "'") + """Is the index given to the HyperParser in normal code?""" + return (not self.isopener[self.indexbracket] or + self.rawtext[self.bracketing[self.indexbracket][0]] + not in ('#', '"', "'")) def get_surrounding_brackets(self, openers='([{', mustclose=False): - """If the index given to the HyperParser is surrounded by a bracket - defined in openers (or at least has one before it), return the - indices of the opening bracket and the closing bracket (or the - end of line, whichever comes first). - If it is not surrounded by brackets, or the end of line comes before - the closing bracket and mustclose is True, returns None. + """Return bracket indexes or None. + + If the index given to the HyperParser is surrounded by a + bracket defined in openers (or at least has one before it), + return the indices of the opening bracket and the closing + bracket (or the end of line, whichever comes first). + + If it is not surrounded by brackets, or the end of line comes + before the closing bracket and mustclose is True, returns None. """ + bracketinglevel = self.bracketing[self.indexbracket][1] before = self.indexbracket - while not self.isopener[before] or \ - self.rawtext[self.bracketing[before][0]] not in openers or \ - self.bracketing[before][1] > bracketinglevel: + while (not self.isopener[before] or + self.rawtext[self.bracketing[before][0]] not in openers or + self.bracketing[before][1] > bracketinglevel): before -= 1 if before < 0: return None bracketinglevel = min(bracketinglevel, self.bracketing[before][1]) after = self.indexbracket + 1 - while after < len(self.bracketing) and \ - self.bracketing[after][1] >= bracketinglevel: + while (after < len(self.bracketing) and + self.bracketing[after][1] >= bracketinglevel): after += 1 beforeindex = self.text.index("%s-%dc" % (self.stopatindex, len(self.rawtext)-self.bracketing[before][0])) - if after >= len(self.bracketing) or \ - self.bracketing[after][0] > len(self.rawtext): + if (after >= len(self.bracketing) or + self.bracketing[after][0] > len(self.rawtext)): if mustclose: return None afterindex = self.stopatindex else: - # We are after a real char, so it is a ')' and we give the index - # before it. - afterindex = self.text.index("%s-%dc" % - (self.stopatindex, + # We are after a real char, so it is a ')' and we give the + # index before it. + afterindex = self.text.index( + "%s-%dc" % (self.stopatindex, len(self.rawtext)-(self.bracketing[after][0]-1))) return beforeindex, afterindex - # This string includes all chars that may be in a white space + # Ascii chars that may be in a white space _whitespace_chars = " \t\n\\" - # This string includes all chars that may be in an identifier + # Ascii chars that may be in an identifier _id_chars = string.ascii_letters + string.digits + "_" - # This string includes all chars that may be the first char of an identifier + # Ascii chars that may be the first char of an identifier _id_first_chars = string.ascii_letters + "_" - # Given a string and pos, return the number of chars in the identifier - # which ends at pos, or 0 if there is no such one. Saved words are not - # identifiers. + # Given a string and pos, return the number of chars in the + # identifier which ends at pos, or 0 if there is no such one. Saved + # words are not identifiers. def _eat_identifier(self, str, limit, pos): i = pos while i > limit and str[i-1] in self._id_chars: i -= 1 - if i < pos and (str[i] not in self._id_first_chars or \ - keyword.iskeyword(str[i:pos])): + if (i < pos and (str[i] not in self._id_first_chars or + (keyword.iskeyword(str[i:pos]) and + str[i:pos] not in {'None', 'False', 'True'}))): i = pos return pos - i def get_expression(self): - """Return a string with the Python expression which ends at the given - index, which is empty if there is no real one. + """Return a string with the Python expression which ends at the + given index, which is empty if there is no real one. """ if not self.is_in_code(): - raise ValueError("get_expression should only be called if index "\ - "is inside a code.") + raise ValueError("get_expression should only be called" + "if index is inside a code.") rawtext = self.rawtext bracketing = self.bracketing @@ -177,20 +182,20 @@ postdot_phase = True while 1: - # Eat whitespaces, comments, and if postdot_phase is False - one dot + # Eat whitespaces, comments, and if postdot_phase is False - a dot while 1: if pos>brck_limit and rawtext[pos-1] in self._whitespace_chars: # Eat a whitespace pos -= 1 - elif not postdot_phase and \ - pos > brck_limit and rawtext[pos-1] == '.': + elif (not postdot_phase and + pos > brck_limit and rawtext[pos-1] == '.'): # Eat a dot pos -= 1 postdot_phase = True - # The next line will fail if we are *inside* a comment, but we - # shouldn't be. - elif pos == brck_limit and brck_index > 0 and \ - rawtext[bracketing[brck_index-1][0]] == '#': + # The next line will fail if we are *inside* a comment, + # but we shouldn't be. + elif (pos == brck_limit and brck_index > 0 and + rawtext[bracketing[brck_index-1][0]] == '#'): # Eat a comment brck_index -= 2 brck_limit = bracketing[brck_index][0] @@ -200,8 +205,8 @@ break if not postdot_phase: - # We didn't find a dot, so the expression end at the last - # identifier pos. + # We didn't find a dot, so the expression end at the + # last identifier pos. break ret = self._eat_identifier(rawtext, brck_limit, pos) @@ -209,13 +214,13 @@ # There is an identifier to eat pos = pos - ret last_identifier_pos = pos - # Now, in order to continue the search, we must find a dot. + # Now, to continue the search, we must find a dot. postdot_phase = False # (the loop continues now) elif pos == brck_limit: - # We are at a bracketing limit. If it is a closing bracket, - # eat the bracket, otherwise, stop the search. + # We are at a bracketing limit. If it is a closing + # bracket, eat the bracket, otherwise, stop the search. level = bracketing[brck_index][1] while brck_index > 0 and bracketing[brck_index-1][1] > level: brck_index -= 1 @@ -244,3 +249,8 @@ break return rawtext[last_identifier_pos:self.indexinrawtext] + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_hyperparser', verbosity=2) diff --git a/Lib/idlelib/ParenMatch.py b/Lib/idlelib/ParenMatch.py --- a/Lib/idlelib/ParenMatch.py +++ b/Lib/idlelib/ParenMatch.py @@ -90,7 +90,8 @@ self.set_timeout = self.set_timeout_none def flash_paren_event(self, event): - indices = HyperParser(self.editwin, "insert").get_surrounding_brackets() + indices = (HyperParser(self.editwin, "insert") + .get_surrounding_brackets()) if indices is None: self.warn_mismatched() return @@ -167,6 +168,11 @@ # associate a counter with an event; only disable the "paren" # tag if the event is for the most recent timer. self.counter += 1 - self.editwin.text_frame.after(self.FLASH_DELAY, - lambda self=self, c=self.counter: \ - self.handle_restore_timer(c)) + self.editwin.text_frame.after( + self.FLASH_DELAY, + lambda self=self, c=self.counter: self.handle_restore_timer(c)) + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_hyperparser.py b/Lib/idlelib/idle_test/test_hyperparser.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_hyperparser.py @@ -0,0 +1,191 @@ +"""Unittest for idlelib.HyperParser""" +import unittest +from test.support import requires +from tkinter import Tk, Text +from idlelib.EditorWindow import EditorWindow +from idlelib.HyperParser import HyperParser + +class DummyEditwin: + def __init__(self, text): + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + self.num_context_lines = 50, 500, 1000 + + _build_char_in_string_func = EditorWindow._build_char_in_string_func + is_char_in_string = EditorWindow.is_char_in_string + + +class HyperParserTest(unittest.TestCase): + code = ( + '"""This is a module docstring"""\n' + '# this line is a comment\n' + 'x = "this is a string"\n' + "y = 'this is also a string'\n" + 'l = [i for i in range(10)]\n' + 'm = [py*py for # comment\n' + ' py in l]\n' + 'x.__len__\n' + "z = ((r'asdf')+('a')))\n" + '[x for x in\n' + 'for = False\n' + ) + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.text = Text(cls.root) + cls.editwin = DummyEditwin(cls.text) + + @classmethod + def tearDownClass(cls): + del cls.text, cls.editwin + cls.root.destroy() + del cls.root + + def setUp(self): + self.text.insert('insert', self.code) + + def tearDown(self): + self.text.delete('1.0', 'end') + self.editwin.context_use_ps1 = True + + def get_parser(self, index): + """ + Return a parser object with index at 'index' + """ + return HyperParser(self.editwin, index) + + def test_init(self): + """ + test corner cases in the init method + """ + with self.assertRaises(ValueError) as ve: + self.text.tag_add('console', '1.0', '1.end') + p = self.get_parser('1.5') + self.assertIn('precedes', str(ve.exception)) + + # test without ps1 + self.editwin.context_use_ps1 = False + + # number of lines lesser than 50 + p = self.get_parser('end') + self.assertEqual(p.rawtext, self.text.get('1.0', 'end')) + + # number of lines greater than 50 + self.text.insert('end', self.text.get('1.0', 'end')*4) + p = self.get_parser('54.5') + + def test_is_in_string(self): + get = self.get_parser + + p = get('1.0') + self.assertFalse(p.is_in_string()) + p = get('1.4') + self.assertTrue(p.is_in_string()) + p = get('2.3') + self.assertFalse(p.is_in_string()) + p = get('3.3') + self.assertFalse(p.is_in_string()) + p = get('3.7') + self.assertTrue(p.is_in_string()) + p = get('4.6') + self.assertTrue(p.is_in_string()) + + def test_is_in_code(self): + get = self.get_parser + + p = get('1.0') + self.assertTrue(p.is_in_code()) + p = get('1.1') + self.assertFalse(p.is_in_code()) + p = get('2.5') + self.assertFalse(p.is_in_code()) + p = get('3.4') + self.assertTrue(p.is_in_code()) + p = get('3.6') + self.assertFalse(p.is_in_code()) + p = get('4.14') + self.assertFalse(p.is_in_code()) + + def test_get_surrounding_bracket(self): + get = self.get_parser + + def without_mustclose(parser): + # a utility function to get surrounding bracket + # with mustclose=False + return parser.get_surrounding_brackets(mustclose=False) + + def with_mustclose(parser): + # a utility function to get surrounding bracket + # with mustclose=True + return parser.get_surrounding_brackets(mustclose=True) + + p = get('3.2') + self.assertIsNone(with_mustclose(p)) + self.assertIsNone(without_mustclose(p)) + + p = get('5.6') + self.assertTupleEqual(without_mustclose(p), ('5.4', '5.25')) + self.assertTupleEqual(without_mustclose(p), with_mustclose(p)) + + p = get('5.23') + self.assertTupleEqual(without_mustclose(p), ('5.21', '5.24')) + self.assertTupleEqual(without_mustclose(p), with_mustclose(p)) + + p = get('6.15') + self.assertTupleEqual(without_mustclose(p), ('6.4', '6.end')) + self.assertIsNone(with_mustclose(p)) + + p = get('9.end') + self.assertIsNone(with_mustclose(p)) + self.assertIsNone(without_mustclose(p)) + + def test_get_expression(self): + get = self.get_parser + + p = get('4.2') + self.assertEqual(p.get_expression(), 'y ') + + p = get('4.7') + with self.assertRaises(ValueError) as ve: + p.get_expression() + self.assertIn('is inside a code', str(ve.exception)) + + p = get('5.25') + self.assertEqual(p.get_expression(), 'range(10)') + + p = get('6.7') + self.assertEqual(p.get_expression(), 'py') + + p = get('6.8') + self.assertEqual(p.get_expression(), '') + + p = get('7.9') + self.assertEqual(p.get_expression(), 'py') + + p = get('8.end') + self.assertEqual(p.get_expression(), 'x.__len__') + + p = get('9.13') + self.assertEqual(p.get_expression(), "r'asdf'") + + p = get('9.17') + with self.assertRaises(ValueError) as ve: + p.get_expression() + self.assertIn('is inside a code', str(ve.exception)) + + p = get('10.0') + self.assertEqual(p.get_expression(), '') + + p = get('11.3') + self.assertEqual(p.get_expression(), '') + + p = get('11.11') + self.assertEqual(p.get_expression(), 'False') + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_parenmatch.py b/Lib/idlelib/idle_test/test_parenmatch.py new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_parenmatch.py @@ -0,0 +1,109 @@ +"""Test idlelib.ParenMatch.""" +# This must currently be a gui test because ParenMatch methods use +# several text methods not defined on idlelib.idle_test.mock_tk.Text. + +import unittest +from unittest.mock import Mock +from test.support import requires +from tkinter import Tk, Text +from idlelib.ParenMatch import ParenMatch + +class DummyEditwin: + def __init__(self, text): + self.text = text + self.indentwidth = 8 + self.tabwidth = 8 + self.context_use_ps1 = True + + +class ParenMatchTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.text = Text(cls.root) + cls.editwin = DummyEditwin(cls.text) + cls.editwin.text_frame = Mock() + + @classmethod + def tearDownClass(cls): + del cls.text, cls.editwin + cls.root.destroy() + del cls.root + + def tearDown(self): + self.text.delete('1.0', 'end') + + def test_paren_expression(self): + """ + Test ParenMatch with 'expression' style. + """ + text = self.text + pm = ParenMatch(self.editwin) + pm.set_style('expression') + + text.insert('insert', 'def foobar(a, b') + pm.flash_paren_event('event') + self.assertIn('<>', text.event_info()) + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.15')) + text.insert('insert', ')') + pm.restore_event() + self.assertNotIn('<>', text.event_info()) + self.assertEqual(text.tag_prevrange('paren', 'end'), ()) + + # paren_closed_event can only be tested as below + pm.paren_closed_event('event') + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.16')) + + def test_paren_default(self): + """ + Test ParenMatch with 'default' style. + """ + text = self.text + pm = ParenMatch(self.editwin) + pm.set_style('default') + + text.insert('insert', 'def foobar(a, b') + pm.flash_paren_event('event') + self.assertIn('<>', text.event_info()) + self.assertTupleEqual(text.tag_prevrange('paren', 'end'), + ('1.10', '1.11')) + text.insert('insert', ')') + pm.restore_event() + self.assertNotIn('<>', text.event_info()) + self.assertEqual(text.tag_prevrange('paren', 'end'), ()) + + def test_paren_corner(self): + """ + Test corner cases in flash_paren_event and paren_closed_event. + + These cases force conditional expression and alternate paths. + """ + text = self.text + pm = ParenMatch(self.editwin) + + text.insert('insert', '# this is a commen)') + self.assertIsNone(pm.paren_closed_event('event')) + + text.insert('insert', '\ndef') + self.assertIsNone(pm.flash_paren_event('event')) + self.assertIsNone(pm.paren_closed_event('event')) + + text.insert('insert', ' a, *arg)') + self.assertIsNone(pm.paren_closed_event('event')) + + def test_handle_restore_timer(self): + pm = ParenMatch(self.editwin) + pm.restore_event = Mock() + pm.handle_restore_timer(0) + self.assertTrue(pm.restore_event.called) + pm.restore_event.reset_mock() + pm.handle_restore_timer(1) + self.assertFalse(pm.restore_event.called) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -312,11 +312,12 @@ When topdown is true, the caller can modify the dirnames list in-place (e.g., via del or slice assignment), and walk will only recurse into the - subdirectories whose names remain in dirnames; this can be used to prune - the search, or to impose a specific order of visiting. Modifying - dirnames when topdown is false is ineffective, since the directories in - dirnames have already been generated by the time dirnames itself is - generated. + subdirectories whose names remain in dirnames; this can be used to prune the + search, or to impose a specific order of visiting. Modifying dirnames when + topdown is false is ineffective, since the directories in dirnames have + already been generated by the time dirnames itself is generated. No matter + the value of topdown, the list of subdirectories is retrieved before the + tuples for the directory and its subdirectories are generated. By default errors from the os.listdir() call are ignored. If optional arg 'onerror' is specified, it should be a function; it @@ -344,6 +345,7 @@ print("bytes in", len(files), "non-directory files") if 'CVS' in dirs: dirs.remove('CVS') # don't visit CVS directories + """ islink, join, isdir = path.islink, path.join, path.isdir diff --git a/Lib/posixpath.py b/Lib/posixpath.py --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -48,7 +48,6 @@ def normcase(s): """Normalize case of pathname. Has no effect under Posix""" - # TODO: on Mac OS X, this should really return s.lower(). if not isinstance(s, (bytes, str)): raise TypeError("normcase() argument must be str or bytes, " "not '{}'".format(s.__class__.__name__)) diff --git a/Lib/socketserver.py b/Lib/socketserver.py --- a/Lib/socketserver.py +++ b/Lib/socketserver.py @@ -539,35 +539,39 @@ def collect_children(self): """Internal routine to wait for children that have exited.""" - if self.active_children is None: return + if self.active_children is None: + return + + # If we're above the max number of children, wait and reap them until + # we go back below threshold. Note that we use waitpid(-1) below to be + # able to collect children in size() syscalls instead + # of size(): the downside is that this might reap children + # which we didn't spawn, which is why we only resort to this when we're + # above max_children. while len(self.active_children) >= self.max_children: - # XXX: This will wait for any child process, not just ones - # spawned by this library. This could confuse other - # libraries that expect to be able to wait for their own - # children. try: - pid, status = os.waitpid(0, 0) + pid, _ = os.waitpid(-1, 0) + self.active_children.discard(pid) + except InterruptedError: + pass + except ChildProcessError: + # we don't have any children, we're done + self.active_children.clear() except OSError: - pid = None - if pid not in self.active_children: continue - self.active_children.remove(pid) + break - # XXX: This loop runs more system calls than it ought - # to. There should be a way to put the active_children into a - # process group and then use os.waitpid(-pgid) to wait for any - # of that set, but I couldn't find a way to allocate pgids - # that couldn't collide. - for child in self.active_children: + # Now reap all defunct children. + for pid in self.active_children.copy(): try: - pid, status = os.waitpid(child, os.WNOHANG) + pid, _ = os.waitpid(pid, os.WNOHANG) + # if the child hasn't exited yet, pid will be 0 and ignored by + # discard() below + self.active_children.discard(pid) + except ChildProcessError: + # someone else reaped it + self.active_children.discard(pid) except OSError: - pid = None - if not pid: continue - try: - self.active_children.remove(pid) - except ValueError as e: - raise ValueError('%s. x=%d and list=%r' % (e.message, pid, - self.active_children)) + pass def handle_timeout(self): """Wait for zombies after self.timeout seconds of inactivity. @@ -589,8 +593,8 @@ if pid: # Parent process if self.active_children is None: - self.active_children = [] - self.active_children.append(pid) + self.active_children = set() + self.active_children.add(pid) self.close_request(request) return else: diff --git a/Lib/stat.py b/Lib/stat.py --- a/Lib/stat.py +++ b/Lib/stat.py @@ -148,6 +148,29 @@ perm.append("-") return "".join(perm) + +# Windows FILE_ATTRIBUTE constants for interpreting os.stat()'s +# "st_file_attributes" member + +FILE_ATTRIBUTE_ARCHIVE = 32 +FILE_ATTRIBUTE_COMPRESSED = 2048 +FILE_ATTRIBUTE_DEVICE = 64 +FILE_ATTRIBUTE_DIRECTORY = 16 +FILE_ATTRIBUTE_ENCRYPTED = 16384 +FILE_ATTRIBUTE_HIDDEN = 2 +FILE_ATTRIBUTE_INTEGRITY_STREAM = 32768 +FILE_ATTRIBUTE_NORMAL = 128 +FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192 +FILE_ATTRIBUTE_NO_SCRUB_DATA = 131072 +FILE_ATTRIBUTE_OFFLINE = 4096 +FILE_ATTRIBUTE_READONLY = 1 +FILE_ATTRIBUTE_REPARSE_POINT = 1024 +FILE_ATTRIBUTE_SPARSE_FILE = 512 +FILE_ATTRIBUTE_SYSTEM = 4 +FILE_ATTRIBUTE_TEMPORARY = 256 +FILE_ATTRIBUTE_VIRTUAL = 65536 + + # If available, use C implementation try: from _stat import * diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py --- a/Lib/test/script_helper.py +++ b/Lib/test/script_helper.py @@ -155,8 +155,8 @@ script_name = make_script(zip_dir, script_basename, source) unlink.append(script_name) if compiled: - init_name = py_compile(init_name, doraise=True) - script_name = py_compile(script_name, doraise=True) + init_name = py_compile.compile(init_name, doraise=True) + script_name = py_compile.compile(script_name, doraise=True) unlink.extend((init_name, script_name)) pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)] script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name)) 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 @@ -19,12 +19,12 @@ PY34 = sys.version_info >= (3, 4) -class BaseEventLoopTests(unittest.TestCase): +class BaseEventLoopTests(test_utils.TestCase): def setUp(self): self.loop = base_events.BaseEventLoop() self.loop._selector = mock.Mock() - asyncio.set_event_loop(None) + self.set_event_loop(self.loop) def test_not_implemented(self): m = mock.Mock() @@ -240,30 +240,23 @@ self.loop.set_debug(False) self.assertFalse(self.loop.get_debug()) - @mock.patch('asyncio.base_events.time') @mock.patch('asyncio.base_events.logger') - def test__run_once_logging(self, m_logger, m_time): + def test__run_once_logging(self, m_logger): + def slow_select(timeout): + time.sleep(1.0) + return [] + # Log to INFO level if timeout > 1.0 sec. - idx = -1 - data = [10.0, 10.0, 12.0, 13.0] - - def monotonic(): - nonlocal data, idx - idx += 1 - return data[idx] - - m_time.monotonic = monotonic - - self.loop._scheduled.append( - asyncio.TimerHandle(11.0, lambda: True, (), self.loop)) + self.loop._selector.select = slow_select self.loop._process_events = mock.Mock() self.loop._run_once() 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)] + def fast_select(timeout): + time.sleep(0.001) + return [] + + self.loop._selector.select = fast_select self.loop._run_once() self.assertEqual(logging.DEBUG, m_logger.log.call_args[0][0]) @@ -555,14 +548,11 @@ self.done.set_result(None) -class BaseEventLoopWithSelectorTests(unittest.TestCase): +class BaseEventLoopWithSelectorTests(test_utils.TestCase): def setUp(self): self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.set_event_loop(self.loop) @mock.patch('asyncio.base_events.socket') def test_create_connection_multiple_errors(self, m_socket): @@ -979,6 +969,34 @@ with self.assertRaises(TypeError): self.loop.run_in_executor(None, coroutine_function) + @mock.patch('asyncio.base_events.logger') + def test_log_slow_callbacks(self, m_logger): + def stop_loop_cb(loop): + loop.stop() + + @asyncio.coroutine + def stop_loop_coro(loop): + yield from () + loop.stop() + + asyncio.set_event_loop(self.loop) + self.loop.set_debug(True) + self.loop.slow_callback_duration = 0.0 + + # slow callback + self.loop.call_soon(stop_loop_cb, self.loop) + self.loop.run_forever() + fmt, *args = m_logger.warning.call_args[0] + self.assertRegex(fmt % tuple(args), + "^Executing Handle.*stop_loop_cb.* took .* seconds$") + + # slow task + asyncio.async(stop_loop_coro(self.loop), loop=self.loop) + self.loop.run_forever() + fmt, *args = m_logger.warning.call_args[0] + self.assertRegex(fmt % tuple(args), + "^Executing Task.*stop_loop_coro.* took .* seconds$") + if __name__ == '__main__': unittest.main() 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 @@ -224,7 +224,7 @@ def setUp(self): super().setUp() self.loop = self.create_event_loop() - asyncio.set_event_loop(None) + self.set_event_loop(self.loop) def tearDown(self): # just in case if we have transport close callbacks @@ -1629,14 +1629,14 @@ if sys.platform == 'win32': - class SelectEventLoopTests(EventLoopTestsMixin, unittest.TestCase): + class SelectEventLoopTests(EventLoopTestsMixin, test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop() class ProactorEventLoopTests(EventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.ProactorEventLoop() @@ -1691,7 +1691,7 @@ if hasattr(selectors, 'KqueueSelector'): class KqueueEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop( @@ -1716,7 +1716,7 @@ if hasattr(selectors, 'EpollSelector'): class EPollEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop(selectors.EpollSelector()) @@ -1724,7 +1724,7 @@ if hasattr(selectors, 'PollSelector'): class PollEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop(selectors.PollSelector()) @@ -1732,7 +1732,7 @@ # Should always exist. class SelectEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, - unittest.TestCase): + test_utils.TestCase): def create_event_loop(self): return asyncio.SelectorEventLoop(selectors.SelectSelector()) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -13,14 +13,10 @@ return f -class FutureTests(unittest.TestCase): +class FutureTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_initial_state(self): f = asyncio.Future(loop=self.loop) @@ -30,12 +26,9 @@ self.assertTrue(f.cancelled()) def test_init_constructor_default_loop(self): - try: - asyncio.set_event_loop(self.loop) - f = asyncio.Future() - self.assertIs(f._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + f = asyncio.Future() + self.assertIs(f._loop, self.loop) def test_constructor_positional(self): # Make sure Future doesn't accept a positional argument @@ -264,14 +257,10 @@ self.assertTrue(f2.cancelled()) -class FutureDoneCallbackTests(unittest.TestCase): +class FutureDoneCallbackTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def run_briefly(self): test_utils.run_briefly(self.loop) diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -17,14 +17,10 @@ RGX_REPR = re.compile(STR_RGX_REPR) -class LockTests(unittest.TestCase): +class LockTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -35,12 +31,9 @@ self.assertIs(lock._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - lock = asyncio.Lock() - self.assertIs(lock._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + lock = asyncio.Lock() + self.assertIs(lock._loop, self.loop) def test_repr(self): lock = asyncio.Lock(loop=self.loop) @@ -240,14 +233,10 @@ self.assertFalse(lock.locked()) -class EventTests(unittest.TestCase): +class EventTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -258,12 +247,9 @@ self.assertIs(ev._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - ev = asyncio.Event() - self.assertIs(ev._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + ev = asyncio.Event() + self.assertIs(ev._loop, self.loop) def test_repr(self): ev = asyncio.Event(loop=self.loop) @@ -376,14 +362,10 @@ self.assertTrue(t.result()) -class ConditionTests(unittest.TestCase): +class ConditionTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -394,12 +376,9 @@ self.assertIs(cond._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - cond = asyncio.Condition() - self.assertIs(cond._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + cond = asyncio.Condition() + self.assertIs(cond._loop, self.loop) def test_wait(self): cond = asyncio.Condition(loop=self.loop) @@ -678,14 +657,10 @@ self.assertFalse(cond.locked()) -class SemaphoreTests(unittest.TestCase): +class SemaphoreTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() def test_ctor_loop(self): loop = mock.Mock() @@ -696,12 +671,9 @@ self.assertIs(sem._loop, self.loop) def test_ctor_noloop(self): - try: - asyncio.set_event_loop(self.loop) - sem = asyncio.Semaphore() - self.assertIs(sem._loop, self.loop) - finally: - asyncio.set_event_loop(None) + asyncio.set_event_loop(self.loop) + sem = asyncio.Semaphore() + self.assertIs(sem._loop, self.loop) def test_initial_value_zero(self): sem = asyncio.Semaphore(0, loop=self.loop) 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 @@ -12,10 +12,10 @@ from asyncio import test_utils -class ProactorSocketTransportTests(unittest.TestCase): +class ProactorSocketTransportTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() + self.loop = self.new_test_loop() self.proactor = mock.Mock() self.loop._proactor = self.proactor self.protocol = test_utils.make_test_protocol(asyncio.Protocol) @@ -343,7 +343,7 @@ tr.close() -class BaseProactorEventLoopTests(unittest.TestCase): +class BaseProactorEventLoopTests(test_utils.TestCase): def setUp(self): self.sock = mock.Mock(socket.socket) @@ -356,6 +356,7 @@ return (self.ssock, self.csock) self.loop = EventLoop(self.proactor) + self.set_event_loop(self.loop, cleanup=False) @mock.patch.object(BaseProactorEventLoop, 'call_soon') @mock.patch.object(BaseProactorEventLoop, '_socketpair') diff --git a/Lib/test/test_asyncio/test_queues.py b/Lib/test/test_asyncio/test_queues.py --- a/Lib/test/test_asyncio/test_queues.py +++ b/Lib/test/test_asyncio/test_queues.py @@ -7,14 +7,10 @@ from asyncio import test_utils -class _QueueTestBase(unittest.TestCase): +class _QueueTestBase(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.loop = self.new_test_loop() class QueueBasicTests(_QueueTestBase): @@ -32,8 +28,7 @@ self.assertAlmostEqual(0.2, when) yield 0.1 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) q = asyncio.Queue(loop=loop) self.assertTrue(fn(q).startswith('= (3, 5)) + + @asyncio.coroutine def coroutine_function(): pass @@ -25,15 +29,10 @@ pass -class TaskTests(unittest.TestCase): +class TaskTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() - gc.collect() + self.loop = self.new_test_loop() def test_task_class(self): @asyncio.coroutine @@ -46,6 +45,7 @@ self.assertIs(t._loop, self.loop) loop = asyncio.new_event_loop() + self.set_event_loop(loop) t = asyncio.Task(notmuch(), loop=loop) self.assertIs(t._loop, loop) loop.close() @@ -61,6 +61,7 @@ self.assertIs(t._loop, self.loop) loop = asyncio.new_event_loop() + self.set_event_loop(loop) t = asyncio.async(notmuch(), loop=loop) self.assertIs(t._loop, loop) loop.close() @@ -76,6 +77,7 @@ self.assertIs(f, f_orig) loop = asyncio.new_event_loop() + self.set_event_loop(loop) with self.assertRaises(ValueError): f = asyncio.async(f_orig, loop=loop) @@ -97,6 +99,7 @@ self.assertIs(t, t_orig) loop = asyncio.new_event_loop() + self.set_event_loop(loop) with self.assertRaises(ValueError): t = asyncio.async(t_orig, loop=loop) @@ -116,10 +119,22 @@ yield from [] return 'abc' + self.assertEqual(notmuch.__name__, 'notmuch') + if PY35: + self.assertEqual(notmuch.__qualname__, + 'TaskTests.test_task_repr..notmuch') + self.assertEqual(notmuch.__module__, __name__) + filename, lineno = test_utils.get_function_source(notmuch) src = "%s:%s" % (filename, lineno) - t = asyncio.Task(notmuch(), loop=self.loop) + gen = notmuch() + self.assertEqual(gen.__name__, 'notmuch') + if PY35: + self.assertEqual(gen.__qualname__, + 'TaskTests.test_task_repr..notmuch') + + t = asyncio.Task(gen, loop=self.loop) t.add_done_callback(Dummy()) self.assertEqual(repr(t), 'Task()' % src) @@ -142,6 +157,12 @@ def notmuch(): pass + self.assertEqual(notmuch.__name__, 'notmuch') + self.assertEqual(notmuch.__module__, __name__) + if PY35: + self.assertEqual(notmuch.__qualname__, + 'TaskTests.test_task_repr_custom..notmuch') + class T(asyncio.Future): def __repr__(self): return 'T[]' @@ -151,13 +172,26 @@ return super().__repr__() gen = notmuch() + if PY35 or tasks._DEBUG: + # On Python >= 3.5, generators now inherit the name of the + # function, as expected, and have a qualified name (__qualname__ + # attribute). In debug mode, @coroutine decorator uses CoroWrapper + # which gets its name (__name__ attribute) from the wrapped + # coroutine function. + coro_name = 'notmuch' + else: + # On Python < 3.5, generators inherit the name of the code, not of + # the function. See: http://bugs.python.org/issue21205 + coro_name = 'coro' + self.assertEqual(gen.__name__, coro_name) + if PY35: + self.assertEqual(gen.__qualname__, + 'TaskTests.test_task_repr_custom..notmuch') + t = MyTask(gen, loop=self.loop) filename = gen.gi_code.co_filename lineno = gen.gi_frame.f_lineno - # FIXME: check for the name "coro" instead of "notmuch" because - # @asyncio.coroutine drops the name of the wrapped function: - # http://bugs.python.org/issue21205 - self.assertEqual(repr(t), 'T[]()' % (filename, lineno)) + self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (coro_name, filename, lineno)) def test_task_basics(self): @asyncio.coroutine @@ -184,8 +218,7 @@ self.assertAlmostEqual(10.0, when) yield 0 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) @asyncio.coroutine def task(): @@ -310,7 +343,7 @@ def test_cancel_current_task(self): loop = asyncio.new_event_loop() - self.addCleanup(loop.close) + self.set_event_loop(loop) @asyncio.coroutine def task(): @@ -338,8 +371,7 @@ self.assertAlmostEqual(0.3, when) yield 0.1 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) x = 0 waiters = [] @@ -374,8 +406,7 @@ self.assertAlmostEqual(0.1, when) when = yield 0.1 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) foo_running = None @@ -400,8 +431,7 @@ self.assertEqual(foo_running, False) def test_wait_for_blocking(self): - loop = test_utils.TestLoop() - self.addCleanup(loop.close) + loop = self.new_test_loop() @asyncio.coroutine def coro(): @@ -421,8 +451,7 @@ self.assertAlmostEqual(0.01, when) yield 0.01 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) @asyncio.coroutine def foo(): @@ -450,8 +479,7 @@ self.assertAlmostEqual(0.15, when) yield 0.15 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) b = asyncio.Task(asyncio.sleep(0.15, loop=loop), loop=loop) @@ -481,8 +509,7 @@ self.assertAlmostEqual(0.015, when) yield 0.015 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.Task(asyncio.sleep(0.01, loop=loop), loop=loop) b = asyncio.Task(asyncio.sleep(0.015, loop=loop), loop=loop) @@ -495,11 +522,8 @@ return 42 asyncio.set_event_loop(loop) - try: - res = loop.run_until_complete( - asyncio.Task(foo(), loop=loop)) - finally: - asyncio.set_event_loop(None) + res = loop.run_until_complete( + asyncio.Task(foo(), loop=loop)) self.assertEqual(res, 42) @@ -537,8 +561,7 @@ self.assertAlmostEqual(0.1, when) yield 0.1 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.Task(asyncio.sleep(10.0, loop=loop), loop=loop) b = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) @@ -593,8 +616,7 @@ self.assertAlmostEqual(10.0, when) yield 0 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) # first_exception, task already has exception a = asyncio.Task(asyncio.sleep(10.0, loop=loop), loop=loop) @@ -627,8 +649,7 @@ self.assertAlmostEqual(0.01, when) yield 0.01 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) # first_exception, exception during waiting a = asyncio.Task(asyncio.sleep(10.0, loop=loop), loop=loop) @@ -660,8 +681,7 @@ self.assertAlmostEqual(0.15, when) yield 0.15 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) @@ -697,8 +717,7 @@ self.assertAlmostEqual(0.11, when) yield 0.11 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) b = asyncio.Task(asyncio.sleep(0.15, loop=loop), loop=loop) @@ -728,8 +747,7 @@ self.assertAlmostEqual(0.1, when) yield 0.1 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) b = asyncio.Task(asyncio.sleep(0.15, loop=loop), loop=loop) @@ -753,8 +771,7 @@ yield 0.01 yield 0 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) completed = set() time_shifted = False @@ -797,8 +814,7 @@ yield 0 yield 0.1 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.sleep(0.1, 'a', loop=loop) b = asyncio.sleep(0.15, 'b', loop=loop) @@ -834,8 +850,7 @@ yield 0 yield 0.01 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.sleep(0.01, 'a', loop=loop) @@ -854,8 +869,7 @@ yield 0.05 yield 0 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.sleep(0.05, 'a', loop=loop) b = asyncio.sleep(0.10, 'b', loop=loop) @@ -880,8 +894,7 @@ self.assertAlmostEqual(0.05, when) yield 0.05 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) a = asyncio.sleep(0.05, 'a', loop=loop) b = asyncio.sleep(0.05, 'b', loop=loop) @@ -922,8 +935,7 @@ self.assertAlmostEqual(0.1, when) yield 0.05 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) @asyncio.coroutine def sleeper(dt, arg): @@ -944,8 +956,7 @@ self.assertAlmostEqual(10.0, when) yield 0 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) t = asyncio.Task(asyncio.sleep(10.0, 'yeah', loop=loop), loop=loop) @@ -976,8 +987,7 @@ self.assertAlmostEqual(5000, when) yield 0.1 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) @asyncio.coroutine def sleep(dt): @@ -1087,8 +1097,7 @@ self.assertAlmostEqual(10.0, when) yield 0 - loop = test_utils.TestLoop(gen) - self.addCleanup(loop.close) + loop = self.new_test_loop(gen) @asyncio.coroutine def sleeper(): @@ -1500,12 +1509,9 @@ class GatherTestsBase: def setUp(self): - self.one_loop = test_utils.TestLoop() - self.other_loop = test_utils.TestLoop() - - def tearDown(self): - self.one_loop.close() - self.other_loop.close() + self.one_loop = self.new_test_loop() + self.other_loop = self.new_test_loop() + self.set_event_loop(self.one_loop, cleanup=False) def _run_loop(self, loop): while loop._ready: @@ -1597,7 +1603,7 @@ self.assertEqual(stdout.rstrip(), b'False') -class FutureGatherTests(GatherTestsBase, unittest.TestCase): +class FutureGatherTests(GatherTestsBase, test_utils.TestCase): def wrap_futures(self, *futures): return futures @@ -1681,16 +1687,12 @@ cb.assert_called_once_with(fut) -class CoroutineGatherTests(GatherTestsBase, unittest.TestCase): +class CoroutineGatherTests(GatherTestsBase, test_utils.TestCase): def setUp(self): super().setUp() asyncio.set_event_loop(self.one_loop) - def tearDown(self): - asyncio.set_event_loop(None) - super().tearDown() - def wrap_futures(self, *futures): coros = [] for fut in futures: diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -29,14 +29,11 @@ @unittest.skipUnless(signal, 'Signals are not supported') -class SelectorEventLoopSignalTests(unittest.TestCase): +class SelectorEventLoopSignalTests(test_utils.TestCase): def setUp(self): self.loop = asyncio.SelectorEventLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.set_event_loop(self.loop) def test_check_signal(self): self.assertRaises( @@ -208,14 +205,11 @@ @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'UNIX Sockets are not supported') -class SelectorEventLoopUnixSocketTests(unittest.TestCase): +class SelectorEventLoopUnixSocketTests(test_utils.TestCase): def setUp(self): self.loop = asyncio.SelectorEventLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() + self.set_event_loop(self.loop) def test_create_unix_server_existing_path_sock(self): with test_utils.unix_socket_path() as path: @@ -304,10 +298,10 @@ self.loop.run_until_complete(coro) -class UnixReadPipeTransportTests(unittest.TestCase): +class UnixReadPipeTransportTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() + self.loop = self.new_test_loop() self.protocol = test_utils.make_test_protocol(asyncio.Protocol) self.pipe = mock.Mock(spec_set=io.RawIOBase) self.pipe.fileno.return_value = 5 @@ -451,7 +445,7 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(4, sys.getrefcount(self.loop), + self.assertEqual(5, sys.getrefcount(self.loop), pprint.pformat(gc.get_referrers(self.loop))) def test__call_connection_lost_with_err(self): @@ -468,14 +462,14 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(4, sys.getrefcount(self.loop), + self.assertEqual(5, sys.getrefcount(self.loop), pprint.pformat(gc.get_referrers(self.loop))) -class UnixWritePipeTransportTests(unittest.TestCase): +class UnixWritePipeTransportTests(test_utils.TestCase): def setUp(self): - self.loop = test_utils.TestLoop() + self.loop = self.new_test_loop() self.protocol = test_utils.make_test_protocol(asyncio.BaseProtocol) self.pipe = mock.Mock(spec_set=io.RawIOBase) self.pipe.fileno.return_value = 5 @@ -737,7 +731,7 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(4, sys.getrefcount(self.loop), + self.assertEqual(5, sys.getrefcount(self.loop), pprint.pformat(gc.get_referrers(self.loop))) def test__call_connection_lost_with_err(self): @@ -753,7 +747,7 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(4, sys.getrefcount(self.loop), + self.assertEqual(5, sys.getrefcount(self.loop), pprint.pformat(gc.get_referrers(self.loop))) def test_close(self): @@ -834,7 +828,7 @@ ignore_warnings = mock.patch.object(log.logger, "warning") def setUp(self): - self.loop = test_utils.TestLoop() + self.loop = self.new_test_loop() self.running = False self.zombies = {} @@ -1392,7 +1386,7 @@ # attach a new loop old_loop = self.loop - self.loop = test_utils.TestLoop() + self.loop = self.new_test_loop() patch = mock.patch.object with patch(old_loop, "remove_signal_handler") as m_old_remove, \ @@ -1447,7 +1441,7 @@ self.assertFalse(callback3.called) # attach a new loop - self.loop = test_utils.TestLoop() + self.loop = self.new_test_loop() with mock.patch.object( self.loop, "add_signal_handler") as m_add_signal_handler: @@ -1505,12 +1499,12 @@ self.assertFalse(self.watcher._zombies) -class SafeChildWatcherTests (ChildWatcherTestsMixin, unittest.TestCase): +class SafeChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): def create_watcher(self): return asyncio.SafeChildWatcher() -class FastChildWatcherTests (ChildWatcherTestsMixin, unittest.TestCase): +class FastChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): def create_watcher(self): return asyncio.FastChildWatcher() 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 @@ -9,6 +9,7 @@ import asyncio from asyncio import _overlapped +from asyncio import test_utils from asyncio import windows_events @@ -26,15 +27,11 @@ self.trans.close() -class ProactorTests(unittest.TestCase): +class ProactorTests(test_utils.TestCase): def setUp(self): self.loop = asyncio.ProactorEventLoop() - asyncio.set_event_loop(None) - - def tearDown(self): - self.loop.close() - self.loop = None + self.set_event_loop(self.loop) def test_close(self): a, b = self.loop._socketpair() diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -507,6 +507,11 @@ for s in ('abcd', range(2000)): self.assertEqual(list(reversed(deque(s))), list(reversed(s))) + def test_reversed_new(self): + klass = type(reversed(deque())) + for s in ('abcd', range(2000)): + self.assertEqual(list(klass(deque(s))), list(reversed(s))) + def test_gc_doesnt_blowup(self): import gc # This used to assert-fail in deque_traverse() under a debug 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 @@ -1149,7 +1149,7 @@ except (TypeError, UnicodeEncodeError): pass else: - raise TestFailed("[chr(128)] slots not caught") + self.fail("[chr(128)] slots not caught") # Test leaks class Counted(object): 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 @@ -1528,9 +1528,7 @@ helper = pydoc.Helper(output=output) helper(self.Color) result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(result, expected_text) def test_inspect_getmembers(self): values = dict(( diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -50,6 +50,45 @@ self.assertEqual(gc.garbage, old_garbage) +class GeneratorTest(unittest.TestCase): + + def test_name(self): + def func(): + yield 1 + + # check generator names + gen = func() + self.assertEqual(gen.__name__, "func") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name..func") + + # modify generator names + gen.__name__ = "name" + gen.__qualname__ = "qualname" + self.assertEqual(gen.__name__, "name") + self.assertEqual(gen.__qualname__, "qualname") + + # generator names must be a string and cannot be deleted + self.assertRaises(TypeError, setattr, gen, '__name__', 123) + self.assertRaises(TypeError, setattr, gen, '__qualname__', 123) + self.assertRaises(TypeError, delattr, gen, '__name__') + self.assertRaises(TypeError, delattr, gen, '__qualname__') + + # modify names of the function creating the generator + func.__qualname__ = "func_qualname" + func.__name__ = "func_name" + gen = func() + self.assertEqual(gen.__name__, "func_name") + self.assertEqual(gen.__qualname__, "func_qualname") + + # unnamed generator + gen = (x for x in range(10)) + self.assertEqual(gen.__name__, + "") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name..") + + tutorial_tests = """ Let's try a simple generator: diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -390,6 +390,31 @@ check_syntax_error(self, "x + 1 = 1") check_syntax_error(self, "a + 1 = b + 2") + # Check the heuristic for print & exec covers significant cases + # As well as placing some limits on false positives + def test_former_statements_refer_to_builtins(self): + keywords = "print", "exec" + # Cases where we want the custom error + cases = [ + "{} foo", + "{} {{1:foo}}", + "if 1: {} foo", + "if 1: {} {{1:foo}}", + "if 1:\n {} foo", + "if 1:\n {} {{1:foo}}", + ] + for keyword in keywords: + custom_msg = "call to '{}'".format(keyword) + for case in cases: + source = case.format(keyword) + with self.subTest(source=source): + with self.assertRaisesRegex(SyntaxError, custom_msg): + exec(source) + source = source.replace("foo", "(foo.)") + with self.subTest(source=source): + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(source) + def test_del_stmt(self): # 'del' exprlist abc = [1,2,3] diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py --- a/Lib/test/test_heapq.py +++ b/Lib/test/test_heapq.py @@ -13,8 +13,8 @@ # _heapq.nlargest/nsmallest are saved in heapq._nlargest/_smallest when # _heapq is imported, so check them there -func_names = ['heapify', 'heappop', 'heappush', 'heappushpop', - 'heapreplace', '_heapreplace_max'] +func_names = ['heapify', 'heappop', 'heappush', 'heappushpop', 'heapreplace', + '_heappop_max', '_heapreplace_max', '_heapify_max'] class TestModules(TestCase): def test_py_functions(self): 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 @@ -485,6 +485,11 @@ (res.read(), res.getheader('Content-type'), res.status)) self.assertEqual(os.environ['SERVER_SOFTWARE'], signature) + def test_urlquote_decoding_in_cgi_check(self): + res = self.request('/cgi-bin%2ffile1.py') + self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), + (res.read(), res.getheader('Content-type'), res.status)) + class SocketlessRequestHandler(SimpleHTTPRequestHandler): def __init__(self): diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1531,6 +1531,13 @@ num_children_after = len(doc.childNodes) self.assertTrue(num_children_after == num_children_before - 1) + def testProcessingInstructionNameError(self): + # wrong variable in .nodeValue property will + # lead to "NameError: name 'data' is not defined" + doc = parse(tstfile) + pi = doc.createProcessingInstruction("y", "z") + pi.nodeValue = "crash" + def test_main(): run_unittest(MinidomTest) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -530,6 +530,28 @@ os.stat(r) self.assertEqual(ctx.exception.errno, errno.EBADF) + def check_file_attributes(self, result): + self.assertTrue(hasattr(result, 'st_file_attributes')) + self.assertTrue(isinstance(result.st_file_attributes, int)) + self.assertTrue(0 <= result.st_file_attributes <= 0xFFFFFFFF) + + @unittest.skipUnless(sys.platform == "win32", + "st_file_attributes is Win32 specific") + def test_file_attributes(self): + # test file st_file_attributes (FILE_ATTRIBUTE_DIRECTORY not set) + result = os.stat(self.fname) + self.check_file_attributes(result) + self.assertEqual( + result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY, + 0) + + # test directory st_file_attributes (FILE_ATTRIBUTE_DIRECTORY set) + result = os.stat(support.TESTFN) + self.check_file_attributes(result) + self.assertEqual( + result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY, + stat.FILE_ATTRIBUTE_DIRECTORY) + from test import mapping_tests class EnvironTests(mapping_tests.BasicTestMappingProtocol): diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -402,6 +402,7 @@ "Docstrings are omitted with -O2 and above") @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') + @requires_docstrings def test_html_doc(self): result, doc_loc = get_pydoc_html(pydoc_mod) mod_file = inspect.getabsfile(pydoc_mod) @@ -421,6 +422,7 @@ "Docstrings are omitted with -O2 and above") @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') + @requires_docstrings def test_text_doc(self): result, doc_loc = get_pydoc_text(pydoc_mod) expected_text = expected_text_pattern % ( @@ -495,6 +497,7 @@ 'Docstrings are omitted with -O2 and above') @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') + @requires_docstrings def test_help_output_redirect(self): # issue 940286, if output is set in Helper, then all output from # Helper.help should be redirected @@ -746,7 +749,7 @@ try: pydoc.render_doc(name) except ImportError: - self.fail('finding the doc of {!r} failed'.format(o)) + self.fail('finding the doc of {!r} failed'.format(name)) for name in ('notbuiltins', 'strrr', 'strr.translate', 'str.trrrranslate', 'builtins.strrr', diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py --- a/Lib/test/test_stat.py +++ b/Lib/test/test_stat.py @@ -1,5 +1,6 @@ import unittest import os +import sys from test.support import TESTFN, import_fresh_module c_stat = import_fresh_module('stat', fresh=['_stat']) @@ -52,6 +53,26 @@ 'S_IWOTH': 0o002, 'S_IXOTH': 0o001} + # defined by the Windows API documentation + file_attributes = { + 'FILE_ATTRIBUTE_ARCHIVE': 32, + 'FILE_ATTRIBUTE_COMPRESSED': 2048, + 'FILE_ATTRIBUTE_DEVICE': 64, + 'FILE_ATTRIBUTE_DIRECTORY': 16, + 'FILE_ATTRIBUTE_ENCRYPTED': 16384, + 'FILE_ATTRIBUTE_HIDDEN': 2, + 'FILE_ATTRIBUTE_INTEGRITY_STREAM': 32768, + 'FILE_ATTRIBUTE_NORMAL': 128, + 'FILE_ATTRIBUTE_NOT_CONTENT_INDEXED': 8192, + 'FILE_ATTRIBUTE_NO_SCRUB_DATA': 131072, + 'FILE_ATTRIBUTE_OFFLINE': 4096, + 'FILE_ATTRIBUTE_READONLY': 1, + 'FILE_ATTRIBUTE_REPARSE_POINT': 1024, + 'FILE_ATTRIBUTE_SPARSE_FILE': 512, + 'FILE_ATTRIBUTE_SYSTEM': 4, + 'FILE_ATTRIBUTE_TEMPORARY': 256, + 'FILE_ATTRIBUTE_VIRTUAL': 65536} + def setUp(self): try: os.remove(TESTFN) @@ -185,6 +206,14 @@ self.assertTrue(callable(func)) self.assertEqual(func(0), 0) + @unittest.skipUnless(sys.platform == "win32", + "FILE_ATTRIBUTE_* constants are Win32 specific") + def test_file_attribute_constants(self): + for key, value in sorted(self.file_attributes.items()): + self.assertTrue(hasattr(self.statmod, key), key) + modvalue = getattr(self.statmod, key) + self.assertEqual(value, modvalue, key) + class TestFilemodeCStat(TestFilemode, unittest.TestCase): statmod = c_stat diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1934,6 +1934,20 @@ """Confirm that issue21618 is fixed (may fail under valgrind).""" fd_status = support.findfile("fd_status.py", subdir="subprocessdata") + # This launches the meat of the test in a child process to + # avoid messing with the larger unittest processes maximum + # number of file descriptors. + # This process launches: + # +--> Process that lowers its RLIMIT_NOFILE aftr setting up + # a bunch of high open fds above the new lower rlimit. + # Those are reported via stdout before launching a new + # process with close_fds=False to run the actual test: + # +--> The TEST: This one launches a fd_status.py + # subprocess with close_fds=True so we can find out if + # any of the fds above the lowered rlimit are still open. + p = subprocess.Popen([sys.executable, '-c', textwrap.dedent( + ''' + import os, resource, subprocess, sys, textwrap open_fds = set() # Add a bunch more fds to pass down. for _ in range(40): @@ -1949,12 +1963,15 @@ open_fds.remove(fd) for fd in open_fds: - self.addCleanup(os.close, fd) + #self.addCleanup(os.close, fd) os.set_inheritable(fd, True) max_fd_open = max(open_fds) - import resource + # Communicate the open_fds to the parent unittest.TestCase process. + print(','.join(map(str, sorted(open_fds)))) + sys.stdout.flush() + rlim_cur, rlim_max = resource.getrlimit(resource.RLIMIT_NOFILE) try: # 29 is lower than the highest fds we are leaving open. @@ -1965,22 +1982,27 @@ # An explicit list of fds to check is passed to fd_status.py as # letting fd_status rely on its default logic would miss the # fds above rlim_cur as it normally only checks up to that limit. - p = subprocess.Popen( + subprocess.Popen( [sys.executable, '-c', textwrap.dedent(""" import subprocess, sys - subprocess.Popen([sys.executable, {fd_status!r}] + + subprocess.Popen([sys.executable, %r] + [str(x) for x in range({max_fd})], close_fds=True).wait() - """.format(fd_status=fd_status, max_fd=max_fd_open+1))], - stdout=subprocess.PIPE, close_fds=False) + """.format(max_fd=max_fd_open+1))], + close_fds=False).wait() finally: resource.setrlimit(resource.RLIMIT_NOFILE, (rlim_cur, rlim_max)) + ''' % fd_status)], stdout=subprocess.PIPE) output, unused_stderr = p.communicate() - remaining_fds = set(map(int, output.strip().split(b','))) + output_lines = output.splitlines() + self.assertEqual(len(output_lines), 2, + msg="expected exactly two lines of output:\n%r" % output) + opened_fds = set(map(int, output_lines[0].strip().split(b','))) + remaining_fds = set(map(int, output_lines[1].strip().split(b','))) - self.assertFalse(remaining_fds & open_fds, + self.assertFalse(remaining_fds & opened_fds, msg="Some fds were left open.") 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 @@ -885,7 +885,7 @@ check(bar, size('PP')) # generator def get_gen(): yield 1 - check(get_gen(), size('Pb2P')) + check(get_gen(), size('Pb2PPP')) # iterator check(iter('abc'), size('lP')) # callable-iterator diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -976,7 +976,7 @@ def _get_nodeValue(self): return self.data def _set_nodeValue(self, value): - self.data = data + self.data = value nodeValue = property(_get_nodeValue, _set_nodeValue) # nodeName is an alias for target diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -174,6 +174,7 @@ Tobias Brink Richard Brodie Michael Broghton +Ammar Brohi Daniel Brotsky Jean Brouwers Gary S. Brown @@ -308,6 +309,7 @@ Arnaud Delobelle Konrad Delong Erik Demaine +Martin Dengler John Dennis L. Peter Deutsch Roger Dev @@ -578,6 +580,7 @@ Ken Howard Brad Howes Mike Hoy +Ben Hoyt Chih-Hao Huang Christian Hudon Lawrence Hudson diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,17 @@ Core and Builtins ----------------- +- Issue #21205: Add a new ``__qualname__`` attribute to generator, the + qualified name, and use it in the representation of a generator + (``repr(gen)``). The default name of the generator (``__name__`` attribute) + is now get from the function instead of the code. Use ``gen.gi_code.co_name`` + to get the name of the code. + +- Issue #21669: With the aid of heuristics in SyntaxError.__init__, the + parser now attempts to generate more meaningful (or at least more search + engine friendly) error messages when "exec" and "print" are used as + statements. + - Issue #21642: If the conditional if-else expression, allow an integer written with no space between itself and the ``else`` keyword (e.g. ``True if 42else False``) to be valid syntax. @@ -92,6 +103,17 @@ Library ------- +- Issue #21491: socketserver: Fix a race condition in child processes reaping. + +- Issue #21719: Added the ``st_file_attributes`` field to os.stat_result on + Windows. + +- Issue #21722: The distutils "upload" command now exits with a non-zero + return code when uploading fails. Patch by Martin Dengler. + +- Issue #21723: asyncio.Queue: support any type of number (ex: float) for the + maximum size. Patch written by Vajrasky Kok. + - Issue #21711: support for "site-python" directories has now been removed from the site module (it was deprecated in 3.4). @@ -106,6 +128,9 @@ run_forever() and run_until_complete() methods of asyncio.BaseEventLoop now raise an exception if the event loop was closed. +- Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths + before checking for a CGI script at that path. + - Issue #21310: Fixed possible resource leak in failed open(). - Issue #21256: Printout of keyword args should be in deterministic order in @@ -485,6 +510,15 @@ IDLE ---- +- Issue #21686: add unittest for HyperParser. Original patch by Saimadhav + Heblikar. + +- Issue #12387: Add missing upper(lower)case versions of default Windows key + bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy. + +- Issue #21695: Closing a Find-in-files output window while the search is + still in progress no longer closes Idle. + - Issue #18910: Add unittest for textView. Patch by Phil Webster. - Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar. diff --git a/Modules/_heapqmodule.c b/Modules/_heapqmodule.c --- a/Modules/_heapqmodule.c +++ b/Modules/_heapqmodule.c @@ -9,7 +9,7 @@ #include "Python.h" static int -_siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) +siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) { PyObject *newitem, *parent; Py_ssize_t parentpos, size; @@ -48,7 +48,7 @@ } static int -_siftup(PyListObject *heap, Py_ssize_t pos) +siftup(PyListObject *heap, Py_ssize_t pos) { Py_ssize_t startpos, endpos, childpos, rightpos, limit; PyObject *tmp1, *tmp2; @@ -91,7 +91,7 @@ pos = childpos; } /* Bubble it up to its final resting place (by sifting its parents down). */ - return _siftdown(heap, startpos, pos); + return siftdown(heap, startpos, pos); } static PyObject * @@ -110,17 +110,16 @@ if (PyList_Append(heap, item) == -1) return NULL; - if (_siftdown((PyListObject *)heap, 0, PyList_GET_SIZE(heap)-1) == -1) + if (siftdown((PyListObject *)heap, 0, PyList_GET_SIZE(heap)-1) == -1) return NULL; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyDoc_STRVAR(heappush_doc, "heappush(heap, item) -> None. Push item onto heap, maintaining the heap invariant."); static PyObject * -heappop(PyObject *self, PyObject *heap) +heappop_internal(PyObject *heap, int siftup_func(PyListObject *, Py_ssize_t)) { PyObject *lastelt, *returnitem; Py_ssize_t n; @@ -130,7 +129,7 @@ return NULL; } - /* # raises appropriate IndexError if heap is empty */ + /* raises IndexError if the heap is empty */ n = PyList_GET_SIZE(heap); if (n == 0) { PyErr_SetString(PyExc_IndexError, "index out of range"); @@ -149,18 +148,24 @@ return lastelt; returnitem = PyList_GET_ITEM(heap, 0); PyList_SET_ITEM(heap, 0, lastelt); - if (_siftup((PyListObject *)heap, 0) == -1) { + if (siftup_func((PyListObject *)heap, 0) == -1) { Py_DECREF(returnitem); return NULL; } return returnitem; } +static PyObject * +heappop(PyObject *self, PyObject *heap) +{ + return heappop_internal(heap, siftup); +} + PyDoc_STRVAR(heappop_doc, "Pop the smallest item off the heap, maintaining the heap invariant."); static PyObject * -heapreplace(PyObject *self, PyObject *args) +heapreplace_internal(PyObject *args, int siftup_func(PyListObject *, Py_ssize_t)) { PyObject *heap, *item, *returnitem; @@ -180,13 +185,19 @@ returnitem = PyList_GET_ITEM(heap, 0); Py_INCREF(item); PyList_SET_ITEM(heap, 0, item); - if (_siftup((PyListObject *)heap, 0) == -1) { + if (siftup_func((PyListObject *)heap, 0) == -1) { Py_DECREF(returnitem); return NULL; } return returnitem; } +static PyObject * +heapreplace(PyObject *self, PyObject *args) +{ + return heapreplace_internal(args, siftup); +} + PyDoc_STRVAR(heapreplace_doc, "heapreplace(heap, item) -> value. Pop and return the current smallest value, and add the new item.\n\ \n\ @@ -227,7 +238,7 @@ returnitem = PyList_GET_ITEM(heap, 0); Py_INCREF(item); PyList_SET_ITEM(heap, 0, item); - if (_siftup((PyListObject *)heap, 0) == -1) { + if (siftup((PyListObject *)heap, 0) == -1) { Py_DECREF(returnitem); return NULL; } @@ -240,7 +251,7 @@ heappush() followed by a separate call to heappop()."); static PyObject * -heapify(PyObject *self, PyObject *heap) +heapify_internal(PyObject *heap, int siftup_func(PyListObject *, Py_ssize_t)) { Py_ssize_t i, n; @@ -258,17 +269,22 @@ and that's again n//2-1. */ for (i=n/2-1 ; i>=0 ; i--) - if(_siftup((PyListObject *)heap, i) == -1) + if(siftup_func((PyListObject *)heap, i) == -1) return NULL; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; +} + +static PyObject * +heapify(PyObject *self, PyObject *heap) +{ + return heapify_internal(heap, siftup); } PyDoc_STRVAR(heapify_doc, "Transform list into a heap, in-place, in O(len(heap)) time."); static int -_siftdownmax(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) +siftdown_max(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) { PyObject *newitem, *parent; Py_ssize_t parentpos, size; @@ -307,7 +323,7 @@ } static int -_siftupmax(PyListObject *heap, Py_ssize_t pos) +siftup_max(PyListObject *heap, Py_ssize_t pos) { Py_ssize_t startpos, endpos, childpos, rightpos, limit; PyObject *tmp1, *tmp2; @@ -350,39 +366,33 @@ pos = childpos; } /* Bubble it up to its final resting place (by sifting its parents down). */ - return _siftdownmax(heap, startpos, pos); + return siftdown_max(heap, startpos, pos); } static PyObject * -_heapreplace_max(PyObject *self, PyObject *args) +heappop_max(PyObject *self, PyObject *heap) { - PyObject *heap, *item, *returnitem; + return heappop_internal(heap, siftup_max); +} - if (!PyArg_UnpackTuple(args, "_heapreplace_max", 2, 2, &heap, &item)) - return NULL; +PyDoc_STRVAR(heappop_max_doc, "Maxheap variant of heappop."); - if (!PyList_Check(heap)) { - PyErr_SetString(PyExc_TypeError, "heap argument must be a list"); - return NULL; - } - - if (PyList_GET_SIZE(heap) < 1) { - PyErr_SetString(PyExc_IndexError, "index out of range"); - return NULL; - } - - returnitem = PyList_GET_ITEM(heap, 0); - Py_INCREF(item); - PyList_SET_ITEM(heap, 0, item); - if (_siftupmax((PyListObject *)heap, 0) == -1) { - Py_DECREF(returnitem); - return NULL; - } - return returnitem; +static PyObject * +heapreplace_max(PyObject *self, PyObject *args) +{ + return heapreplace_internal(args, siftup_max); } PyDoc_STRVAR(heapreplace_max_doc, "Maxheap variant of heapreplace"); +static PyObject * +heapify_max(PyObject *self, PyObject *heap) +{ + return heapify_internal(heap, siftup_max); +} + +PyDoc_STRVAR(heapify_max_doc, "Maxheap variant of heapify."); + static PyMethodDef heapq_methods[] = { {"heappush", (PyCFunction)heappush, METH_VARARGS, heappush_doc}, @@ -394,8 +404,12 @@ METH_VARARGS, heapreplace_doc}, {"heapify", (PyCFunction)heapify, METH_O, heapify_doc}, - {"_heapreplace_max",(PyCFunction)_heapreplace_max, + {"_heappop_max", (PyCFunction)heappop_max, + METH_O, heappop_max_doc}, + {"_heapreplace_max",(PyCFunction)heapreplace_max, METH_VARARGS, heapreplace_max_doc}, + {"_heapify_max", (PyCFunction)heapify_max, + METH_O, heapify_max_doc}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -224,8 +224,8 @@ PyObject_HEAD PyObject *decoder; PyObject *errors; - signed int pendingcr: 1; - signed int translate: 1; + unsigned int pendingcr: 1; + unsigned int translate: 1; unsigned int seennl: 3; } nldecoder_object; @@ -546,7 +546,7 @@ if (!PyArg_Parse(state, "(OK)", &buffer, &flag)) return NULL; - self->pendingcr = (int) flag & 1; + self->pendingcr = (int) (flag & 1); flag >>= 1; if (self->decoder != Py_None) diff --git a/Modules/_stat.c b/Modules/_stat.c --- a/Modules/_stat.c +++ b/Modules/_stat.c @@ -27,9 +27,21 @@ #endif /* HAVE_SYS_STAT_H */ #ifdef MS_WINDOWS +#include typedef unsigned short mode_t; + +/* FILE_ATTRIBUTE_INTEGRITY_STREAM and FILE_ATTRIBUTE_NO_SCRUB_DATA + are not present in VC2010, so define them manually */ +#ifndef FILE_ATTRIBUTE_INTEGRITY_STREAM +# define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x8000 #endif +#ifndef FILE_ATTRIBUTE_NO_SCRUB_DATA +# define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x20000 +#endif + +#endif /* MS_WINDOWS */ + /* From Python's stat.py */ #ifndef S_IMODE # define S_IMODE 07777 @@ -473,6 +485,10 @@ ST_ATIME\n\ ST_MTIME\n\ ST_CTIME\n\ +\n" + +"FILE_ATTRIBUTE_*: Windows file attribute constants\n\ + (only present on Windows)\n\ "); @@ -555,6 +571,26 @@ if (PyModule_AddIntConstant(m, "ST_MTIME", 8)) return NULL; if (PyModule_AddIntConstant(m, "ST_CTIME", 9)) return NULL; +#ifdef MS_WINDOWS + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_ARCHIVE)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_COMPRESSED)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_DEVICE)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_DIRECTORY)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_ENCRYPTED)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_HIDDEN)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_INTEGRITY_STREAM)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_NORMAL)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_NO_SCRUB_DATA)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_OFFLINE)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_READONLY)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_REPARSE_POINT)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_SPARSE_FILE)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_SYSTEM)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_TEMPORARY)) return NULL; + if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_VIRTUAL)) return NULL; +#endif + return m; } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1417,6 +1417,7 @@ Therefore, we implement our own stat, based on the Win32 API directly. */ #define HAVE_STAT_NSEC 1 +#define HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES 1 struct win32_stat{ unsigned long st_dev; @@ -1433,6 +1434,7 @@ int st_mtime_nsec; time_t st_ctime; int st_ctime_nsec; + unsigned long st_file_attributes; }; static __int64 secs_between_epochs = 11644473600; /* Seconds between 1.1.1601 and 1.1.1970 */ @@ -1497,6 +1499,7 @@ /* now set the bits that make this a symlink */ result->st_mode |= S_IFLNK; } + result->st_file_attributes = info->dwFileAttributes; return 0; } @@ -1961,6 +1964,9 @@ #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME {"st_birthtime", "time of creation"}, #endif +#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES + {"st_file_attributes", "Windows file attribute bits"}, +#endif {0} }; @@ -2000,6 +2006,12 @@ #define ST_BIRTHTIME_IDX ST_GEN_IDX #endif +#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES +#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_IDX+1) +#else +#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_IDX +#endif + static PyStructSequence_Desc stat_result_desc = { "stat_result", /* name */ stat_result__doc__, /* doc */ @@ -2267,6 +2279,10 @@ PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX, PyLong_FromLong((long)st->st_flags)); #endif +#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES + PyStructSequence_SET_ITEM(v, ST_FILE_ATTRIBUTES_IDX, + PyLong_FromUnsignedLong(st->st_file_attributes)); +#endif if (PyErr_Occurred()) { Py_DECREF(v); diff --git a/Objects/exceptions.c b/Objects/exceptions.c --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1254,6 +1254,9 @@ * SyntaxError extends Exception */ +/* Helper function to customise error message for some syntax errors */ +static int _report_missing_parentheses(PySyntaxErrorObject *self); + static int SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds) { @@ -1298,6 +1301,13 @@ Py_INCREF(self->text); Py_DECREF(info); + + /* Issue #21669: Custom error for 'print' & 'exec' as statements */ + if (self->text && PyUnicode_Check(self->text)) { + if (_report_missing_parentheses(self) < 0) { + return -1; + } + } } return 0; } @@ -2783,3 +2793,128 @@ PyErr_Restore(new_exc, new_val, new_tb); return new_val; } + + +/* To help with migration from Python 2, SyntaxError.__init__ applies some + * heuristics to try to report a more meaningful exception when print and + * exec are used like statements. + * + * The heuristics are currently expected to detect the following cases: + * - top level statement + * - statement in a nested suite + * - trailing section of a one line complex statement + * + * They're currently known not to trigger: + * - after a semi-colon + * + * The error message can be a bit odd in cases where the "arguments" are + * completely illegal syntactically, but that isn't worth the hassle of + * fixing. + * + * We also can't do anything about cases that are legal Python 3 syntax + * but mean something entirely different from what they did in Python 2 + * (omitting the arguments entirely, printing items preceded by a unary plus + * or minus, using the stream redirection syntax). + */ + +static int +_check_for_legacy_statements(PySyntaxErrorObject *self, Py_ssize_t start) +{ + /* Return values: + * -1: an error occurred + * 0: nothing happened + * 1: the check triggered & the error message was changed + */ + static PyObject *print_prefix = NULL; + static PyObject *exec_prefix = NULL; + Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text); + int kind = PyUnicode_KIND(self->text); + void *data = PyUnicode_DATA(self->text); + + /* Ignore leading whitespace */ + while (start < text_len) { + Py_UCS4 ch = PyUnicode_READ(kind, data, start); + if (!Py_UNICODE_ISSPACE(ch)) + break; + start++; + } + /* Checking against an empty or whitespace-only part of the string */ + if (start == text_len) { + return 0; + } + + /* Check for legacy print statements */ + if (print_prefix == NULL) { + print_prefix = PyUnicode_InternFromString("print "); + if (print_prefix == NULL) { + return -1; + } + } + if (PyUnicode_Tailmatch(self->text, print_prefix, + start, text_len, -1)) { + Py_CLEAR(self->msg); + self->msg = PyUnicode_FromString( + "Missing parentheses in call to 'print'"); + return 1; + } + + /* Check for legacy exec statements */ + if (exec_prefix == NULL) { + exec_prefix = PyUnicode_InternFromString("exec "); + if (exec_prefix == NULL) { + return -1; + } + } + if (PyUnicode_Tailmatch(self->text, exec_prefix, + start, text_len, -1)) { + Py_CLEAR(self->msg); + self->msg = PyUnicode_FromString( + "Missing parentheses in call to 'exec'"); + return 1; + } + /* Fall back to the default error message */ + return 0; +} + +static int +_report_missing_parentheses(PySyntaxErrorObject *self) +{ + Py_UCS4 left_paren = 40; + Py_ssize_t left_paren_index; + Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text); + int legacy_check_result = 0; + + /* Skip entirely if there is an opening parenthesis */ + left_paren_index = PyUnicode_FindChar(self->text, left_paren, + 0, text_len, 1); + if (left_paren_index < -1) { + return -1; + } + if (left_paren_index != -1) { + /* Use default error message for any line with an opening paren */ + return 0; + } + /* Handle the simple statement case */ + legacy_check_result = _check_for_legacy_statements(self, 0); + if (legacy_check_result < 0) { + return -1; + + } + if (legacy_check_result == 0) { + /* Handle the one-line complex statement case */ + Py_UCS4 colon = 58; + Py_ssize_t colon_index; + colon_index = PyUnicode_FindChar(self->text, colon, + 0, text_len, 1); + if (colon_index < -1) { + return -1; + } + if (colon_index >= 0 && colon_index < text_len) { + /* Check again, starting from just after the colon */ + if (_check_for_legacy_statements(self, colon_index+1) < 0) { + return -1; + } + } + } + return 0; +} diff --git a/Objects/genobject.c b/Objects/genobject.c --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -12,6 +12,8 @@ { Py_VISIT((PyObject *)gen->gi_frame); Py_VISIT(gen->gi_code); + Py_VISIT(gen->gi_name); + Py_VISIT(gen->gi_qualname); return 0; } @@ -58,6 +60,8 @@ _PyObject_GC_UNTRACK(self); Py_CLEAR(gen->gi_frame); Py_CLEAR(gen->gi_code); + Py_CLEAR(gen->gi_name); + Py_CLEAR(gen->gi_qualname); PyObject_GC_Del(gen); } @@ -418,33 +422,73 @@ gen_repr(PyGenObject *gen) { return PyUnicode_FromFormat("", - ((PyCodeObject *)gen->gi_code)->co_name, - gen); + gen->gi_qualname, gen); } +static PyObject * +gen_get_name(PyGenObject *op) +{ + Py_INCREF(op->gi_name); + return op->gi_name; +} + +static int +gen_set_name(PyGenObject *op, PyObject *value) +{ + PyObject *tmp; + + /* Not legal to del gen.gi_name or to set it to anything + * other than a string object. */ + if (value == NULL || !PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "__name__ must be set to a string object"); + return -1; + } + tmp = op->gi_name; + Py_INCREF(value); + op->gi_name = value; + Py_DECREF(tmp); + return 0; +} static PyObject * -gen_get_name(PyGenObject *gen) +gen_get_qualname(PyGenObject *op) { - PyObject *name = ((PyCodeObject *)gen->gi_code)->co_name; - Py_INCREF(name); - return name; + Py_INCREF(op->gi_qualname); + return op->gi_qualname; } +static int +gen_set_qualname(PyGenObject *op, PyObject *value) +{ + PyObject *tmp; -PyDoc_STRVAR(gen__name__doc__, -"Return the name of the generator's associated code object."); + /* Not legal to del gen.__qualname__ or to set it to anything + * other than a string object. */ + if (value == NULL || !PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "__qualname__ must be set to a string object"); + return -1; + } + tmp = op->gi_qualname; + Py_INCREF(value); + op->gi_qualname = value; + Py_DECREF(tmp); + return 0; +} static PyGetSetDef gen_getsetlist[] = { - {"__name__", (getter)gen_get_name, NULL, gen__name__doc__}, - {NULL} + {"__name__", (getter)gen_get_name, (setter)gen_set_name, + PyDoc_STR("name of the generator")}, + {"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname, + PyDoc_STR("qualified name of the generator")}, + {NULL} /* Sentinel */ }; - static PyMemberDef gen_memberlist[] = { - {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, - {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, - {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, + {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, + {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, + {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, {NULL} /* Sentinel */ }; @@ -510,7 +554,7 @@ }; PyObject * -PyGen_New(PyFrameObject *f) +PyGen_NewWithQualName(PyFrameObject *f, PyObject *name, PyObject *qualname) { PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type); if (gen == NULL) { @@ -523,10 +567,26 @@ gen->gi_code = (PyObject *)(f->f_code); gen->gi_running = 0; gen->gi_weakreflist = NULL; + if (name != NULL) + gen->gi_name = name; + else + gen->gi_name = ((PyCodeObject *)gen->gi_code)->co_name; + Py_INCREF(gen->gi_name); + if (qualname != NULL) + gen->gi_qualname = qualname; + else + gen->gi_qualname = gen->gi_name; + Py_INCREF(gen->gi_qualname); _PyObject_GC_TRACK(gen); return (PyObject *)gen; } +PyObject * +PyGen_New(PyFrameObject *f) +{ + return PyGen_NewWithQualName(f, NULL, NULL); +} + int PyGen_NeedsFinalizing(PyGenObject *gen) { diff --git a/PCbuild/prepare_ssl.py b/PCbuild/prepare_ssl.py --- a/PCbuild/prepare_ssl.py +++ b/PCbuild/prepare_ssl.py @@ -186,7 +186,7 @@ ssl_dir = sys.argv[1] - if not os.path.exists(ssl_dir) and os.path.isdir(ssl_dir): + if not os.path.isdir(ssl_dir): print(ssl_dir, "is not an existing directory!") sys.exit(1) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1327,7 +1327,7 @@ PyDoc_STRVAR(len_doc, "len(object)\n\ \n\ -Return the number of items of a sequence or mapping."); +Return the number of items of a sequence or collection."); static PyObject * diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1267,6 +1267,13 @@ /* Other threads may run now */ take_gil(tstate); + + /* Check if we should make a quick exit. */ + if (_Py_Finalizing && _Py_Finalizing != tstate) { + drop_gil(tstate); + PyThread_exit_thread(); + } + if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); } @@ -3401,10 +3408,11 @@ PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust the test in the if statements in Misc/gdbinit (pystack and pystackv). */ -PyObject * -PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, +static PyObject * +_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname) { PyCodeObject* co = (PyCodeObject*)_co; PyFrameObject *f; @@ -3596,7 +3604,7 @@ /* Create a new generator that owns the ready to run frame * and return that as the value. */ - return PyGen_New(f); + return PyGen_NewWithQualName(f, name, qualname); } retval = PyEval_EvalFrameEx(f,0); @@ -3615,6 +3623,16 @@ return retval; } +PyObject * +PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, + PyObject **args, int argcount, PyObject **kws, int kwcount, + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) +{ + return _PyEval_EvalCodeWithName(_co, globals, locals, + args, argcount, kws, kwcount, + defs, defcount, kwdefs, closure, + NULL, NULL); +} static PyObject * special_lookup(PyObject *o, _Py_Identifier *id) @@ -4313,6 +4331,8 @@ PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func); + PyObject *name = ((PyFunctionObject *)func) -> func_name; + PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname; PyObject **d = NULL; int nd = 0; @@ -4355,10 +4375,11 @@ d = &PyTuple_GET_ITEM(argdefs, 0); nd = Py_SIZE(argdefs); } - return PyEval_EvalCodeEx((PyObject*)co, globals, - (PyObject *)NULL, (*pp_stack)-n, na, - (*pp_stack)-2*nk, nk, d, nd, kwdefs, - PyFunction_GET_CLOSURE(func)); + return _PyEval_EvalCodeWithName((PyObject*)co, globals, + (PyObject *)NULL, (*pp_stack)-n, na, + (*pp_stack)-2*nk, nk, d, nd, kwdefs, + PyFunction_GET_CLOSURE(func), + name, qualname); } static PyObject * -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 21 20:27:58 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 21 Jun 2014 20:27:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgMjE2MzU6?= =?utf-8?q?__Fix_caching_in_difflib=2ESequenceMatcher=2Eget=5Fmatching=5Fb?= =?utf-8?b?bG9ja3MoKS4=?= Message-ID: <3gwlk613qGz7LkC@mail.python.org> http://hg.python.org/cpython/rev/f02a563ad1bf changeset: 91307:f02a563ad1bf branch: 2.7 parent: 91301:20e65501cfa4 user: Raymond Hettinger date: Sat Jun 21 11:27:36 2014 -0700 summary: Issue 21635: Fix caching in difflib.SequenceMatcher.get_matching_blocks(). files: Lib/difflib.py | 4 ++-- Lib/test/test_difflib.py | 9 +++++++++ Misc/NEWS | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Lib/difflib.py b/Lib/difflib.py --- a/Lib/difflib.py +++ b/Lib/difflib.py @@ -523,8 +523,8 @@ non_adjacent.append((i1, j1, k1)) non_adjacent.append( (la, lb, 0) ) - self.matching_blocks = non_adjacent - return map(Match._make, self.matching_blocks) + self.matching_blocks = map(Match._make, non_adjacent) + return self.matching_blocks def get_opcodes(self): """Return list of 5-tuples describing how to turn a into b. diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py --- a/Lib/test/test_difflib.py +++ b/Lib/test/test_difflib.py @@ -59,6 +59,15 @@ diff_gen = difflib.unified_diff([], []) self.assertRaises(StopIteration, diff_gen.next) + def test_matching_blocks_cache(self): + # Issue #21635 + s = difflib.SequenceMatcher(None, "abxcd", "abcd") + first = s.get_matching_blocks() + second = s.get_matching_blocks() + self.assertEqual(second[0].size, 2) + self.assertEqual(second[1].size, 2) + self.assertEqual(second[2].size, 0) + def test_added_tab_hint(self): # Check fix for bug #1488943 diff = list(difflib.Differ().compare(["\tI am a buggy"],["\t\tI am a bug"])) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -31,6 +31,10 @@ - Issue #21491: SocketServer: Fix a race condition in child processes reaping. +- Issue #21635: The difflib SequenceMatcher.get_matching_blocks() method + cache didn't match the actual result. The former was a list of tuples + and the latter was a list of named tuples. + - Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 21 20:59:56 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 21 Jun 2014 20:59:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgMjE2MzU6?= =?utf-8?q?__Fix_caching_in_difflib=2ESequenceMatcher=2Eget=5Fmatching=5Fb?= =?utf-8?b?bG9ja3MoKS4=?= Message-ID: <3gwmR02c3Kz7LjN@mail.python.org> http://hg.python.org/cpython/rev/ed73c127421c changeset: 91308:ed73c127421c branch: 3.4 parent: 91303:9c5e9e2e0a09 user: Raymond Hettinger date: Sat Jun 21 11:57:36 2014 -0700 summary: Issue 21635: Fix caching in difflib.SequenceMatcher.get_matching_blocks(). files: Lib/difflib.py | 4 ++-- Lib/test/test_difflib.py | 9 +++++++++ Misc/NEWS | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Lib/difflib.py b/Lib/difflib.py --- a/Lib/difflib.py +++ b/Lib/difflib.py @@ -511,8 +511,8 @@ non_adjacent.append((i1, j1, k1)) non_adjacent.append( (la, lb, 0) ) - self.matching_blocks = non_adjacent - return map(Match._make, self.matching_blocks) + self.matching_blocks = list(map(Match._make, non_adjacent)) + return self.matching_blocks def get_opcodes(self): """Return list of 5-tuples describing how to turn a into b. diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py --- a/Lib/test/test_difflib.py +++ b/Lib/test/test_difflib.py @@ -76,6 +76,15 @@ diff_gen = difflib.unified_diff([], []) self.assertRaises(StopIteration, next, diff_gen) + def test_matching_blocks_cache(self): + # Issue #21635 + s = difflib.SequenceMatcher(None, "abxcd", "abcd") + first = s.get_matching_blocks() + second = s.get_matching_blocks() + self.assertEqual(second[0].size, 2) + self.assertEqual(second[1].size, 2) + self.assertEqual(second[2].size, 0) + def test_added_tab_hint(self): # Check fix for bug #1488943 diff = list(difflib.Differ().compare(["\tI am a buggy"],["\t\tI am a bug"])) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,10 @@ - Issue #21491: socketserver: Fix a race condition in child processes reaping. +- Issue #21635: The difflib SequenceMatcher.get_matching_blocks() method + cache didn't match the actual result. The former was a list of tuples + and the latter was a list of named tuples. + - Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 21 20:59:57 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 21 Jun 2014 20:59:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gwmR141TDz7Ljr@mail.python.org> http://hg.python.org/cpython/rev/769105445b22 changeset: 91309:769105445b22 parent: 91306:fdfa15a9243c parent: 91308:ed73c127421c user: Raymond Hettinger date: Sat Jun 21 11:59:46 2014 -0700 summary: merge files: Lib/difflib.py | 4 ++-- Lib/test/test_difflib.py | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Lib/difflib.py b/Lib/difflib.py --- a/Lib/difflib.py +++ b/Lib/difflib.py @@ -511,8 +511,8 @@ non_adjacent.append((i1, j1, k1)) non_adjacent.append( (la, lb, 0) ) - self.matching_blocks = non_adjacent - return map(Match._make, self.matching_blocks) + self.matching_blocks = list(map(Match._make, non_adjacent)) + return self.matching_blocks def get_opcodes(self): """Return list of 5-tuples describing how to turn a into b. diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py --- a/Lib/test/test_difflib.py +++ b/Lib/test/test_difflib.py @@ -76,6 +76,15 @@ diff_gen = difflib.unified_diff([], []) self.assertRaises(StopIteration, next, diff_gen) + def test_matching_blocks_cache(self): + # Issue #21635 + s = difflib.SequenceMatcher(None, "abxcd", "abcd") + first = s.get_matching_blocks() + second = s.get_matching_blocks() + self.assertEqual(second[0].size, 2) + self.assertEqual(second[1].size, 2) + self.assertEqual(second[2].size, 0) + def test_added_tab_hint(self): # Check fix for bug #1488943 diff = list(difflib.Differ().compare(["\tI am a buggy"],["\t\tI am a bug"])) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 21 21:08:32 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 21 Jun 2014 21:08:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_21786=3A__Clean-up_t?= =?utf-8?q?est=5Fpydoc_taking_taking_advantage_of_diffing_in?= Message-ID: <3gwmcw3zGHz7LjP@mail.python.org> http://hg.python.org/cpython/rev/b1c7f28f9c0a changeset: 91310:b1c7f28f9c0a user: Raymond Hettinger date: Sat Jun 21 12:08:22 2014 -0700 summary: Issue 21786: Clean-up test_pydoc taking taking advantage of diffing in unittest. files: Lib/test/test_pydoc.py | 40 +++++------------------------ 1 files changed, 7 insertions(+), 33 deletions(-) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -2,7 +2,6 @@ import sys import builtins import contextlib -import difflib import inspect import pydoc import keyword @@ -356,15 +355,6 @@ output = patt.sub('', output) return output.strip(), loc -def print_diffs(text1, text2): - "Prints unified diffs for two texts" - # XXX now obsolete, use unittest built-in support - lines1 = text1.splitlines(keepends=True) - lines2 = text2.splitlines(keepends=True) - diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected', - tofile='got') - print('\n' + ''.join(diffs)) - def get_html_title(text): # Bit of hack, but good enough for test purposes header, _, _ = text.partition("") @@ -414,9 +404,7 @@ expected_html = expected_html_pattern % ( (mod_url, mod_file, doc_loc) + expected_html_data_docstrings) - if result != expected_html: - print_diffs(expected_html, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(result, expected_html) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -429,9 +417,7 @@ (doc_loc,) + expected_text_data_docstrings + (inspect.getabsfile(pydoc_mod),)) - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) def test_text_enum_member_with_value_zero(self): # Test issue #20654 to ensure enum member with value 0 can be @@ -887,9 +873,7 @@ expected_text = expected_dynamicattribute_pattern % ( (__name__,) + expected_text_data_docstrings[:2]) result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -910,9 +894,7 @@ helper(Class) expected_text = expected_virtualattribute_pattern1 % __name__ result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -952,19 +934,13 @@ helper(Class1) expected_text1 = expected_virtualattribute_pattern2 % __name__ result1 = output.getvalue().strip() - if result1 != expected_text1: - print_diffs(expected_text1, result1) - fail1 = True + self.assertEqual(expected_text1, result1) output = StringIO() helper = pydoc.Helper(output=output) helper(Class2) expected_text2 = expected_virtualattribute_pattern3 % __name__ result2 = output.getvalue().strip() - if result2 != expected_text2: - print_diffs(expected_text2, result2) - fail2 = True - if fail1 or fail2: - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text2, result2) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -981,9 +957,7 @@ helper(C) expected_text = expected_missingattribute_pattern % __name__ result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) @reap_threads -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 07:21:14 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 22 Jun 2014 07:21:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxODIz?= =?utf-8?q?=3A_Catch_turtle=2ETerminator_exceptions_in_turtledemo=2E?= Message-ID: <3gx2Ct1jPfz7LkR@mail.python.org> http://hg.python.org/cpython/rev/a43d03cdf38b changeset: 91311:a43d03cdf38b branch: 2.7 parent: 91307:f02a563ad1bf user: Terry Jan Reedy date: Sun Jun 22 01:18:48 2014 -0400 summary: Issue #21823: Catch turtle.Terminator exceptions in turtledemo. Add note to demohelp.txt about doing so. files: Demo/turtle/demohelp.txt | 16 +++++--- Demo/turtle/tdemo_clock.py | 36 ++++++++++-------- Demo/turtle/tdemo_minimal_hanoi.py | 10 +++- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/Demo/turtle/demohelp.txt b/Demo/turtle/demohelp.txt --- a/Demo/turtle/demohelp.txt +++ b/Demo/turtle/demohelp.txt @@ -65,11 +65,15 @@ be executed by the viewer (see provided example scripts) main() may return a string which will be displayed in the Label below the source code window (when execution - has finished.) + has finished.) - !! For programs, which are EVENT DRIVEN, main must return - !! the string "EVENTLOOP". This informs the viewer, that the - !! script is still running and must be stopped by the user! + If the demo is EVENT DRIVEN, main must return the string + "EVENTLOOP". This informs the demo viewer that the script is + still running and must be stopped by the user! - - + If an "EVENTLOOP" demo runs by itself, as with clock, which uses + ontimer, or minimal_hanoi, which loops by recursion, then the + code should catch the turtle.Terminator exception that will be + raised when the user presses the STOP button. (Paint is not such + a demo; it only acts in response to mouse clicks and movements.) + diff --git a/Demo/turtle/tdemo_clock.py b/Demo/turtle/tdemo_clock.py --- a/Demo/turtle/tdemo_clock.py +++ b/Demo/turtle/tdemo_clock.py @@ -11,6 +11,7 @@ ------------------------------------ """ from turtle import * +from turtle import Terminator # not in __all__ from datetime import datetime mode("logo") @@ -102,22 +103,25 @@ sekunde = t.second + t.microsecond*0.000001 minute = t.minute + sekunde/60.0 stunde = t.hour + minute/60.0 - tracer(False) - writer.clear() - writer.home() - writer.forward(65) - writer.write(wochentag(t), - align="center", font=("Courier", 14, "bold")) - writer.back(150) - writer.write(datum(t), - align="center", font=("Courier", 14, "bold")) - writer.forward(85) - tracer(True) - second_hand.setheading(6*sekunde) - minute_hand.setheading(6*minute) - hour_hand.setheading(30*stunde) - tracer(True) - ontimer(tick, 100) + try: + tracer(False) # Terminator can occur here + writer.clear() + writer.home() + writer.forward(65) + writer.write(wochentag(t), + align="center", font=("Courier", 14, "bold")) + writer.back(150) + writer.write(datum(t), + align="center", font=("Courier", 14, "bold")) + writer.forward(85) + tracer(True) + second_hand.setheading(6*sekunde) # or here + minute_hand.setheading(6*minute) + hour_hand.setheading(30*stunde) + tracer(True) + ontimer(tick, 100) + except Terminator: + pass # turtledemo user pressed STOP def main(): tracer(False) diff --git a/Demo/turtle/tdemo_minimal_hanoi.py b/Demo/turtle/tdemo_minimal_hanoi.py --- a/Demo/turtle/tdemo_minimal_hanoi.py +++ b/Demo/turtle/tdemo_minimal_hanoi.py @@ -18,6 +18,7 @@ --------------------------------------- """ from turtle import * +from turtle import Terminator # not in __all__ class Disc(Turtle): def __init__(self, n): @@ -50,9 +51,12 @@ def play(): onkey(None,"space") clear() - hanoi(6, t1, t2, t3) - write("press STOP button to exit", - align="center", font=("Courier", 16, "bold")) + try: + hanoi(6, t1, t2, t3) + write("press STOP button to exit", + align="center", font=("Courier", 16, "bold")) + except Terminator: + pass # turtledemo user pressed STOP def main(): global t1, t2, t3 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 07:21:15 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 22 Jun 2014 07:21:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxODIz?= =?utf-8?q?=3A_Catch_turtle=2ETerminator_exceptions_in_turtledemo=2E?= Message-ID: <3gx2Cv5YwDz7Lm3@mail.python.org> http://hg.python.org/cpython/rev/1ae2382417dc changeset: 91312:1ae2382417dc branch: 3.4 parent: 91308:ed73c127421c user: Terry Jan Reedy date: Sun Jun 22 01:18:54 2014 -0400 summary: Issue #21823: Catch turtle.Terminator exceptions in turtledemo. Add note to demohelp.txt about doing so. files: Lib/turtledemo/clock.py | 36 +++++++++++--------- Lib/turtledemo/demohelp.txt | 16 +++++--- Lib/turtledemo/minimal_hanoi.py | 10 ++++- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/Lib/turtledemo/clock.py b/Lib/turtledemo/clock.py --- a/Lib/turtledemo/clock.py +++ b/Lib/turtledemo/clock.py @@ -11,6 +11,7 @@ ------------------------------------ """ from turtle import * +from turtle import Terminator # not in __all__ from datetime import datetime mode("logo") @@ -102,22 +103,25 @@ sekunde = t.second + t.microsecond*0.000001 minute = t.minute + sekunde/60.0 stunde = t.hour + minute/60.0 - tracer(False) - writer.clear() - writer.home() - writer.forward(65) - writer.write(wochentag(t), - align="center", font=("Courier", 14, "bold")) - writer.back(150) - writer.write(datum(t), - align="center", font=("Courier", 14, "bold")) - writer.forward(85) - tracer(True) - second_hand.setheading(6*sekunde) - minute_hand.setheading(6*minute) - hour_hand.setheading(30*stunde) - tracer(True) - ontimer(tick, 100) + try: + tracer(False) # Terminator can occur here + writer.clear() + writer.home() + writer.forward(65) + writer.write(wochentag(t), + align="center", font=("Courier", 14, "bold")) + writer.back(150) + writer.write(datum(t), + align="center", font=("Courier", 14, "bold")) + writer.forward(85) + tracer(True) + second_hand.setheading(6*sekunde) # or here + minute_hand.setheading(6*minute) + hour_hand.setheading(30*stunde) + tracer(True) + ontimer(tick, 100) + except Terminator: + pass # turtledemo user pressed STOP def main(): tracer(False) diff --git a/Lib/turtledemo/demohelp.txt b/Lib/turtledemo/demohelp.txt --- a/Lib/turtledemo/demohelp.txt +++ b/Lib/turtledemo/demohelp.txt @@ -60,11 +60,15 @@ be executed by the viewer (see provided example scripts) main() may return a string which will be displayed in the Label below the source code window (when execution - has finished.) + has finished.) - !! For programs, which are EVENT DRIVEN, main must return - !! the string "EVENTLOOP". This informs the viewer, that the - !! script is still running and must be stopped by the user! + If the demo is EVENT DRIVEN, main must return the string + "EVENTLOOP". This informs the demo viewer that the script is + still running and must be stopped by the user! - - + If an "EVENTLOOP" demo runs by itself, as with clock, which uses + ontimer, or minimal_hanoi, which loops by recursion, then the + code should catch the turtle.Terminator exception that will be + raised when the user presses the STOP button. (Paint is not such + a demo; it only acts in response to mouse clicks and movements.) + diff --git a/Lib/turtledemo/minimal_hanoi.py b/Lib/turtledemo/minimal_hanoi.py --- a/Lib/turtledemo/minimal_hanoi.py +++ b/Lib/turtledemo/minimal_hanoi.py @@ -18,6 +18,7 @@ --------------------------------------- """ from turtle import * +from turtle import Terminator # not in __all__ class Disc(Turtle): def __init__(self, n): @@ -50,9 +51,12 @@ def play(): onkey(None,"space") clear() - hanoi(6, t1, t2, t3) - write("press STOP button to exit", - align="center", font=("Courier", 16, "bold")) + try: + hanoi(6, t1, t2, t3) + write("press STOP button to exit", + align="center", font=("Courier", 16, "bold")) + except Terminator: + pass # turtledemo user pressed STOP def main(): global t1, t2, t3 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 07:21:17 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 22 Jun 2014 07:21:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gx2Cx289hz7LmZ@mail.python.org> http://hg.python.org/cpython/rev/3f4abe3107ce changeset: 91313:3f4abe3107ce parent: 91310:b1c7f28f9c0a parent: 91312:1ae2382417dc user: Terry Jan Reedy date: Sun Jun 22 01:20:52 2014 -0400 summary: Merge with 3.4 files: Lib/turtledemo/clock.py | 36 +++++---- Lib/turtledemo/demohelp.txt | 74 +++++++++++++++++++++ Lib/turtledemo/minimal_hanoi.py | 10 +- 3 files changed, 101 insertions(+), 19 deletions(-) diff --git a/Lib/turtledemo/clock.py b/Lib/turtledemo/clock.py --- a/Lib/turtledemo/clock.py +++ b/Lib/turtledemo/clock.py @@ -11,6 +11,7 @@ ------------------------------------ """ from turtle import * +from turtle import Terminator # not in __all__ from datetime import datetime mode("logo") @@ -102,22 +103,25 @@ sekunde = t.second + t.microsecond*0.000001 minute = t.minute + sekunde/60.0 stunde = t.hour + minute/60.0 - tracer(False) - writer.clear() - writer.home() - writer.forward(65) - writer.write(wochentag(t), - align="center", font=("Courier", 14, "bold")) - writer.back(150) - writer.write(datum(t), - align="center", font=("Courier", 14, "bold")) - writer.forward(85) - tracer(True) - second_hand.setheading(6*sekunde) - minute_hand.setheading(6*minute) - hour_hand.setheading(30*stunde) - tracer(True) - ontimer(tick, 100) + try: + tracer(False) # Terminator can occur here + writer.clear() + writer.home() + writer.forward(65) + writer.write(wochentag(t), + align="center", font=("Courier", 14, "bold")) + writer.back(150) + writer.write(datum(t), + align="center", font=("Courier", 14, "bold")) + writer.forward(85) + tracer(True) + second_hand.setheading(6*sekunde) # or here + minute_hand.setheading(6*minute) + hour_hand.setheading(30*stunde) + tracer(True) + ontimer(tick, 100) + except Terminator: + pass # turtledemo user pressed STOP def main(): tracer(False) diff --git a/Lib/turtledemo/demohelp.txt b/Lib/turtledemo/demohelp.txt new file mode 100644 --- /dev/null +++ b/Lib/turtledemo/demohelp.txt @@ -0,0 +1,74 @@ + + + ---------------------------------------------- + + turtleDemo - Help + + ---------------------------------------------- + + This document has two sections: + + (1) How to use the demo viewer + (2) How to add your own demos to the demo repository + + + (1) How to use the demo viewer. + + Select a demoscript from the example menu. + The (syntax coloured) source code appears in the left + source code window. IT CANNOT BE EDITED, but ONLY VIEWED! + + - Press START button to start the demo. + - Stop execution by pressing the STOP button. + - Clear screen by pressing the CLEAR button. + - Restart by pressing the START button again. + + SPECIAL demos are those which run EVENTDRIVEN. + (For example clock.py - or oldTurtleDemo.py which + in the end expects a mouse click.): + + Press START button to start the demo. + + - Until the EVENTLOOP is entered everything works + as in an ordinary demo script. + + - When the EVENTLOOP is entered, you control the + application by using the mouse and/or keys (or it's + controlled by some timer events) + To stop it you can and must press the STOP button. + + While the EVENTLOOP is running, the examples menu is disabled. + + - Only after having pressed the STOP button, you may + restart it or choose another example script. + + * * * * * * * * + In some rare situations there may occur interferences/conflicts + between events concerning the demo script and those concerning the + demo-viewer. (They run in the same process.) Strange behaviour may be + the consequence and in the worst case you must close and restart the + viewer. + * * * * * * * * + + + (2) How to add your own demos to the demo repository + + - place: same directory as turtledemo/__main__.py + + - requirements on source code: + code must contain a main() function which will + be executed by the viewer (see provided example scripts) + main() may return a string which will be displayed + in the Label below the source code window (when execution + has finished.) + + If the demo is EVENT DRIVEN, main must return the string + "EVENTLOOP". This informs the demo viewer that the script is + still running and must be stopped by the user! + + If an "EVENTLOOP" demo runs by itself, as with clock, which uses + ontimer, or minimal_hanoi, which loops by recursion, then the + code should catch the turtle.Terminator exception that will be + raised when the user presses the STOP button. (Paint is not such + a demo; it only acts in response to mouse clicks and movements.) + diff --git a/Lib/turtledemo/minimal_hanoi.py b/Lib/turtledemo/minimal_hanoi.py --- a/Lib/turtledemo/minimal_hanoi.py +++ b/Lib/turtledemo/minimal_hanoi.py @@ -18,6 +18,7 @@ --------------------------------------- """ from turtle import * +from turtle import Terminator # not in __all__ class Disc(Turtle): def __init__(self, n): @@ -50,9 +51,12 @@ def play(): onkey(None,"space") clear() - hanoi(6, t1, t2, t3) - write("press STOP button to exit", - align="center", font=("Courier", 16, "bold")) + try: + hanoi(6, t1, t2, t3) + write("press STOP button to exit", + align="center", font=("Courier", 16, "bold")) + except Terminator: + pass # turtledemo user pressed STOP def main(): global t1, t2, t3 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 08:32:42 2014 From: python-checkins at python.org (terry.reedy) Date: Sun, 22 Jun 2014 08:32:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxODI0?= =?utf-8?q?=3A_Turtledemo_2=2E7_help_menu_entries_now_display_help_text_in?= =?utf-8?q?stead?= Message-ID: <3gx3pL6g3Kz7LjV@mail.python.org> http://hg.python.org/cpython/rev/9778d37c2d18 changeset: 91314:9778d37c2d18 branch: 2.7 parent: 91311:a43d03cdf38b user: Terry Jan Reedy date: Sun Jun 22 02:32:26 2014 -0400 summary: Issue #21824: Turtledemo 2.7 help menu entries now display help text instead of help file name. files: Demo/turtle/turtleDemo.py | 26 +++++++++++++++----------- 1 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Demo/turtle/turtleDemo.py b/Demo/turtle/turtleDemo.py --- a/Demo/turtle/turtleDemo.py +++ b/Demo/turtle/turtleDemo.py @@ -5,11 +5,17 @@ from Tkinter import * from idlelib.Percolator import Percolator from idlelib.ColorDelegator import ColorDelegator -from idlelib.textView import TextViewer +from idlelib.textView import view_file import turtle import time +demo_dir = os.getcwd() +if "turtleDemo.py" not in os.listdir(demo_dir): + print "Directory of turtleDemo must be current working directory!" + print "But in your case this is", demo_dir + sys.exit() + STARTUP = 1 READY = 2 RUNNING = 3 @@ -21,12 +27,7 @@ txtfont = ('Lucida Console', 8, 'normal') def getExampleEntries(): - cwd = os.getcwd() - if "turtleDemo.py" not in os.listdir(cwd): - print "Directory of turtleDemo must be current working directory!" - print "But in your case this is", cwd - sys.exit() - entries1 = [entry for entry in os.listdir(cwd) if + entries1 = [entry for entry in os.listdir(demo_dir) if entry.startswith("tdemo_") and not entry.endswith(".pyc")] entries2 = [] @@ -34,7 +35,7 @@ if entry.endswith(".py"): entries2.append(entry) else: - path = os.path.join(cwd,entry) + path = os.path.join(demo_dir, entry) sys.path.append(path) subdir = [entry] scripts = [script for script in os.listdir(path) if @@ -44,13 +45,16 @@ return entries2 def showDemoHelp(): - TextViewer(demo.root, "Help on turtleDemo", "demohelp.txt") + view_file(demo.root, "Help on turtleDemo", + os.path.join(demo_dir, "demohelp.txt")) def showAboutDemo(): - TextViewer(demo.root, "About turtleDemo", "about_turtledemo.txt") + view_file(demo.root, "About turtleDemo", + os.path.join(demo_dir, "about_turtledemo.txt")) def showAboutTurtle(): - TextViewer(demo.root, "About the new turtle module", "about_turtle.txt") + view_file(demo.root, "About the new turtle module.", + os.path.join(demo_dir, "about_turtle.txt")) class DemoWindow(object): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 10:24:04 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 22 Jun 2014 10:24:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxODEy?= =?utf-8?q?=3A__Trigger_immediate_transformation_in_turtle=2Eshapetransfor?= =?utf-8?b?bSgpLg==?= Message-ID: <3gx6Gr0cRsz7LjV@mail.python.org> http://hg.python.org/cpython/rev/39b094798e14 changeset: 91315:39b094798e14 branch: 3.4 parent: 91312:1ae2382417dc user: Raymond Hettinger date: Sun Jun 22 01:21:51 2014 -0700 summary: Issue #21812: Trigger immediate transformation in turtle.shapetransform(). files: Lib/turtle.py | 2 +- Misc/ACKS | 1 + Misc/NEWS | 3 +++ 3 files changed, 5 insertions(+), 1 deletions(-) diff --git a/Lib/turtle.py b/Lib/turtle.py --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -2945,7 +2945,7 @@ self._stretchfactor = a11, a22 self._shearfactor = a12/a22 self._tilt = alfa - self._update() + self.pen(resizemode="user") def _polytrafo(self, poly): diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -235,6 +235,7 @@ Albert Chin-A-Young Adal Chiriliuc Matt Chisholm +Lita Cho Anders Chrigstr?m Tom Christiansen Vadim Chugunov diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,9 @@ - Issue #21491: socketserver: Fix a race condition in child processes reaping. +- Issue #21812: turtle.shapetransform did not tranform the turtle on the + first call. (Issue identified and fixed by Lita Cho.) + - Issue #21635: The difflib SequenceMatcher.get_matching_blocks() method cache didn't match the actual result. The former was a list of tuples and the latter was a list of named tuples. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 10:24:05 2014 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 22 Jun 2014 10:24:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gx6Gs2MxNz7Lkr@mail.python.org> http://hg.python.org/cpython/rev/3426d25a00a2 changeset: 91316:3426d25a00a2 parent: 91313:3f4abe3107ce parent: 91315:39b094798e14 user: Raymond Hettinger date: Sun Jun 22 01:23:55 2014 -0700 summary: merge files: Lib/turtle.py | 2 +- Misc/ACKS | 1 + 2 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/turtle.py b/Lib/turtle.py --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -2945,7 +2945,7 @@ self._stretchfactor = a11, a22 self._shearfactor = a12/a22 self._tilt = alfa - self._update() + self.pen(resizemode="user") def _polytrafo(self, poly): diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -236,6 +236,7 @@ Albert Chin-A-Young Adal Chiriliuc Matt Chisholm +Lita Cho Anders Chrigstr?m Tom Christiansen Vadim Chugunov -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sun Jun 22 10:55:56 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 22 Jun 2014 10:55:56 +0200 Subject: [Python-checkins] Daily reference leaks (b1c7f28f9c0a): sum=60 Message-ID: results for b1c7f28f9c0a on branch "default" -------------------------------------------- test_collections leaked [-2, -2, 0] references, sum=-4 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_multiprocessing_fork leaked [0, 38, 0] references, sum=38 test_multiprocessing_fork leaked [0, 17, 0] memory blocks, sum=17 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/reflogZfLhEh', '-x'] From python-checkins at python.org Sun Jun 22 12:44:17 2014 From: python-checkins at python.org (giampaolo.rodola) Date: Sun, 22 Jun 2014 12:44:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=236916=3A_attempt_to_fix_?= =?utf-8?q?BB_failure?= Message-ID: <3gx9Nd6NDZz7Lkr@mail.python.org> http://hg.python.org/cpython/rev/aeeb385e61e4 changeset: 91317:aeeb385e61e4 parent: 91306:fdfa15a9243c user: Giampaolo Rodola' date: Sun Jun 22 12:43:19 2014 +0200 summary: #6916: attempt to fix BB failure files: Lib/test/test_asynchat.py | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asynchat.py b/Lib/test/test_asynchat.py --- a/Lib/test/test_asynchat.py +++ b/Lib/test/test_asynchat.py @@ -263,7 +263,8 @@ def test_basic(self): with warnings.catch_warnings(record=True) as w: f = asynchat.fifo() - assert issubclass(w[0].category, DeprecationWarning) + if w: + assert issubclass(w[0].category, DeprecationWarning) f.push(7) f.push(b'a') self.assertEqual(len(f), 2) @@ -280,7 +281,8 @@ def test_given_list(self): with warnings.catch_warnings(record=True) as w: f = asynchat.fifo([b'x', 17, 3]) - assert issubclass(w[0].category, DeprecationWarning) + if w: + assert issubclass(w[0].category, DeprecationWarning) self.assertEqual(len(f), 3) self.assertEqual(f.pop(), (1, b'x')) self.assertEqual(f.pop(), (1, 17)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 12:44:19 2014 From: python-checkins at python.org (giampaolo.rodola) Date: Sun, 22 Jun 2014 12:44:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge_heads?= Message-ID: <3gx9Ng2CJBz7LmP@mail.python.org> http://hg.python.org/cpython/rev/3f3de8c47ff8 changeset: 91318:3f3de8c47ff8 parent: 91317:aeeb385e61e4 parent: 91316:3426d25a00a2 user: Giampaolo Rodola' date: Sun Jun 22 12:44:05 2014 +0200 summary: merge heads files: Lib/difflib.py | 4 +- Lib/test/test_difflib.py | 9 ++ Lib/test/test_pydoc.py | 40 +--------- Lib/turtle.py | 2 +- Lib/turtledemo/clock.py | 36 +++++---- Lib/turtledemo/demohelp.txt | 74 +++++++++++++++++++++ Lib/turtledemo/minimal_hanoi.py | 10 +- Misc/ACKS | 1 + 8 files changed, 121 insertions(+), 55 deletions(-) diff --git a/Lib/difflib.py b/Lib/difflib.py --- a/Lib/difflib.py +++ b/Lib/difflib.py @@ -511,8 +511,8 @@ non_adjacent.append((i1, j1, k1)) non_adjacent.append( (la, lb, 0) ) - self.matching_blocks = non_adjacent - return map(Match._make, self.matching_blocks) + self.matching_blocks = list(map(Match._make, non_adjacent)) + return self.matching_blocks def get_opcodes(self): """Return list of 5-tuples describing how to turn a into b. diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py --- a/Lib/test/test_difflib.py +++ b/Lib/test/test_difflib.py @@ -76,6 +76,15 @@ diff_gen = difflib.unified_diff([], []) self.assertRaises(StopIteration, next, diff_gen) + def test_matching_blocks_cache(self): + # Issue #21635 + s = difflib.SequenceMatcher(None, "abxcd", "abcd") + first = s.get_matching_blocks() + second = s.get_matching_blocks() + self.assertEqual(second[0].size, 2) + self.assertEqual(second[1].size, 2) + self.assertEqual(second[2].size, 0) + def test_added_tab_hint(self): # Check fix for bug #1488943 diff = list(difflib.Differ().compare(["\tI am a buggy"],["\t\tI am a bug"])) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -2,7 +2,6 @@ import sys import builtins import contextlib -import difflib import inspect import pydoc import keyword @@ -356,15 +355,6 @@ output = patt.sub('', output) return output.strip(), loc -def print_diffs(text1, text2): - "Prints unified diffs for two texts" - # XXX now obsolete, use unittest built-in support - lines1 = text1.splitlines(keepends=True) - lines2 = text2.splitlines(keepends=True) - diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected', - tofile='got') - print('\n' + ''.join(diffs)) - def get_html_title(text): # Bit of hack, but good enough for test purposes header, _, _ = text.partition("") @@ -414,9 +404,7 @@ expected_html = expected_html_pattern % ( (mod_url, mod_file, doc_loc) + expected_html_data_docstrings) - if result != expected_html: - print_diffs(expected_html, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(result, expected_html) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -429,9 +417,7 @@ (doc_loc,) + expected_text_data_docstrings + (inspect.getabsfile(pydoc_mod),)) - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) def test_text_enum_member_with_value_zero(self): # Test issue #20654 to ensure enum member with value 0 can be @@ -887,9 +873,7 @@ expected_text = expected_dynamicattribute_pattern % ( (__name__,) + expected_text_data_docstrings[:2]) result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -910,9 +894,7 @@ helper(Class) expected_text = expected_virtualattribute_pattern1 % __name__ result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -952,19 +934,13 @@ helper(Class1) expected_text1 = expected_virtualattribute_pattern2 % __name__ result1 = output.getvalue().strip() - if result1 != expected_text1: - print_diffs(expected_text1, result1) - fail1 = True + self.assertEqual(expected_text1, result1) output = StringIO() helper = pydoc.Helper(output=output) helper(Class2) expected_text2 = expected_virtualattribute_pattern3 % __name__ result2 = output.getvalue().strip() - if result2 != expected_text2: - print_diffs(expected_text2, result2) - fail2 = True - if fail1 or fail2: - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text2, result2) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -981,9 +957,7 @@ helper(C) expected_text = expected_missingattribute_pattern % __name__ result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) @reap_threads diff --git a/Lib/turtle.py b/Lib/turtle.py --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -2945,7 +2945,7 @@ self._stretchfactor = a11, a22 self._shearfactor = a12/a22 self._tilt = alfa - self._update() + self.pen(resizemode="user") def _polytrafo(self, poly): diff --git a/Lib/turtledemo/clock.py b/Lib/turtledemo/clock.py --- a/Lib/turtledemo/clock.py +++ b/Lib/turtledemo/clock.py @@ -11,6 +11,7 @@ ------------------------------------ """ from turtle import * +from turtle import Terminator # not in __all__ from datetime import datetime mode("logo") @@ -102,22 +103,25 @@ sekunde = t.second + t.microsecond*0.000001 minute = t.minute + sekunde/60.0 stunde = t.hour + minute/60.0 - tracer(False) - writer.clear() - writer.home() - writer.forward(65) - writer.write(wochentag(t), - align="center", font=("Courier", 14, "bold")) - writer.back(150) - writer.write(datum(t), - align="center", font=("Courier", 14, "bold")) - writer.forward(85) - tracer(True) - second_hand.setheading(6*sekunde) - minute_hand.setheading(6*minute) - hour_hand.setheading(30*stunde) - tracer(True) - ontimer(tick, 100) + try: + tracer(False) # Terminator can occur here + writer.clear() + writer.home() + writer.forward(65) + writer.write(wochentag(t), + align="center", font=("Courier", 14, "bold")) + writer.back(150) + writer.write(datum(t), + align="center", font=("Courier", 14, "bold")) + writer.forward(85) + tracer(True) + second_hand.setheading(6*sekunde) # or here + minute_hand.setheading(6*minute) + hour_hand.setheading(30*stunde) + tracer(True) + ontimer(tick, 100) + except Terminator: + pass # turtledemo user pressed STOP def main(): tracer(False) diff --git a/Lib/turtledemo/demohelp.txt b/Lib/turtledemo/demohelp.txt new file mode 100644 --- /dev/null +++ b/Lib/turtledemo/demohelp.txt @@ -0,0 +1,74 @@ + + + ---------------------------------------------- + + turtleDemo - Help + + ---------------------------------------------- + + This document has two sections: + + (1) How to use the demo viewer + (2) How to add your own demos to the demo repository + + + (1) How to use the demo viewer. + + Select a demoscript from the example menu. + The (syntax coloured) source code appears in the left + source code window. IT CANNOT BE EDITED, but ONLY VIEWED! + + - Press START button to start the demo. + - Stop execution by pressing the STOP button. + - Clear screen by pressing the CLEAR button. + - Restart by pressing the START button again. + + SPECIAL demos are those which run EVENTDRIVEN. + (For example clock.py - or oldTurtleDemo.py which + in the end expects a mouse click.): + + Press START button to start the demo. + + - Until the EVENTLOOP is entered everything works + as in an ordinary demo script. + + - When the EVENTLOOP is entered, you control the + application by using the mouse and/or keys (or it's + controlled by some timer events) + To stop it you can and must press the STOP button. + + While the EVENTLOOP is running, the examples menu is disabled. + + - Only after having pressed the STOP button, you may + restart it or choose another example script. + + * * * * * * * * + In some rare situations there may occur interferences/conflicts + between events concerning the demo script and those concerning the + demo-viewer. (They run in the same process.) Strange behaviour may be + the consequence and in the worst case you must close and restart the + viewer. + * * * * * * * * + + + (2) How to add your own demos to the demo repository + + - place: same directory as turtledemo/__main__.py + + - requirements on source code: + code must contain a main() function which will + be executed by the viewer (see provided example scripts) + main() may return a string which will be displayed + in the Label below the source code window (when execution + has finished.) + + If the demo is EVENT DRIVEN, main must return the string + "EVENTLOOP". This informs the demo viewer that the script is + still running and must be stopped by the user! + + If an "EVENTLOOP" demo runs by itself, as with clock, which uses + ontimer, or minimal_hanoi, which loops by recursion, then the + code should catch the turtle.Terminator exception that will be + raised when the user presses the STOP button. (Paint is not such + a demo; it only acts in response to mouse clicks and movements.) + diff --git a/Lib/turtledemo/minimal_hanoi.py b/Lib/turtledemo/minimal_hanoi.py --- a/Lib/turtledemo/minimal_hanoi.py +++ b/Lib/turtledemo/minimal_hanoi.py @@ -18,6 +18,7 @@ --------------------------------------- """ from turtle import * +from turtle import Terminator # not in __all__ class Disc(Turtle): def __init__(self, n): @@ -50,9 +51,12 @@ def play(): onkey(None,"space") clear() - hanoi(6, t1, t2, t3) - write("press STOP button to exit", - align="center", font=("Courier", 16, "bold")) + try: + hanoi(6, t1, t2, t3) + write("press STOP button to exit", + align="center", font=("Courier", 16, "bold")) + except Terminator: + pass # turtledemo user pressed STOP def main(): global t1, t2, t3 diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -236,6 +236,7 @@ Albert Chin-A-Young Adal Chiriliuc Matt Chisholm +Lita Cho Anders Chrigstr?m Tom Christiansen Vadim Chugunov -- Repository URL: http://hg.python.org/cpython From root at python.org Sun Jun 22 14:10:22 2014 From: root at python.org (Cron Daemon) Date: Sun, 22 Jun 2014 14:10:22 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From root at python.org Sun Jun 22 14:15:23 2014 From: root at python.org (Cron Daemon) Date: Sun, 22 Jun 2014 14:15:23 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From python-checkins at python.org Sun Jun 22 22:29:10 2014 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 22 Jun 2014 22:29:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzEwNzQ3?= =?utf-8?q?=3A_Use_versioned_labels_in_the_Windows_start_menu=2E?= Message-ID: <3gxQMV28Vnz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/b896fe4b1201 changeset: 91319:b896fe4b1201 branch: 3.4 parent: 91315:39b094798e14 user: Martin v. L?wis date: Sun Jun 22 22:22:33 2014 +0200 summary: Issue #10747: Use versioned labels in the Windows start menu. Patch by Olive Kilburn. files: Misc/NEWS | 3 +++ Tools/msi/msi.py | 31 +++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -157,6 +157,9 @@ - Issue #21671, CVE-2014-0224: The bundled version of OpenSSL has been updated to 1.0.1h. +- Issue #10747: Use versioned labels in the Windows start menu. + Patch by Olive Kilburn. + Tools/Demos ----------- diff --git a/Tools/msi/msi.py b/Tools/msi/msi.py --- a/Tools/msi/msi.py +++ b/Tools/msi/msi.py @@ -1320,27 +1320,38 @@ add_data(db, "RemoveFile", [("MenuDir", "TARGETDIR", None, "MenuDir", 2)]) tcltkshortcuts = [] + if msilib.Win64: + bitted = "64 bit" + else: + bitted = "32 bit" if have_tcl: tcltkshortcuts = [ - ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe", - tcltk.id, r'"[TARGETDIR]Lib\idlelib\idle.pyw"', None, None, "python_icon.exe", 0, None, "TARGETDIR"), + ("IDLE", "MenuDir", + "IDLE|IDLE (Python "+short_version+" GUI - "+bitted+")", + "pythonw.exe", tcltk.id, r'"[TARGETDIR]Lib\idlelib\idle.pyw"', + None, None, "python_icon.exe", 0, None, "TARGETDIR"), ] add_data(db, "Shortcut", tcltkshortcuts + [# Advertised shortcuts: targets are features, not files - ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe", - default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"), + ("Python", "MenuDir", + "PYTHON|Python "+short_version+" (command line - "+bitted+")", + "python.exe", default_feature.id, None, None, None, + "python_icon.exe", 2, None, "TARGETDIR"), # Advertising the Manual breaks on (some?) Win98, and the shortcut lacks an # icon first. #("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation", # htmlfiles.id, None, None, None, None, None, None, None), ## Non-advertised shortcuts: must be associated with a registry component - ("Manual", "MenuDir", "MANUAL|Python Manuals", "REGISTRY.doc", - "[#%s]" % docfile, None, - None, None, None, None, None, None), - ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "python.exe", - default_feature.id, r'-m pydoc -b', None, None, "python_icon.exe", 0, None, "TARGETDIR"), - ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY", + ("Manual", "MenuDir", "MANUAL|Python "+short_version+" Manuals", + "REGISTRY.doc", "[#%s]" % docfile, + None, None, None, None, None, None, None), + ("PyDoc", "MenuDir", + "MODDOCS|Python "+short_version+" Docs Server (pydoc - "+ + bitted+")", "python.exe", default_feature.id, r'-m pydoc -b', + None, None, "python_icon.exe", 0, None, "TARGETDIR"), + ("Uninstall", "MenuDir", "UNINST|Uninstall Python "+ + short_version+" ("+bitted+")", "REGISTRY", SystemFolderName+"msiexec", "/x%s" % product_code, None, None, None, None, None, None), ]) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 22:29:11 2014 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 22 Jun 2014 22:29:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2310747=3A_Merge_with_3=2E4?= Message-ID: <3gxQMW3rxCz7Lk2@mail.python.org> http://hg.python.org/cpython/rev/3ca598c3437f changeset: 91320:3ca598c3437f parent: 91318:3f3de8c47ff8 parent: 91319:b896fe4b1201 user: Martin v. L?wis date: Sun Jun 22 22:28:04 2014 +0200 summary: Issue #10747: Merge with 3.4 files: Misc/NEWS | 4 +++- Tools/msi/msi.py | 31 +++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -1,4 +1,4 @@ -+++++++++++ +?+++++++++++ Python News +++++++++++ @@ -685,6 +685,8 @@ - Issue #21671, CVE-2014-0224: The bundled version of OpenSSL has been updated to 1.0.1h. +- Issue #10747: Use versioned labels in the Windows start menu. + Patch by Olive Kilburn. What's New in Python 3.4.0? =========================== diff --git a/Tools/msi/msi.py b/Tools/msi/msi.py --- a/Tools/msi/msi.py +++ b/Tools/msi/msi.py @@ -1320,27 +1320,38 @@ add_data(db, "RemoveFile", [("MenuDir", "TARGETDIR", None, "MenuDir", 2)]) tcltkshortcuts = [] + if msilib.Win64: + bitted = "64 bit" + else: + bitted = "32 bit" if have_tcl: tcltkshortcuts = [ - ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe", - tcltk.id, r'"[TARGETDIR]Lib\idlelib\idle.pyw"', None, None, "python_icon.exe", 0, None, "TARGETDIR"), + ("IDLE", "MenuDir", + "IDLE|IDLE (Python "+short_version+" GUI - "+bitted+")", + "pythonw.exe", tcltk.id, r'"[TARGETDIR]Lib\idlelib\idle.pyw"', + None, None, "python_icon.exe", 0, None, "TARGETDIR"), ] add_data(db, "Shortcut", tcltkshortcuts + [# Advertised shortcuts: targets are features, not files - ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe", - default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"), + ("Python", "MenuDir", + "PYTHON|Python "+short_version+" (command line - "+bitted+")", + "python.exe", default_feature.id, None, None, None, + "python_icon.exe", 2, None, "TARGETDIR"), # Advertising the Manual breaks on (some?) Win98, and the shortcut lacks an # icon first. #("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation", # htmlfiles.id, None, None, None, None, None, None, None), ## Non-advertised shortcuts: must be associated with a registry component - ("Manual", "MenuDir", "MANUAL|Python Manuals", "REGISTRY.doc", - "[#%s]" % docfile, None, - None, None, None, None, None, None), - ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "python.exe", - default_feature.id, r'-m pydoc -b', None, None, "python_icon.exe", 0, None, "TARGETDIR"), - ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY", + ("Manual", "MenuDir", "MANUAL|Python "+short_version+" Manuals", + "REGISTRY.doc", "[#%s]" % docfile, + None, None, None, None, None, None, None), + ("PyDoc", "MenuDir", + "MODDOCS|Python "+short_version+" Docs Server (pydoc - "+ + bitted+")", "python.exe", default_feature.id, r'-m pydoc -b', + None, None, "python_icon.exe", 0, None, "TARGETDIR"), + ("Uninstall", "MenuDir", "UNINST|Uninstall Python "+ + short_version+" ("+bitted+")", "REGISTRY", SystemFolderName+"msiexec", "/x%s" % product_code, None, None, None, None, None, None), ]) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 23:17:56 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 22 Jun 2014 23:17:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_add_BufferedIOBase=2Ereadi?= =?utf-8?q?nto1_=28closes_=2320578=29?= Message-ID: <3gxRRm6hCPz7LjW@mail.python.org> http://hg.python.org/cpython/rev/213490268be4 changeset: 91321:213490268be4 user: Benjamin Peterson date: Sun Jun 22 14:17:44 2014 -0700 summary: add BufferedIOBase.readinto1 (closes #20578) Patch by Nikolaus Rath. files: Doc/library/io.rst | 26 ++++++- Lib/_pyio.py | 95 +++++++++++++++++++++++++-- Lib/test/test_io.py | 67 +++++++++++++++++++ Modules/_io/bufferedio.c | 66 +++++++++++++++++- 4 files changed, 235 insertions(+), 19 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -465,10 +465,11 @@ .. method:: read1(size=-1) - Read and return up to *size* bytes, with at most one call to the underlying - raw stream's :meth:`~RawIOBase.read` method. This can be useful if you - are implementing your own buffering on top of a :class:`BufferedIOBase` - object. + Read and return up to *size* bytes, with at most one call to the + underlying raw stream's :meth:`~RawIOBase.read` (or + :meth:`~RawIOBase.readinto`) method. This can be useful if you + are implementing your own buffering on top of a + :class:`BufferedIOBase` object. .. method:: readinto(b) @@ -481,6 +482,18 @@ A :exc:`BlockingIOError` is raised if the underlying raw stream is in non blocking-mode, and has no data available at the moment. + .. method:: readinto1(b) + + Read up to ``len(b)`` bytes into bytearray *b*, ,using at most + one call to the underlying raw stream's :meth:`~RawIOBase.read` + (or :meth:`~RawIOBase.readinto`) method. Return the number of + bytes read. + + A :exc:`BlockingIOError` is raised if the underlying raw stream is in + non blocking-mode, and has no data available at the moment. + + .. versionadded:: 3.5 + .. method:: write(b) Write the given :class:`bytes` or :class:`bytearray` object, *b* and @@ -596,6 +609,11 @@ In :class:`BytesIO`, this is the same as :meth:`read`. + .. method:: readinto1() + + In :class:`BytesIO`, this is the same as :meth:`readinto`. + + .. versionadded:: 3.5 .. class:: BufferedReader(raw, buffer_size=DEFAULT_BUFFER_SIZE) diff --git a/Lib/_pyio.py b/Lib/_pyio.py --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -6,6 +6,7 @@ import abc import codecs import errno +import array # Import _thread instead of threading to reduce startup cost try: from _thread import allocate_lock as Lock @@ -662,16 +663,33 @@ Raises BlockingIOError if the underlying raw stream has no data at the moment. """ - # XXX This ought to work with anything that supports the buffer API - data = self.read(len(b)) + + return self._readinto(b, read1=False) + + def readinto1(self, b): + """Read up to len(b) bytes into *b*, using at most one system call + + Returns an int representing the number of bytes read (0 for EOF). + + Raises BlockingIOError if the underlying raw stream has no + data at the moment. + """ + + return self._readinto(b, read1=True) + + def _readinto(self, b, read1): + if not isinstance(b, memoryview): + b = memoryview(b) + b = b.cast('B') + + if read1: + data = self.read1(len(b)) + else: + data = self.read(len(b)) n = len(data) - try: - b[:n] = data - except TypeError as err: - import array - if not isinstance(b, array.array): - raise err - b[:n] = array.array('b', data) + + b[:n] = data + return n def write(self, b): @@ -1065,6 +1083,58 @@ return self._read_unlocked( min(size, len(self._read_buf) - self._read_pos)) + # Implementing readinto() and readinto1() is not strictly necessary (we + # could rely on the base class that provides an implementation in terms of + # read() and read1()). We do it anyway to keep the _pyio implementation + # similar to the io implementation (which implements the methods for + # performance reasons). + def _readinto(self, buf, read1): + """Read data into *buf* with at most one system call.""" + + if len(buf) == 0: + return 0 + + # Need to create a memoryview object of type 'b', otherwise + # we may not be able to assign bytes to it, and slicing it + # would create a new object. + if not isinstance(buf, memoryview): + buf = memoryview(buf) + buf = buf.cast('B') + + written = 0 + with self._read_lock: + while written < len(buf): + + # First try to read from internal buffer + avail = min(len(self._read_buf) - self._read_pos, len(buf)) + if avail: + buf[written:written+avail] = \ + self._read_buf[self._read_pos:self._read_pos+avail] + self._read_pos += avail + written += avail + if written == len(buf): + break + + # If remaining space in callers buffer is larger than + # internal buffer, read directly into callers buffer + if len(buf) - written > self.buffer_size: + n = self.raw.readinto(buf[written:]) + if not n: + break # eof + written += n + + # Otherwise refill internal buffer - unless we're + # in read1 mode and already got some data + elif not (read1 and written): + if not self._peek_unlocked(1): + break # eof + + # In readinto1 mode, return as soon as we have some data + if read1 and written: + break + + return written + def tell(self): return _BufferedIOMixin.tell(self) - len(self._read_buf) + self._read_pos @@ -1214,6 +1284,9 @@ def read1(self, size): return self.reader.read1(size) + def readinto1(self, b): + return self.reader.readinto1(b) + def readable(self): return self.reader.readable() @@ -1296,6 +1369,10 @@ self.flush() return BufferedReader.read1(self, size) + def readinto1(self, b): + self.flush() + return BufferedReader.readinto1(self, b) + def write(self, b): if self._read_buf: # Undo readahead 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 @@ -943,6 +943,71 @@ self.assertEqual(bufio.readinto(b), 1) self.assertEqual(b, b"cb") + def test_readinto1(self): + buffer_size = 10 + rawio = self.MockRawIO((b"abc", b"de", b"fgh", b"jkl")) + bufio = self.tp(rawio, buffer_size=buffer_size) + b = bytearray(2) + self.assertEqual(bufio.peek(3), b'abc') + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 2) + self.assertEqual(b, b"ab") + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 1) + self.assertEqual(b[:1], b"c") + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 2) + self.assertEqual(b, b"de") + self.assertEqual(rawio._reads, 2) + b = bytearray(2*buffer_size) + self.assertEqual(bufio.peek(3), b'fgh') + self.assertEqual(rawio._reads, 3) + self.assertEqual(bufio.readinto1(b), 6) + self.assertEqual(b[:6], b"fghjkl") + self.assertEqual(rawio._reads, 4) + + def test_readinto_array(self): + buffer_size = 60 + data = b"a" * 26 + rawio = self.MockRawIO((data,)) + bufio = self.tp(rawio, buffer_size=buffer_size) + + # Create an array with element size > 1 byte + b = array.array('i', b'x' * 32) + assert len(b) != 16 + + # Read into it. We should get as many *bytes* as we can fit into b + # (which is more than the number of elements) + n = bufio.readinto(b) + self.assertGreater(n, len(b)) + + # Check that old contents of b are preserved + bm = memoryview(b).cast('B') + self.assertLess(n, len(bm)) + self.assertEqual(bm[:n], data[:n]) + self.assertEqual(bm[n:], b'x' * (len(bm[n:]))) + + def test_readinto1_array(self): + buffer_size = 60 + data = b"a" * 26 + rawio = self.MockRawIO((data,)) + bufio = self.tp(rawio, buffer_size=buffer_size) + + # Create an array with element size > 1 byte + b = array.array('i', b'x' * 32) + assert len(b) != 16 + + # Read into it. We should get as many *bytes* as we can fit into b + # (which is more than the number of elements) + n = bufio.readinto1(b) + self.assertGreater(n, len(b)) + + # Check that old contents of b are preserved + bm = memoryview(b).cast('B') + self.assertLess(n, len(bm)) + self.assertEqual(bm[:n], data[:n]) + self.assertEqual(bm[n:], b'x' * (len(bm[n:]))) + def test_readlines(self): def bufio(): rawio = self.MockRawIO((b"abc\n", b"d\n", b"ef")) @@ -3050,6 +3115,8 @@ self.assertRaises(ValueError, f.readall) if hasattr(f, "readinto"): self.assertRaises(ValueError, f.readinto, bytearray(1024)) + if hasattr(f, "readinto1"): + self.assertRaises(ValueError, f.readinto1, bytearray(1024)) self.assertRaises(ValueError, f.readline) self.assertRaises(ValueError, f.readlines) self.assertRaises(ValueError, f.seek, 0) diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -24,6 +24,7 @@ _Py_IDENTIFIER(read1); _Py_IDENTIFIER(readable); _Py_IDENTIFIER(readinto); +_Py_IDENTIFIER(readinto1); _Py_IDENTIFIER(writable); _Py_IDENTIFIER(write); @@ -47,17 +48,21 @@ ); static PyObject * -bufferediobase_readinto(PyObject *self, PyObject *args) +_bufferediobase_readinto_generic(PyObject *self, PyObject *args, char readinto1) { Py_buffer buf; Py_ssize_t len; PyObject *data; - if (!PyArg_ParseTuple(args, "w*:readinto", &buf)) { + if (!PyArg_ParseTuple(args, + readinto1 ? "w*:readinto1" : "w*:readinto", + &buf)) { return NULL; } - data = _PyObject_CallMethodId(self, &PyId_read, "n", buf.len); + data = _PyObject_CallMethodId(self, + readinto1 ? &PyId_read1 : &PyId_read, + "n", buf.len); if (data == NULL) goto error; @@ -89,6 +94,18 @@ } static PyObject * +bufferediobase_readinto(PyObject *self, PyObject *args) +{ + return _bufferediobase_readinto_generic(self, args, 0); +} + +static PyObject * +bufferediobase_readinto1(PyObject *self, PyObject *args) +{ + return _bufferediobase_readinto_generic(self, args, 1); +} + +static PyObject * bufferediobase_unsupported(const char *message) { _PyIO_State *state = IO_STATE(); @@ -167,6 +184,7 @@ {"read", bufferediobase_read, METH_VARARGS, bufferediobase_read_doc}, {"read1", bufferediobase_read1, METH_VARARGS, bufferediobase_read1_doc}, {"readinto", bufferediobase_readinto, METH_VARARGS, NULL}, + {"readinto1", bufferediobase_readinto1, METH_VARARGS, NULL}, {"write", bufferediobase_write, METH_VARARGS, bufferediobase_write_doc}, {NULL, NULL} }; @@ -989,7 +1007,7 @@ } static PyObject * -buffered_readinto(buffered *self, PyObject *args) +_buffered_readinto_generic(buffered *self, PyObject *args, char readinto1) { Py_buffer buf; Py_ssize_t n, written = 0, remaining; @@ -997,7 +1015,9 @@ CHECK_INITIALIZED(self) - if (!PyArg_ParseTuple(args, "w*:readinto", &buf)) + if (!PyArg_ParseTuple(args, + readinto1 ? "w*:readinto1" : "w*:readinto", + &buf)) return NULL; n = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t); @@ -1035,7 +1055,10 @@ n = _bufferedreader_raw_read(self, (char *) buf.buf + written, remaining); } - else { + + /* In readinto1 mode, we do not want to fill the internal + buffer if we already have some data to return */ + else if (!(readinto1 && written)) { n = _bufferedreader_fill_buffer(self); if (n > 0) { if (n > remaining) @@ -1046,6 +1069,9 @@ continue; /* short circuit */ } } + else + n = 0; + if (n == 0 || (n == -2 && written > 0)) break; if (n < 0) { @@ -1055,6 +1081,12 @@ } goto end; } + + /* At most one read in readinto1 mode */ + if (readinto1) { + written += n; + break; + } } res = PyLong_FromSsize_t(written); @@ -1066,6 +1098,19 @@ } static PyObject * +buffered_readinto(buffered *self, PyObject *args) +{ + return _buffered_readinto_generic(self, args, 0); +} + +static PyObject * +buffered_readinto1(buffered *self, PyObject *args) +{ + return _buffered_readinto_generic(self, args, 1); +} + + +static PyObject * _buffered_readline(buffered *self, Py_ssize_t limit) { PyObject *res = NULL; @@ -1750,6 +1795,7 @@ {"peek", (PyCFunction)buffered_peek, METH_VARARGS}, {"read1", (PyCFunction)buffered_read1, METH_VARARGS}, {"readinto", (PyCFunction)buffered_readinto, METH_VARARGS}, + {"readinto1", (PyCFunction)buffered_readinto1, METH_VARARGS}, {"readline", (PyCFunction)buffered_readline, METH_VARARGS}, {"seek", (PyCFunction)buffered_seek, METH_VARARGS}, {"tell", (PyCFunction)buffered_tell, METH_NOARGS}, @@ -2349,6 +2395,12 @@ } static PyObject * +bufferedrwpair_readinto1(rwpair *self, PyObject *args) +{ + return _forward_call(self->reader, &PyId_readinto1, args); +} + +static PyObject * bufferedrwpair_write(rwpair *self, PyObject *args) { return _forward_call(self->writer, &PyId_write, args); @@ -2413,6 +2465,7 @@ {"peek", (PyCFunction)bufferedrwpair_peek, METH_VARARGS}, {"read1", (PyCFunction)bufferedrwpair_read1, METH_VARARGS}, {"readinto", (PyCFunction)bufferedrwpair_readinto, METH_VARARGS}, + {"readinto1", (PyCFunction)bufferedrwpair_readinto1, METH_VARARGS}, {"write", (PyCFunction)bufferedrwpair_write, METH_VARARGS}, {"flush", (PyCFunction)bufferedrwpair_flush, METH_NOARGS}, @@ -2561,6 +2614,7 @@ {"read", (PyCFunction)buffered_read, METH_VARARGS}, {"read1", (PyCFunction)buffered_read1, METH_VARARGS}, {"readinto", (PyCFunction)buffered_readinto, METH_VARARGS}, + {"readinto1", (PyCFunction)buffered_readinto1, METH_VARARGS}, {"readline", (PyCFunction)buffered_readline, METH_VARARGS}, {"peek", (PyCFunction)buffered_peek, METH_VARARGS}, {"write", (PyCFunction)bufferedwriter_write, METH_VARARGS}, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 22 23:22:20 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 22 Jun 2014 23:22:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_reflow_to_80_chars?= Message-ID: <3gxRXr6hJlz7LjT@mail.python.org> http://hg.python.org/cpython/rev/588d16400736 changeset: 91322:588d16400736 user: Benjamin Peterson date: Sun Jun 22 14:19:07 2014 -0700 summary: reflow to 80 chars files: Doc/library/io.rst | 25 ++++++++++++------------- 1 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -391,8 +391,8 @@ .. method:: readinto(b) Read up to ``len(b)`` bytes into :class:`bytearray` *b* and return the - number of bytes read. If the object is in non-blocking mode and no - bytes are available, ``None`` is returned. + number of bytes read. If the object is in non-blocking mode and no bytes + are available, ``None`` is returned. .. method:: write(b) @@ -467,9 +467,9 @@ Read and return up to *size* bytes, with at most one call to the underlying raw stream's :meth:`~RawIOBase.read` (or - :meth:`~RawIOBase.readinto`) method. This can be useful if you - are implementing your own buffering on top of a - :class:`BufferedIOBase` object. + :meth:`~RawIOBase.readinto`) method. This can be useful if you are + implementing your own buffering on top of a :class:`BufferedIOBase` + object. .. method:: readinto(b) @@ -479,18 +479,17 @@ Like :meth:`read`, multiple reads may be issued to the underlying raw stream, unless the latter is interactive. - A :exc:`BlockingIOError` is raised if the underlying raw stream is in - non blocking-mode, and has no data available at the moment. + A :exc:`BlockingIOError` is raised if the underlying raw stream is in non + blocking-mode, and has no data available at the moment. .. method:: readinto1(b) - Read up to ``len(b)`` bytes into bytearray *b*, ,using at most - one call to the underlying raw stream's :meth:`~RawIOBase.read` - (or :meth:`~RawIOBase.readinto`) method. Return the number of - bytes read. + Read up to ``len(b)`` bytes into bytearray *b*, ,using at most one call to + the underlying raw stream's :meth:`~RawIOBase.read` (or + :meth:`~RawIOBase.readinto`) method. Return the number of bytes read. - A :exc:`BlockingIOError` is raised if the underlying raw stream is in - non blocking-mode, and has no data available at the moment. + A :exc:`BlockingIOError` is raised if the underlying raw stream is in non + blocking-mode, and has no data available at the moment. .. versionadded:: 3.5 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:05:20 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:05:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogQmFz?= =?utf-8?q?eEventLoop=2E=5Fassert=5Fis=5Fcurrent=5Fevent=5Floop=28=29_now_?= =?utf-8?q?only_raises_an?= Message-ID: <3gxSVS6xhYz7LjN@mail.python.org> http://hg.python.org/cpython/rev/4cd3e1f9c250 changeset: 91323:4cd3e1f9c250 branch: 3.4 parent: 91319:b896fe4b1201 user: Victor Stinner date: Mon Jun 23 00:03:43 2014 +0200 summary: asyncio: BaseEventLoop._assert_is_current_event_loop() now only raises an exception if the current loop is not None. Guido van Rossum wrote: "The behavior that you can set the loop to None (and keep track of it explicitly) is part of the spec, and this should still be supported even in debug mode. The behavior that we raise an error if you are caught having multiple active loops per thread is just a debugging heuristic, and it shouldn't break code that follows the spec." files: Lib/asyncio/base_events.py | 3 ++- 1 files changed, 2 insertions(+), 1 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 @@ -327,7 +327,8 @@ Should only be called when (self._debug == True). The caller is responsible for checking this condition for performance reasons. """ - if events.get_event_loop() is not self: + current = events.get_event_loop() + if current is not None and current is not self: raise RuntimeError( "non-threadsafe operation invoked on an event loop other " "than the current one") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:05:22 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:05:22 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogKE1lcmdlIDMuNCkgYXN5bmNpbzogQmFzZUV2ZW50TG9vcC5fYXNzZXJ0?= =?utf-8?b?X2lzX2N1cnJlbnRfZXZlbnRfbG9vcCgpIG5vdyBvbmx5?= Message-ID: <3gxSVV1Wh3z7LjW@mail.python.org> http://hg.python.org/cpython/rev/94570709cae9 changeset: 91324:94570709cae9 parent: 91322:588d16400736 parent: 91323:4cd3e1f9c250 user: Victor Stinner date: Mon Jun 23 00:04:00 2014 +0200 summary: (Merge 3.4) asyncio: BaseEventLoop._assert_is_current_event_loop() now only raises an exception if the current loop is not None. Guido van Rossum wrote: "The behavior that you can set the loop to None (and keep track of it explicitly) is part of the spec, and this should still be supported even in debug mode. The behavior that we raise an error if you are caught having multiple active loops per thread is just a debugging heuristic, and it shouldn't break code that follows the spec." files: Lib/asyncio/base_events.py | 3 ++- 1 files changed, 2 insertions(+), 1 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 @@ -327,7 +327,8 @@ Should only be called when (self._debug == True). The caller is responsible for checking this condition for performance reasons. """ - if events.get_event_loop() is not self: + current = events.get_event_loop() + if current is not None and current is not self: raise RuntimeError( "non-threadsafe operation invoked on an event loop other " "than the current one") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:15:38 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:15:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogRW5h?= =?utf-8?q?ble_the_debug_mode_of_event_loops_when_the_PYTHONASYNCIODEBUG?= Message-ID: <3gxSkL6y5pz7Ljq@mail.python.org> http://hg.python.org/cpython/rev/2228222faa7e changeset: 91325:2228222faa7e branch: 3.4 parent: 91323:4cd3e1f9c250 user: Victor Stinner date: Mon Jun 23 00:12:14 2014 +0200 summary: asyncio: Enable the debug mode of event loops when the PYTHONASYNCIODEBUG environment variable is set files: Doc/library/asyncio-eventloop.rst | 6 +++++- Lib/asyncio/base_events.py | 3 ++- Lib/test/test_asyncio/test_selector_events.py | 2 -- Lib/test/test_asyncio/test_subprocess.py | 4 ++-- Lib/test/test_asyncio/test_tasks.py | 2 ++ Lib/test/test_asyncio/test_unix_events.py | 8 -------- 6 files changed, 11 insertions(+), 14 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 @@ -585,7 +585,11 @@ .. method:: BaseEventLoop.get_debug() - Get the debug mode (:class:`bool`) of the event loop, ``False`` by default. + Get the debug mode (:class:`bool`) of the event loop. + + The default value is ``True`` if the environment variable + :envvar:`PYTHONASYNCIODEBUG` is set to a non-empty string, ``False`` + otherwise. .. versionadded:: 3.4.2 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 @@ -137,7 +137,8 @@ self._running = False self._clock_resolution = time.get_clock_info('monotonic').resolution self._exception_handler = None - self._debug = False + self._debug = (not sys.flags.ignore_environment + and bool(os.environ.get('PYTHONASYNCIODEBUG'))) # In debug mode, if the execution of a callback or a step of a task # exceed this duration in seconds, the slow callback/task is logged. self.slow_callback_duration = 0.1 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 @@ -682,8 +682,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(3, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) class SelectorSocketTransportTests(test_utils.TestCase): 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 @@ -141,7 +141,7 @@ policy = asyncio.get_event_loop_policy() self.loop = policy.new_event_loop() - # ensure that the event loop is passed explicitly in the code + # ensure that the event loop is passed explicitly in asyncio policy.set_event_loop(None) watcher = self.Watcher() @@ -172,7 +172,7 @@ policy = asyncio.get_event_loop_policy() self.loop = asyncio.ProactorEventLoop() - # ensure that the event loop is passed explicitly in the code + # ensure that the event loop is passed explicitly in asyncio policy.set_event_loop(None) def tearDown(self): 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 @@ -1712,6 +1712,8 @@ self.assertIs(fut._loop, self.one_loop) gen1.close() gen2.close() + + self.set_event_loop(self.other_loop, cleanup=False) gen3 = coro() gen4 = coro() fut = asyncio.gather(gen3, gen4, loop=self.other_loop) diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -445,8 +445,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(5, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) def test__call_connection_lost_with_err(self): tr = unix_events._UnixReadPipeTransport( @@ -462,8 +460,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(5, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) class UnixWritePipeTransportTests(test_utils.TestCase): @@ -731,8 +727,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(5, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) def test__call_connection_lost_with_err(self): tr = unix_events._UnixWritePipeTransport( @@ -747,8 +741,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(5, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) def test_close(self): tr = unix_events._UnixWritePipeTransport( -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:15:40 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:15:40 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Enable_the_debug_mode_of_ev?= =?utf-8?q?ent_loops_when_the?= Message-ID: <3gxSkN2sTyz7Ljq@mail.python.org> http://hg.python.org/cpython/rev/57cd4ad9ea16 changeset: 91326:57cd4ad9ea16 parent: 91324:94570709cae9 parent: 91325:2228222faa7e user: Victor Stinner date: Mon Jun 23 00:14:45 2014 +0200 summary: (Merge 3.4) asyncio: Enable the debug mode of event loops when the PYTHONASYNCIODEBUG environment variable is set files: Doc/library/asyncio-eventloop.rst | 6 +++++- Lib/asyncio/base_events.py | 3 ++- Lib/test/test_asyncio/test_selector_events.py | 2 -- Lib/test/test_asyncio/test_subprocess.py | 4 ++-- Lib/test/test_asyncio/test_tasks.py | 2 ++ Lib/test/test_asyncio/test_unix_events.py | 8 -------- 6 files changed, 11 insertions(+), 14 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 @@ -585,7 +585,11 @@ .. method:: BaseEventLoop.get_debug() - Get the debug mode (:class:`bool`) of the event loop, ``False`` by default. + Get the debug mode (:class:`bool`) of the event loop. + + The default value is ``True`` if the environment variable + :envvar:`PYTHONASYNCIODEBUG` is set to a non-empty string, ``False`` + otherwise. .. versionadded:: 3.4.2 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 @@ -137,7 +137,8 @@ self._running = False self._clock_resolution = time.get_clock_info('monotonic').resolution self._exception_handler = None - self._debug = False + self._debug = (not sys.flags.ignore_environment + and bool(os.environ.get('PYTHONASYNCIODEBUG'))) # In debug mode, if the execution of a callback or a step of a task # exceed this duration in seconds, the slow callback/task is logged. self.slow_callback_duration = 0.1 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 @@ -682,8 +682,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(3, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) class SelectorSocketTransportTests(test_utils.TestCase): 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 @@ -141,7 +141,7 @@ policy = asyncio.get_event_loop_policy() self.loop = policy.new_event_loop() - # ensure that the event loop is passed explicitly in the code + # ensure that the event loop is passed explicitly in asyncio policy.set_event_loop(None) watcher = self.Watcher() @@ -172,7 +172,7 @@ policy = asyncio.get_event_loop_policy() self.loop = asyncio.ProactorEventLoop() - # ensure that the event loop is passed explicitly in the code + # ensure that the event loop is passed explicitly in asyncio policy.set_event_loop(None) def tearDown(self): 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 @@ -1712,6 +1712,8 @@ self.assertIs(fut._loop, self.one_loop) gen1.close() gen2.close() + + self.set_event_loop(self.other_loop, cleanup=False) gen3 = coro() gen4 = coro() fut = asyncio.gather(gen3, gen4, loop=self.other_loop) diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -445,8 +445,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(5, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) def test__call_connection_lost_with_err(self): tr = unix_events._UnixReadPipeTransport( @@ -462,8 +460,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(5, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) class UnixWritePipeTransportTests(test_utils.TestCase): @@ -731,8 +727,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(5, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) def test__call_connection_lost_with_err(self): tr = unix_events._UnixWritePipeTransport( @@ -747,8 +741,6 @@ self.assertEqual(2, sys.getrefcount(self.protocol), pprint.pformat(gc.get_referrers(self.protocol))) self.assertIsNone(tr._loop) - self.assertEqual(5, sys.getrefcount(self.loop), - pprint.pformat(gc.get_referrers(self.loop))) def test_close(self): tr = unix_events._UnixWritePipeTransport( -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:21:57 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:21:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogQWRk?= =?utf-8?q?_an_unit_test_to_check_that_setting_the_PYTHONASYNCIODEBUG_env_?= =?utf-8?q?var?= Message-ID: <3gxSsd6pTXz7Ljt@mail.python.org> http://hg.python.org/cpython/rev/94cb5ff71106 changeset: 91327:94cb5ff71106 branch: 3.4 parent: 91325:2228222faa7e user: Victor Stinner date: Mon Jun 23 00:19:33 2014 +0200 summary: asyncio: Add an unit test to check that setting the PYTHONASYNCIODEBUG env var enables debug mode of the event loop. files: Lib/test/test_asyncio/test_base_events.py | 24 +++++++++++ Lib/test/test_asyncio/test_tasks.py | 4 - 2 files changed, 24 insertions(+), 4 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 @@ -7,6 +7,7 @@ import time import unittest from unittest import mock +from test.script_helper import assert_python_ok from test.support import IPV6_ENABLED import asyncio @@ -489,6 +490,29 @@ self.assertIs(type(_context['context']['exception']), ZeroDivisionError) + def test_env_var_debug(self): + code = '\n'.join(( + 'import asyncio', + 'loop = asyncio.get_event_loop()', + 'print(loop.get_debug())')) + + # Test with -E to not fail if the unit test was run with + # PYTHONASYNCIODEBUG set to a non-empty string + sts, stdout, stderr = assert_python_ok('-E', '-c', code) + self.assertEqual(stdout.rstrip(), b'False') + + sts, stdout, stderr = assert_python_ok('-c', code, + PYTHONASYNCIODEBUG='') + self.assertEqual(stdout.rstrip(), b'False') + + sts, stdout, stderr = assert_python_ok('-c', code, + PYTHONASYNCIODEBUG='1') + self.assertEqual(stdout.rstrip(), b'True') + + sts, stdout, stderr = assert_python_ok('-E', '-c', code, + PYTHONASYNCIODEBUG='1') + self.assertEqual(stdout.rstrip(), b'False') + class MyProto(asyncio.Protocol): done = None 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 @@ -1577,11 +1577,7 @@ self.assertEqual(fut.result(), [3, 1, exc, exc2]) def test_env_var_debug(self): - path = os.path.dirname(asyncio.__file__) - path = os.path.normpath(os.path.join(path, '..')) code = '\n'.join(( - 'import sys', - 'sys.path.insert(0, %r)' % path, 'import asyncio.tasks', 'print(asyncio.tasks._DEBUG)')) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:21:59 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:21:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Add_an_unit_test_to_check_t?= =?utf-8?q?hat_setting_the?= Message-ID: <3gxSsg15Whz7Ljt@mail.python.org> http://hg.python.org/cpython/rev/3e87fb8df9c3 changeset: 91328:3e87fb8df9c3 parent: 91326:57cd4ad9ea16 parent: 91327:94cb5ff71106 user: Victor Stinner date: Mon Jun 23 00:21:09 2014 +0200 summary: (Merge 3.4) asyncio: Add an unit test to check that setting the PYTHONASYNCIODEBUG env var enables debug mode of the event loop. files: Lib/test/test_asyncio/test_base_events.py | 24 +++++++++++ Lib/test/test_asyncio/test_tasks.py | 4 - 2 files changed, 24 insertions(+), 4 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 @@ -7,6 +7,7 @@ import time import unittest from unittest import mock +from test.script_helper import assert_python_ok from test.support import IPV6_ENABLED import asyncio @@ -489,6 +490,29 @@ self.assertIs(type(_context['context']['exception']), ZeroDivisionError) + def test_env_var_debug(self): + code = '\n'.join(( + 'import asyncio', + 'loop = asyncio.get_event_loop()', + 'print(loop.get_debug())')) + + # Test with -E to not fail if the unit test was run with + # PYTHONASYNCIODEBUG set to a non-empty string + sts, stdout, stderr = assert_python_ok('-E', '-c', code) + self.assertEqual(stdout.rstrip(), b'False') + + sts, stdout, stderr = assert_python_ok('-c', code, + PYTHONASYNCIODEBUG='') + self.assertEqual(stdout.rstrip(), b'False') + + sts, stdout, stderr = assert_python_ok('-c', code, + PYTHONASYNCIODEBUG='1') + self.assertEqual(stdout.rstrip(), b'True') + + sts, stdout, stderr = assert_python_ok('-E', '-c', code, + PYTHONASYNCIODEBUG='1') + self.assertEqual(stdout.rstrip(), b'False') + class MyProto(asyncio.Protocol): done = None 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 @@ -1577,11 +1577,7 @@ self.assertEqual(fut.result(), [3, 1, exc, exc2]) def test_env_var_debug(self): - path = os.path.dirname(asyncio.__file__) - path = os.path.normpath(os.path.join(path, '..')) code = '\n'.join(( - 'import sys', - 'sys.path.insert(0, %r)' % path, 'import asyncio.tasks', 'print(asyncio.tasks._DEBUG)')) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:32:37 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:32:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbywgVHVs?= =?utf-8?q?ip_issue_172=3A_only_log_selector_timing_in_debug_mode?= Message-ID: <3gxT5x1lF5z7LjP@mail.python.org> http://hg.python.org/cpython/rev/d855c034d7d5 changeset: 91329:d855c034d7d5 branch: 3.4 parent: 91327:94cb5ff71106 user: Victor Stinner date: Mon Jun 23 00:31:08 2014 +0200 summary: asyncio, Tulip issue 172: only log selector timing in debug mode files: Lib/asyncio/base_events.py | 3 +-- Lib/test/test_asyncio/test_base_events.py | 3 +++ 2 files changed, 4 insertions(+), 2 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 @@ -834,8 +834,7 @@ when = self._scheduled[0]._when timeout = max(0, when - self.time()) - # TODO: Instrumentation only in debug mode? - if logger.isEnabledFor(logging.INFO): + if self._debug: t0 = self.time() event_list = self._selector.select(timeout) dt = self.time() - t0 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 @@ -247,6 +247,9 @@ time.sleep(1.0) return [] + # logging needs debug flag + self.loop.set_debug(True) + # Log to INFO level if timeout > 1.0 sec. self.loop._selector.select = slow_select self.loop._process_events = mock.Mock() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:32:38 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:32:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=2C_Tulip_issue_172=3A_only_log?= =?utf-8?q?_selector_timing_in_debug_mode?= Message-ID: <3gxT5y3W5Pz7Ljt@mail.python.org> http://hg.python.org/cpython/rev/188970f81ef8 changeset: 91330:188970f81ef8 parent: 91328:3e87fb8df9c3 parent: 91329:d855c034d7d5 user: Victor Stinner date: Mon Jun 23 00:31:31 2014 +0200 summary: (Merge 3.4) asyncio, Tulip issue 172: only log selector timing in debug mode files: Lib/asyncio/base_events.py | 3 +-- Lib/test/test_asyncio/test_base_events.py | 3 +++ 2 files changed, 4 insertions(+), 2 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 @@ -834,8 +834,7 @@ when = self._scheduled[0]._when timeout = max(0, when - self.time()) - # TODO: Instrumentation only in debug mode? - if logger.isEnabledFor(logging.INFO): + if self._debug: t0 = self.time() event_list = self._selector.select(timeout) dt = self.time() - t0 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 @@ -247,6 +247,9 @@ time.sleep(1.0) return [] + # logging needs debug flag + self.loop.set_debug(True) + # Log to INFO level if timeout > 1.0 sec. self.loop._selector.select = slow_select self.loop._process_events = mock.Mock() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:46:26 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:46:26 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogZG9j?= =?utf-8?q?ument_the_debug_mode?= Message-ID: <3gxTPt3M3Hz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/46440d260af1 changeset: 91331:46440d260af1 branch: 3.4 parent: 91329:d855c034d7d5 user: Victor Stinner date: Mon Jun 23 00:36:11 2014 +0200 summary: asyncio: document the debug mode files: Doc/library/asyncio-dev.rst | 31 ++++++++++++++++-- Doc/library/asyncio-eventloop.rst | 2 +- Doc/using/cmdline.rst | 4 +- 3 files changed, 30 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 @@ -9,6 +9,29 @@ This page lists common traps and explains how to avoid them. +.. _asyncio-debug-mode: + +Debug mode of asyncio +--------------------- + +To enable the debug mode globally, set the environment variable +:envvar:`PYTHONASYNCIODEBUG` to ``1``. Examples of effects of the debug mode: + +* Log :ref:`coroutines defined but never "yielded from" + ` +* :meth:`~BaseEventLoop.call_soon` and :meth:`~BaseEventLoop.call_at` methods + raise an exception if they are called from the wrong thread. +* Log the execution time of the selector +* Log callbacks taking more than 100 ms to be executed. The + :attr:`BaseEventLoop.slow_callback_duration` attribute is the minimum + duration in seconds of "slow" callbacks. + +.. seealso:: + + The :meth:`BaseEventLoop.set_debug` method and the :ref:`asyncio logger + `. + + .. _asyncio-multithreading: Concurrency and multithreading @@ -83,10 +106,10 @@ When a coroutine function is called but not passed to :func:`async` or to the :class:`Task` constructor, it is not scheduled and it is probably a bug. -To detect such bug, set the environment variable :envvar:`PYTHONASYNCIODEBUG` -to ``1``. When the coroutine object is destroyed by the garbage collector, a -log will be emitted with the traceback where the coroutine function was called. -See the :ref:`asyncio logger `. +To detect such bug, :ref:`enable the debug mode of asyncio +`. When the coroutine object is destroyed by the garbage +collector, a log will be emitted with the traceback where the coroutine +function was called. See the :ref:`asyncio logger `. The debug flag changes the behaviour of the :func:`coroutine` decorator. The debug flag value is only used when then coroutine function is defined, not when 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 @@ -601,7 +601,7 @@ .. seealso:: - The :ref:`Develop with asyncio ` section. + The :ref:`debug mode of asyncio `. Server diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -616,8 +616,8 @@ .. envvar:: PYTHONASYNCIODEBUG - If this environment variable is set to a non-empty string, enable the debug - mode of the :mod:`asyncio` module. + If this environment variable is set to a non-empty string, enable the + :ref:`debug mode ` of the :mod:`asyncio` module. .. versionadded:: 3.4 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 00:46:27 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 00:46:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_document_the_debug_mode?= Message-ID: <3gxTPv4mC6z7Lk1@mail.python.org> http://hg.python.org/cpython/rev/302f257823e8 changeset: 91332:302f257823e8 parent: 91330:188970f81ef8 parent: 91331:46440d260af1 user: Victor Stinner date: Mon Jun 23 00:46:16 2014 +0200 summary: (Merge 3.4) asyncio: document the debug mode files: Doc/library/asyncio-dev.rst | 31 ++++++++++++++++-- Doc/library/asyncio-eventloop.rst | 2 +- Doc/using/cmdline.rst | 4 +- 3 files changed, 30 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 @@ -9,6 +9,29 @@ This page lists common traps and explains how to avoid them. +.. _asyncio-debug-mode: + +Debug mode of asyncio +--------------------- + +To enable the debug mode globally, set the environment variable +:envvar:`PYTHONASYNCIODEBUG` to ``1``. Examples of effects of the debug mode: + +* Log :ref:`coroutines defined but never "yielded from" + ` +* :meth:`~BaseEventLoop.call_soon` and :meth:`~BaseEventLoop.call_at` methods + raise an exception if they are called from the wrong thread. +* Log the execution time of the selector +* Log callbacks taking more than 100 ms to be executed. The + :attr:`BaseEventLoop.slow_callback_duration` attribute is the minimum + duration in seconds of "slow" callbacks. + +.. seealso:: + + The :meth:`BaseEventLoop.set_debug` method and the :ref:`asyncio logger + `. + + .. _asyncio-multithreading: Concurrency and multithreading @@ -83,10 +106,10 @@ When a coroutine function is called but not passed to :func:`async` or to the :class:`Task` constructor, it is not scheduled and it is probably a bug. -To detect such bug, set the environment variable :envvar:`PYTHONASYNCIODEBUG` -to ``1``. When the coroutine object is destroyed by the garbage collector, a -log will be emitted with the traceback where the coroutine function was called. -See the :ref:`asyncio logger `. +To detect such bug, :ref:`enable the debug mode of asyncio +`. When the coroutine object is destroyed by the garbage +collector, a log will be emitted with the traceback where the coroutine +function was called. See the :ref:`asyncio logger `. The debug flag changes the behaviour of the :func:`coroutine` decorator. The debug flag value is only used when then coroutine function is defined, not when 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 @@ -601,7 +601,7 @@ .. seealso:: - The :ref:`Develop with asyncio ` section. + The :ref:`debug mode of asyncio `. Server diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -616,8 +616,8 @@ .. envvar:: PYTHONASYNCIODEBUG - If this environment variable is set to a non-empty string, enable the debug - mode of the :mod:`asyncio` module. + If this environment variable is set to a non-empty string, enable the + :ref:`debug mode ` of the :mod:`asyncio` module. .. versionadded:: 3.4 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 01:04:19 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 01:04:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbywgVHVs?= =?utf-8?q?ip_issue_171=3A_BaseEventLoop=2Eclose=28=29_now_raises_an_excep?= =?utf-8?q?tion_if_the?= Message-ID: <3gxTpW6wtJz7LjW@mail.python.org> http://hg.python.org/cpython/rev/4f0480e92ffc changeset: 91333:4f0480e92ffc branch: 3.4 parent: 91331:46440d260af1 user: Victor Stinner date: Mon Jun 23 01:02:37 2014 +0200 summary: asyncio, Tulip issue 171: BaseEventLoop.close() now raises an exception if the event loop is running. You must first stop the event loop and then wait until it stopped, before closing it. files: Doc/library/asyncio-eventloop.rst | 2 ++ Lib/asyncio/base_events.py | 4 ++++ Lib/asyncio/proactor_events.py | 2 +- Lib/asyncio/selector_events.py | 2 +- Lib/asyncio/unix_events.py | 2 +- Lib/test/test_asyncio/test_events.py | 9 +++++++++ 6 files changed, 18 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 @@ -132,6 +132,8 @@ This clears the queues and shuts down the executor, but does not wait for the executor to finish. + The event loop must not be running. + This is idempotent and irreversible. No other methods should be called after this one. 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 @@ -247,7 +247,11 @@ This clears the queues and shuts down the executor, but does not wait for the executor to finish. + + The event loop must not be running. """ + if self._running: + raise RuntimeError("cannot close a running event loop") if self._closed: return self._closed = True 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 @@ -355,12 +355,12 @@ def close(self): if self.is_closed(): return + super().close() self._stop_accept_futures() self._close_self_pipe() self._proactor.close() self._proactor = None self._selector = None - super().close() def sock_recv(self, sock, n): return self._proactor.recv(sock, n) 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 @@ -57,11 +57,11 @@ def close(self): if self.is_closed(): return + super().close() self._close_self_pipe() if self._selector is not None: self._selector.close() self._selector = None - super().close() def _socketpair(self): raise NotImplementedError 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 @@ -44,9 +44,9 @@ return socket.socketpair() def close(self): + super().close() for sig in list(self._signal_handlers): self.remove_signal_handler(sig) - super().close() def add_signal_handler(self, sig, callback, *args): """Add a handler for a signal. UNIX only. 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 @@ -1365,6 +1365,15 @@ with self.assertRaises(RuntimeError): loop.add_writer(w, callback) + def test_close_running_event_loop(self): + @asyncio.coroutine + def close_loop(loop): + self.loop.close() + + coro = close_loop(self.loop) + with self.assertRaises(RuntimeError): + self.loop.run_until_complete(coro) + class SubprocessTestsMixin: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 01:04:21 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 01:04:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=2C_Tulip_issue_171=3A_BaseEven?= =?utf-8?q?tLoop=2Eclose=28=29_now_raises_an?= Message-ID: <3gxTpY2hf7z7Lkd@mail.python.org> http://hg.python.org/cpython/rev/e257a58bd718 changeset: 91334:e257a58bd718 parent: 91332:302f257823e8 parent: 91333:4f0480e92ffc user: Victor Stinner date: Mon Jun 23 01:03:13 2014 +0200 summary: (Merge 3.4) asyncio, Tulip issue 171: BaseEventLoop.close() now raises an exception if the event loop is running. You must first stop the event loop and then wait until it stopped, before closing it. files: Doc/library/asyncio-eventloop.rst | 2 ++ Lib/asyncio/base_events.py | 4 ++++ Lib/asyncio/proactor_events.py | 2 +- Lib/asyncio/selector_events.py | 2 +- Lib/asyncio/unix_events.py | 2 +- Lib/test/test_asyncio/test_events.py | 9 +++++++++ 6 files changed, 18 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 @@ -132,6 +132,8 @@ This clears the queues and shuts down the executor, but does not wait for the executor to finish. + The event loop must not be running. + This is idempotent and irreversible. No other methods should be called after this one. 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 @@ -247,7 +247,11 @@ This clears the queues and shuts down the executor, but does not wait for the executor to finish. + + The event loop must not be running. """ + if self._running: + raise RuntimeError("cannot close a running event loop") if self._closed: return self._closed = True 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 @@ -355,12 +355,12 @@ def close(self): if self.is_closed(): return + super().close() self._stop_accept_futures() self._close_self_pipe() self._proactor.close() self._proactor = None self._selector = None - super().close() def sock_recv(self, sock, n): return self._proactor.recv(sock, n) 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 @@ -57,11 +57,11 @@ def close(self): if self.is_closed(): return + super().close() self._close_self_pipe() if self._selector is not None: self._selector.close() self._selector = None - super().close() def _socketpair(self): raise NotImplementedError 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 @@ -44,9 +44,9 @@ return socket.socketpair() def close(self): + super().close() for sig in list(self._signal_handlers): self.remove_signal_handler(sig) - super().close() def add_signal_handler(self, sig, callback, *args): """Add a handler for a signal. UNIX only. 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 @@ -1365,6 +1365,15 @@ with self.assertRaises(RuntimeError): loop.add_writer(w, callback) + def test_close_running_event_loop(self): + @asyncio.coroutine + def close_loop(loop): + self.loop.close() + + coro = close_loop(self.loop) + with self.assertRaises(RuntimeError): + self.loop.run_until_complete(coro) + class SubprocessTestsMixin: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 01:27:16 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 23 Jun 2014 01:27:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_some_timezone_?= =?utf-8?q?doc_improvements_=28closes_=2316667=29?= Message-ID: <3gxVK05St6z7Lk1@mail.python.org> http://hg.python.org/cpython/rev/b32174cad588 changeset: 91335:b32174cad588 branch: 3.4 parent: 91333:4f0480e92ffc user: Benjamin Peterson date: Sun Jun 22 16:26:39 2014 -0700 summary: some timezone doc improvements (closes #16667) From the combined efforts of Berber Peksage, Andrew Svetlov, and Yayoi Ukai. files: Doc/library/datetime.rst | 12 +++++++----- 1 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1687,11 +1687,11 @@ .. seealso:: `pytz `_ - The standard library has no :class:`tzinfo` instances except for UTC, but - there exists a third-party library which brings the *IANA timezone - database* (also known as the Olson database) to Python: *pytz*. - - *pytz* contains up-to-date information and its usage is recommended. + The standard library has :class:`timezone` class for handling arbitrary + fixed offsets from UTC and :attr:`timezone.utc` as UTC timezone instance. + + *pytz* library brings the *IANA timezone database* (also known as the + Olson database) to Python and its usage is recommended. `IANA timezone database `_ The Time Zone Database (often called tz or zoneinfo) contains code and @@ -1728,6 +1728,8 @@ *offset*, HH and MM are two digits of ``offset.hours`` and ``offset.minutes`` respectively. + .. versionadded:: 3.2 + .. method:: timezone.utcoffset(dt) Return the fixed value specified when the :class:`timezone` instance is -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 01:27:17 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 23 Jun 2014 01:27:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMTY2Njcp?= Message-ID: <3gxVK16n16z7LkN@mail.python.org> http://hg.python.org/cpython/rev/7dc94337ef67 changeset: 91336:7dc94337ef67 parent: 91334:e257a58bd718 parent: 91335:b32174cad588 user: Benjamin Peterson date: Sun Jun 22 16:27:10 2014 -0700 summary: merge 3.4 (#16667) files: Doc/library/datetime.rst | 12 +++++++----- 1 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1692,11 +1692,11 @@ .. seealso:: `pytz `_ - The standard library has no :class:`tzinfo` instances except for UTC, but - there exists a third-party library which brings the *IANA timezone - database* (also known as the Olson database) to Python: *pytz*. - - *pytz* contains up-to-date information and its usage is recommended. + The standard library has :class:`timezone` class for handling arbitrary + fixed offsets from UTC and :attr:`timezone.utc` as UTC timezone instance. + + *pytz* library brings the *IANA timezone database* (also known as the + Olson database) to Python and its usage is recommended. `IANA timezone database `_ The Time Zone Database (often called tz or zoneinfo) contains code and @@ -1733,6 +1733,8 @@ *offset*, HH and MM are two digits of ``offset.hours`` and ``offset.minutes`` respectively. + .. versionadded:: 3.2 + .. method:: timezone.utcoffset(dt) Return the fixed value specified when the :class:`timezone` instance is -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 01:49:19 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 23 Jun 2014 01:49:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?devguide=3A_add_a_pointer_to_the_benc?= =?utf-8?q?hmarks_repo_=28closes_=2317449=29?= Message-ID: <3gxVpR1Tx1z7LjN@mail.python.org> http://hg.python.org/devguide/rev/428b6350307e changeset: 704:428b6350307e user: Benjamin Peterson date: Sun Jun 22 16:49:13 2014 -0700 summary: add a pointer to the benchmarks repo (closes #17449) Initial patch by Kavya Joshi. files: runtests.rst | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-) diff --git a/runtests.rst b/runtests.rst --- a/runtests.rst +++ b/runtests.rst @@ -124,3 +124,13 @@ When you are adding tests to an existing test file, it is also recommended that you study the other tests in that file; it will teach you which precautions you have to take to make your tests robust and portable. + + +Benchmarks +---------- +Benchmarking is useful to test that a change does not degrade performance. + +`The Grand Unified Python Benchmark Suite `_ +has a collection of benchmarks for all Python implementations. Documentation +about running the benchmarks is in the `README.txt +`_ of the benchmarks repo. -- Repository URL: http://hg.python.org/devguide From python-checkins at python.org Mon Jun 23 03:00:14 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 23 Jun 2014 03:00:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_clarify_that_i?= =?utf-8?q?slink_only_really_works_if_python_knows_about_symlinks_=28close?= =?utf-8?q?s?= Message-ID: <3gxXNG2fvVz7Lm6@mail.python.org> http://hg.python.org/cpython/rev/f463387d2434 changeset: 91337:f463387d2434 branch: 2.7 parent: 91314:9778d37c2d18 user: Benjamin Peterson date: Sun Jun 22 17:59:35 2014 -0700 summary: clarify that islink only really works if python knows about symlinks (closes #13143) Patch from Yayoi Ukai. files: Doc/library/os.path.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -185,7 +185,7 @@ .. function:: islink(path) Return ``True`` if *path* refers to a directory entry that is a symbolic link. - Always ``False`` if symbolic links are not supported. + Always ``False`` if symbolic links are not supported by the python runtime. .. function:: ismount(path) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 03:00:15 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 23 Jun 2014 03:00:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_clarify_that_i?= =?utf-8?q?slink_only_really_works_if_python_knows_about_symlinks_=28close?= =?utf-8?q?s?= Message-ID: <3gxXNH3yb5z7LmZ@mail.python.org> http://hg.python.org/cpython/rev/db7887f3e6a2 changeset: 91338:db7887f3e6a2 branch: 3.4 parent: 91335:b32174cad588 user: Benjamin Peterson date: Sun Jun 22 17:59:35 2014 -0700 summary: clarify that islink only really works if python knows about symlinks (closes #13143) Patch from Yayoi Ukai. files: Doc/library/os.path.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -188,7 +188,7 @@ .. function:: islink(path) Return ``True`` if *path* refers to a directory entry that is a symbolic link. - Always ``False`` if symbolic links are not supported. + Always ``False`` if symbolic links are not supported by the python runtime. .. function:: ismount(path) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 03:00:16 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 23 Jun 2014 03:00:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMTMxNDMp?= Message-ID: <3gxXNJ5j4nz7Lkr@mail.python.org> http://hg.python.org/cpython/rev/81529993f60d changeset: 91339:81529993f60d parent: 91336:7dc94337ef67 parent: 91338:db7887f3e6a2 user: Benjamin Peterson date: Sun Jun 22 18:00:07 2014 -0700 summary: merge 3.4 (#13143) files: Doc/library/os.path.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -188,7 +188,7 @@ .. function:: islink(path) Return ``True`` if *path* refers to a directory entry that is a symbolic link. - Always ``False`` if symbolic links are not supported. + Always ``False`` if symbolic links are not supported by the python runtime. .. function:: ismount(path) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 04:11:05 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 23 Jun 2014 04:11:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogZml4IG50cGF0aC5q?= =?utf-8?q?oin_on_UNC-style_paths_by_backporting_py3k=27s_splitdrive_=28cl?= =?utf-8?q?oses?= Message-ID: <3gxYy14QQ3z7Ljq@mail.python.org> http://hg.python.org/cpython/rev/26ec6248ee8b changeset: 91340:26ec6248ee8b branch: 2.7 parent: 91337:f463387d2434 user: Benjamin Peterson date: Sun Jun 22 19:07:38 2014 -0700 summary: fix ntpath.join on UNC-style paths by backporting py3k's splitdrive (closes #21672) files: Lib/ntpath.py | 47 +++++++++++++++++++++++++--- Lib/test/test_ntpath.py | 32 ++++++++++++++---- Misc/NEWS | 2 + 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -82,6 +82,10 @@ if result_path and result_path[-1] not in '\\/': result_path = result_path + '\\' result_path = result_path + p_path + ## add separator between UNC and non-absolute path + if (result_path and result_path[0] not in '\\/' and + result_drive and result_drive[-1:] != ':'): + return result_drive + sep + result_path return result_drive + result_path @@ -89,13 +93,46 @@ # colon) and the path specification. # It is always true that drivespec + pathspec == p def splitdrive(p): - """Split a pathname into drive and path specifiers. Returns a 2-tuple -"(drive,path)"; either part may be empty""" - if p[1:2] == ':': - return p[0:2], p[2:] + """Split a pathname into drive/UNC sharepoint and relative path specifiers. + Returns a 2-tuple (drive_or_unc, path); either part may be empty. + + If you assign + result = splitdrive(p) + It is always true that: + result[0] + result[1] == p + + If the path contained a drive letter, drive_or_unc will contain everything + up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir") + + If the path contained a UNC path, the drive_or_unc will contain the host name + and share up to but not including the fourth directory separator character. + e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir") + + Paths cannot contain both a drive letter and a UNC path. + + """ + if len(p) > 1: + normp = p.replace(altsep, sep) + if (normp[0:2] == sep*2) and (normp[2] != sep): + # is a UNC path: + # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path + # \\machine\mountpoint\directory\etc\... + # directory ^^^^^^^^^^^^^^^ + index = normp.find(sep, 2) + if index == -1: + return '', p + index2 = normp.find(sep, index + 1) + # a UNC path can't have two slashes in a row + # (after the initial two) + if index2 == index + 1: + return '', p + if index2 == -1: + index2 = len(p) + return p[:index2], p[index2:] + if normp[1] == ':': + return p[:2], p[2:] return '', p - # Parse UNC paths def splitunc(p): """Split a pathname into UNC mount point and relative path specifiers. diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1,3 +1,4 @@ +# coding: utf-8 import ntpath import os import sys @@ -34,6 +35,21 @@ ('c:', '\\foo\\bar')) tester('ntpath.splitdrive("c:/foo/bar")', ('c:', '/foo/bar')) + tester('ntpath.splitdrive("\\\\conky\\mountpoint\\foo\\bar")', + ('\\\\conky\\mountpoint', '\\foo\\bar')) + tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")', + ('//conky/mountpoint', '/foo/bar')) + tester('ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")', + ('', '\\\\\\conky\\mountpoint\\foo\\bar')) + tester('ntpath.splitdrive("///conky/mountpoint/foo/bar")', + ('', '///conky/mountpoint/foo/bar')) + tester('ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")', + ('', '\\\\conky\\\\mountpoint\\foo\\bar')) + tester('ntpath.splitdrive("//conky//mountpoint/foo/bar")', + ('', '//conky//mountpoint/foo/bar')) + # Issue #19911: UNC part containing U+0130 + self.assertEqual(ntpath.splitdrive(u'//conky/MOUNTPO?NT/foo/bar'), + (u'//conky/MOUNTPO?NT', '/foo/bar')) def test_splitunc(self): tester('ntpath.splitunc("c:\\foo\\bar")', @@ -62,10 +78,10 @@ tester('ntpath.split("c:\\")', ('c:\\', '')) tester('ntpath.split("\\\\conky\\mountpoint\\")', - ('\\\\conky\\mountpoint', '')) + ('\\\\conky\\mountpoint\\', '')) tester('ntpath.split("c:/")', ('c:/', '')) - tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint', '')) + tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', '')) def test_isabs(self): tester('ntpath.isabs("c:\\")', 1) @@ -114,9 +130,9 @@ tester("ntpath.join('c:/', 'x/y')", 'c:/x/y') tester("ntpath.join('c:/a/b', 'x/y')", 'c:/a/b\\x/y') tester("ntpath.join('c:/a/b/', 'x/y')", 'c:/a/b/x/y') - #tester("ntpath.join('//computer/share', 'x/y')", '//computer/share\\x/y') - #tester("ntpath.join('//computer/share/', 'x/y')", '//computer/share/x/y') - #tester("ntpath.join('//computer/share/a/b', 'x/y')", '//computer/share/a/b\\x/y') + tester("ntpath.join('//computer/share', 'x/y')", '//computer/share\\x/y') + tester("ntpath.join('//computer/share/', 'x/y')", '//computer/share/x/y') + tester("ntpath.join('//computer/share/a/b', 'x/y')", '//computer/share/a/b\\x/y') tester("ntpath.join('a/b', '/x/y')", '/x/y') tester("ntpath.join('/a/b', '/x/y')", '/x/y') @@ -124,9 +140,9 @@ tester("ntpath.join('c:a/b', '/x/y')", 'c:/x/y') tester("ntpath.join('c:/', '/x/y')", 'c:/x/y') tester("ntpath.join('c:/a/b', '/x/y')", 'c:/x/y') - #tester("ntpath.join('//computer/share', '/x/y')", '//computer/share/x/y') - #tester("ntpath.join('//computer/share/', '/x/y')", '//computer/share/x/y') - #tester("ntpath.join('//computer/share/a', '/x/y')", '//computer/share/x/y') + tester("ntpath.join('//computer/share', '/x/y')", '//computer/share/x/y') + tester("ntpath.join('//computer/share/', '/x/y')", '//computer/share/x/y') + tester("ntpath.join('//computer/share/a', '/x/y')", '//computer/share/x/y') tester("ntpath.join('c:', 'C:x/y')", 'C:x/y') tester("ntpath.join('c:a/b', 'C:x/y')", 'C:a/b\\x/y') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,8 @@ Library ------- +- Issue #21672: Fix the behavior of ntpath.join on UNC-style paths. + - Issue #21491: SocketServer: Fix a race condition in child processes reaping. - Issue #21635: The difflib SequenceMatcher.get_matching_blocks() method -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 04:33:27 2014 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 23 Jun 2014 04:33:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzgzNDM6?= =?utf-8?q?_Named_group_error_msgs_did_not_show_the_group_name=2E?= Message-ID: <3gxZRq0p6Zz7LjP@mail.python.org> http://hg.python.org/cpython/rev/9c7b50a36b42 changeset: 91341:9c7b50a36b42 branch: 2.7 user: Raymond Hettinger date: Sun Jun 22 19:33:19 2014 -0700 summary: Issue #8343: Named group error msgs did not show the group name. files: Lib/sre_parse.py | 9 ++++++--- Misc/NEWS | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -567,7 +567,8 @@ "%r" % name) gid = state.groupdict.get(name) if gid is None: - raise error, "unknown group name" + msg = "unknown group name: {0!r}".format(name) + raise error(msg) subpatternappend((GROUPREF, gid)) continue else: @@ -620,7 +621,8 @@ if isname(condname): condgroup = state.groupdict.get(condname) if condgroup is None: - raise error, "unknown group name" + msg = "unknown group name: {0!r}".format(condgroup) + raise error(msg) else: try: condgroup = int(condname) @@ -746,7 +748,8 @@ try: index = pattern.groupindex[name] except KeyError: - raise IndexError, "unknown group name" + msg = "unknown group name: {0!r}".format(name) + raise IndexError(msg) a((MARK, index)) elif c == "0": if s.next in OCTDIGITS: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -31,6 +31,9 @@ - Issue #21672: Fix the behavior of ntpath.join on UNC-style paths. +- Issue #8343: Named group error messages in the re module did not show + the name of the erroneous group. + - Issue #21491: SocketServer: Fix a race condition in child processes reaping. - Issue #21635: The difflib SequenceMatcher.get_matching_blocks() method -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 04:45:12 2014 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 23 Jun 2014 04:45:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Display_the_na?= =?utf-8?q?me_not_the_group_itself?= Message-ID: <3gxZjN3MJnz7LjW@mail.python.org> http://hg.python.org/cpython/rev/b2e5d3a97452 changeset: 91342:b2e5d3a97452 branch: 2.7 user: Raymond Hettinger date: Sun Jun 22 19:45:07 2014 -0700 summary: Display the name not the group itself files: Lib/sre_parse.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -621,7 +621,7 @@ if isname(condname): condgroup = state.groupdict.get(condname) if condgroup is None: - msg = "unknown group name: {0!r}".format(condgroup) + msg = "unknown group name: {0!r}".format(condname) raise error(msg) else: try: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 04:48:02 2014 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 23 Jun 2014 04:48:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzgzNDM6?= =?utf-8?q?_Named_group_error_msgs_did_not_show_the_group_name=2E?= Message-ID: <3gxZmf0hQyz7LjN@mail.python.org> http://hg.python.org/cpython/rev/404dcd29b0a6 changeset: 91343:404dcd29b0a6 branch: 3.4 parent: 91338:db7887f3e6a2 user: Raymond Hettinger date: Sun Jun 22 19:47:22 2014 -0700 summary: Issue #8343: Named group error msgs did not show the group name. files: Lib/sre_parse.py | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -616,7 +616,8 @@ "%r" % name) gid = state.groupdict.get(name) if gid is None: - raise error("unknown group name") + msg = "unknown group name: {0!r}".format(name) + raise error(msg) subpatternappend((GROUPREF, gid)) continue else: @@ -669,7 +670,8 @@ if condname.isidentifier(): condgroup = state.groupdict.get(condname) if condgroup is None: - raise error("unknown group name") + msg = "unknown group name: {0!r}".format(condname) + raise error(msg) else: try: condgroup = int(condname) @@ -806,7 +808,8 @@ try: index = pattern.groupindex[name] except KeyError: - raise IndexError("unknown group name") + msg = "unknown group name: {0!r}".format(name) + raise IndexError(msg) addgroup(index) elif c == "0": if s.next in OCTDIGITS: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 04:48:03 2014 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 23 Jun 2014 04:48:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gxZmg2PDvz7LjW@mail.python.org> http://hg.python.org/cpython/rev/8e3ef955dbc3 changeset: 91344:8e3ef955dbc3 parent: 91339:81529993f60d parent: 91343:404dcd29b0a6 user: Raymond Hettinger date: Sun Jun 22 19:47:55 2014 -0700 summary: merge files: Lib/sre_parse.py | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -616,7 +616,8 @@ "%r" % name) gid = state.groupdict.get(name) if gid is None: - raise error("unknown group name") + msg = "unknown group name: {0!r}".format(name) + raise error(msg) subpatternappend((GROUPREF, gid)) continue else: @@ -669,7 +670,8 @@ if condname.isidentifier(): condgroup = state.groupdict.get(condname) if condgroup is None: - raise error("unknown group name") + msg = "unknown group name: {0!r}".format(condname) + raise error(msg) else: try: condgroup = int(condname) @@ -806,7 +808,8 @@ try: index = pattern.groupindex[name] except KeyError: - raise IndexError("unknown group name") + msg = "unknown group name: {0!r}".format(name) + raise IndexError(msg) addgroup(index) elif c == "0": if s.next in OCTDIGITS: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 05:28:17 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 23 Jun 2014 05:28:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_adjust_zipfile?= =?utf-8?q?_tests_for_splitdrive_improvements?= Message-ID: <3gxbg55dCCz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/e28004fb30c0 changeset: 91345:e28004fb30c0 branch: 2.7 parent: 91342:b2e5d3a97452 user: Benjamin Peterson date: Sun Jun 22 20:26:07 2014 -0700 summary: adjust zipfile tests for splitdrive improvements files: Lib/test/test_zipfile.py | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -476,14 +476,14 @@ (r'C:/foo/bar', 'foo/bar'), (r'C://foo/bar', 'foo/bar'), (r'C:\foo\bar', 'foo/bar'), - (r'//conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), - (r'\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'//conky/mountpoint/foo/bar', 'foo/bar'), + (r'\\conky\mountpoint\foo\bar', 'foo/bar'), (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), - (r'//?/C:/foo/bar', '_/C_/foo/bar'), - (r'\\?\C:\foo\bar', '_/C_/foo/bar'), + (r'//?/C:/foo/bar', 'foo/bar'), + (r'\\?\C:\foo\bar', 'foo/bar'), (r'C:/../C:/foo/bar', 'C_/foo/bar'), (r'a:b\ce|f"g?h*i', 'b/c_d_e_f_g_h_i'), ('../../foo../../ba..r', 'foo/ba..r'), -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Jun 23 11:02:44 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 23 Jun 2014 11:02:44 +0200 Subject: [Python-checkins] Daily reference leaks (81529993f60d): sum=15 Message-ID: results for 81529993f60d on branch "default" -------------------------------------------- test_asyncio leaked [0, 4, 0] memory blocks, sum=4 test_collections leaked [0, -2, 0] references, sum=-2 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [2, -2, 2] references, sum=2 test_site leaked [2, -2, 2] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog55imk_', '-x'] From python-checkins at python.org Mon Jun 23 15:15:44 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 15:15:44 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogRml4?= =?utf-8?q?_BaseEventLoop=2E=5Fassert=5Fis=5Fcurrent=5Fevent=5Floop=28=29?= =?utf-8?b?OiBnZXRfZXZlbnRfbG9vcCgp?= Message-ID: <3gxrhw2HKPz7M0n@mail.python.org> http://hg.python.org/cpython/rev/389ef84c2823 changeset: 91346:389ef84c2823 branch: 3.4 parent: 91343:404dcd29b0a6 user: Victor Stinner date: Mon Jun 23 15:14:13 2014 +0200 summary: asyncio: Fix BaseEventLoop._assert_is_current_event_loop(): get_event_loop() raises an exception if there is no current loop files: Lib/asyncio/base_events.py | 7 +++++-- 1 files changed, 5 insertions(+), 2 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 @@ -332,8 +332,11 @@ Should only be called when (self._debug == True). The caller is responsible for checking this condition for performance reasons. """ - current = events.get_event_loop() - if current is not None and current is not self: + try: + current = events.get_event_loop() + except AssertionError: + return + if current is not self: raise RuntimeError( "non-threadsafe operation invoked on an event loop other " "than the current one") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 15:15:45 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 23 Jun 2014 15:15:45 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Fix_BaseEventLoop=2E=5Fasse?= =?utf-8?b?cnRfaXNfY3VycmVudF9ldmVudF9sb29wKCk6?= Message-ID: <3gxrhx3yYKz7M67@mail.python.org> http://hg.python.org/cpython/rev/3fffe9681f60 changeset: 91347:3fffe9681f60 parent: 91344:8e3ef955dbc3 parent: 91346:389ef84c2823 user: Victor Stinner date: Mon Jun 23 15:14:50 2014 +0200 summary: (Merge 3.4) asyncio: Fix BaseEventLoop._assert_is_current_event_loop(): get_event_loop() raises an exception if there is no current loop files: Lib/asyncio/base_events.py | 7 +++++-- 1 files changed, 5 insertions(+), 2 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 @@ -332,8 +332,11 @@ Should only be called when (self._debug == True). The caller is responsible for checking this condition for performance reasons. """ - current = events.get_event_loop() - if current is not None and current is not self: + try: + current = events.get_event_loop() + except AssertionError: + return + if current is not self: raise RuntimeError( "non-threadsafe operation invoked on an event loop other " "than the current one") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 19:24:02 2014 From: python-checkins at python.org (yury.selivanov) Date: Mon, 23 Jun 2014 19:24:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogaW5zcGVjdDogVmFs?= =?utf-8?q?idate_that_=5F=5Fsignature=5F=5F_is_None_or_an_instance_of_Sign?= =?utf-8?q?ature=2E?= Message-ID: <3gxyCQ3D2tz7LkK@mail.python.org> http://hg.python.org/cpython/rev/cc0f5d6ccb70 changeset: 91348:cc0f5d6ccb70 branch: 3.4 parent: 91346:389ef84c2823 user: Yury Selivanov date: Mon Jun 23 10:21:04 2014 -0700 summary: inspect: Validate that __signature__ is None or an instance of Signature. Closes #21801. files: Lib/inspect.py | 4 ++++ Lib/test/test_inspect.py | 7 +++++++ Misc/NEWS | 2 ++ 3 files changed, 13 insertions(+), 0 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1912,6 +1912,10 @@ pass else: if sig is not None: + if not isinstance(sig, Signature): + raise TypeError( + 'unexpected object {!r} in __signature__ ' + 'attribute'.format(sig)) return sig try: 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 @@ -3048,6 +3048,13 @@ self.assertEqual(lines[:-1], inspect.getsource(module).splitlines()) self.assertEqual(err, b'') + def test_custom_getattr(self): + def foo(): + pass + foo.__signature__ = 42 + with self.assertRaises(TypeError): + inspect.signature(foo) + @unittest.skipIf(ThreadPoolExecutor is None, 'threads required to test __qualname__ for source files') def test_qualname_source(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -105,6 +105,8 @@ - Issue #21538: The plistlib module now supports loading of binary plist files when reference or offset size is not a power of two. +- Issue #21801: Validate that __signature__ is None or an instance of Signature. + Build ----- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 23 19:24:03 2014 From: python-checkins at python.org (yury.selivanov) Date: Mon, 23 Jun 2014 19:24:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_inspect=3A_Validate_that_?= =?utf-8?q?=5F=5Fsignature=5F=5F_is_None_or_an_instance_of_Signature=2E?= Message-ID: <3gxyCS07cjz7LkX@mail.python.org> http://hg.python.org/cpython/rev/fa5b985f0920 changeset: 91349:fa5b985f0920 parent: 91347:3fffe9681f60 user: Yury Selivanov date: Mon Jun 23 10:23:50 2014 -0700 summary: inspect: Validate that __signature__ is None or an instance of Signature. Closes #21801. files: Lib/inspect.py | 4 ++++ Lib/test/test_inspect.py | 7 +++++++ Misc/NEWS | 3 +++ 3 files changed, 14 insertions(+), 0 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1939,6 +1939,10 @@ pass else: if sig is not None: + if not isinstance(sig, Signature): + raise TypeError( + 'unexpected object {!r} in __signature__ ' + 'attribute'.format(sig)) return sig try: 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 @@ -3136,6 +3136,13 @@ self.assertEqual(lines[:-1], inspect.getsource(module).splitlines()) self.assertEqual(err, b'') + def test_custom_getattr(self): + def foo(): + pass + foo.__signature__ = 42 + with self.assertRaises(TypeError): + inspect.signature(foo) + @unittest.skipIf(ThreadPoolExecutor is None, 'threads required to test __qualname__ for source files') def test_qualname_source(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -498,6 +498,9 @@ - Issue #11571: Ensure that the turtle window becomes the topmost window when launched on OS X. +- Issue #21801: Validate that __signature__ is None or an instance of Signature. + + Extension Modules ----------------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 03:03:30 2014 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 24 Jun 2014 03:03:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzExOTc0?= =?utf-8?q?=3A__Add_tutorial_section_on_class_and_instance_variables?= Message-ID: <3gy8PZ1Y5gz7Ljg@mail.python.org> http://hg.python.org/cpython/rev/8e0b7393e921 changeset: 91350:8e0b7393e921 branch: 2.7 parent: 91345:e28004fb30c0 user: Raymond Hettinger date: Mon Jun 23 18:03:21 2014 -0700 summary: Issue #11974: Add tutorial section on class and instance variables (Based on a patch from Renee Chu.) files: Doc/tutorial/classes.rst | 71 ++++++++++++++++++++++++++++ Misc/ACKS | 1 + 2 files changed, 72 insertions(+), 0 deletions(-) diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -337,6 +337,77 @@ argument list. +.. _tut-class-and-instance-variables: + +Class and Instance Variables +---------------------------- + +Generally speaking, instance variables are for data unique to each instance +and class variables are for attributes and methods shared by all instances +of the class:: + + class Dog: + + kind = 'canine' # class variable shared by all instances + + def __init__(self, name): + self.name = name # instance variable unique to each instance + + >>> d = Dog('Fido') + >>> e = Dog('Buddy') + >>> d.kind # shared by all dogs + 'canine' + >>> e.kind # shared by all dogs + 'canine' + >>> d.name # unique to d + 'Fido' + >>> e.name # unique to e + 'Buddy' + +As discussed in :ref:`tut-object`, shared data can have possibly surprising +effects with involving :term:`mutable` objects such as lists and dictionaries. +For example, the *tricks* list in the following code should not be used as a +class variable because just a single list would be shared by all *Dog* +instances:: + + class Dog: + + tricks = [] # mistaken use of a class variable + + def __init__(self, name): + self.name = name + + def add_trick(self, trick): + self.tricks.append(trick) + + >>> d = Dog('Fido') + >>> e = Dog('Buddy') + >>> d.add_trick('roll over') + >>> e.add_trick('play dead') + >>> d.tricks # unexpectedly shared by all dogs + ['roll over', 'play dead'] + +Correct design of the class should use an instance variable instead:: + + class Dog: + + def __init__(self, name): + self.name = name + self.tricks = [] # creates a new empty list for each dog + + def add_trick(self, trick): + self.tricks.append(trick) + + >>> d = Dog('Fido') + >>> e = Dog('Buddy') + >>> d.add_trick('roll over') + >>> e.add_trick('play dead') + >>> d.tricks + ['roll over'] + >>> e.tricks + ['play dead'] + + .. _tut-remarks: Random Remarks diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -235,6 +235,7 @@ Matt Chisholm Anders Chrigstr?m Tom Christiansen +Renee Chu Vadim Chugunov Mauro Cicognini David Cinege -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 05:13:55 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 24 Jun 2014 05:13:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_avoid_overflow?= =?utf-8?q?_with_large_buffer_sizes_and/or_offsets_=28closes_=2321831=29?= Message-ID: <3gyCJ319mcz7Lk7@mail.python.org> http://hg.python.org/cpython/rev/8d963c7db507 changeset: 91351:8d963c7db507 branch: 2.7 user: Benjamin Peterson date: Mon Jun 23 20:12:27 2014 -0700 summary: avoid overflow with large buffer sizes and/or offsets (closes #21831) files: Lib/test/test_buffer.py | 6 ++++++ Misc/NEWS | 3 +++ Objects/bufferobject.c | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -4,6 +4,7 @@ """ +import sys import unittest from test import test_support @@ -29,6 +30,11 @@ m = memoryview(b) # Should not raise an exception self.assertEqual(m.tobytes(), s) + def test_large_buffer_size_and_offset(self): + data = bytearray('hola mundo') + buf = buffer(data, sys.maxsize, sys.maxsize) + self.assertEqual(buf[:4096], "") + def test_main(): with test_support.check_py3k_warnings(("buffer.. not supported", diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #21831: Avoid integer overflow when large sizes and offsets are given to + the buffer type. + - Issue #1856: Avoid crashes and lockups when daemon threads run while the interpreter is shutting down; instead, these threads are now killed when they try to take the GIL. diff --git a/Objects/bufferobject.c b/Objects/bufferobject.c --- a/Objects/bufferobject.c +++ b/Objects/bufferobject.c @@ -88,7 +88,7 @@ *size = count; else *size = self->b_size; - if (offset + *size > count) + if (*size > count - offset) *size = count - offset; } return 1; @@ -875,4 +875,4 @@ 0, /* tp_init */ 0, /* tp_alloc */ buffer_new, /* tp_new */ -}; \ No newline at end of file +}; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 05:16:12 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 24 Jun 2014 05:16:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_add_Chris_Fost?= =?utf-8?q?er?= Message-ID: <3gyCLh3YWzz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/e04eefef7820 changeset: 91352:e04eefef7820 branch: 2.7 user: Benjamin Peterson date: Mon Jun 23 20:14:46 2014 -0700 summary: add Chris Foster files: Misc/ACKS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -412,6 +412,7 @@ Michael Foord Amaury Forgeot d'Arc Doug Fort +Chris Foster John Fouhy Andrew Francis Stefan Franke -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 05:16:13 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 24 Jun 2014 05:16:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_add_Chris_Fost?= =?utf-8?q?er?= Message-ID: <3gyCLj4zT5z7LkJ@mail.python.org> http://hg.python.org/cpython/rev/fc68113cfb0c changeset: 91353:fc68113cfb0c branch: 3.4 parent: 91348:cc0f5d6ccb70 user: Benjamin Peterson date: Mon Jun 23 20:14:46 2014 -0700 summary: add Chris Foster files: Misc/ACKS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -414,6 +414,7 @@ Michael Foord Amaury Forgeot d'Arc Doug Fort +Chris Foster John Fouhy Andrew Francis Stefan Franke -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 05:16:14 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 24 Jun 2014 05:16:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_null_merge_3=2E4?= Message-ID: <3gyCLk6Tt1z7Lk7@mail.python.org> http://hg.python.org/cpython/rev/e7fa8518e99a changeset: 91354:e7fa8518e99a parent: 91349:fa5b985f0920 parent: 91348:cc0f5d6ccb70 user: Benjamin Peterson date: Mon Jun 23 20:16:01 2014 -0700 summary: null merge 3.4 files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 05:16:16 2014 From: python-checkins at python.org (benjamin.peterson) Date: Tue, 24 Jun 2014 05:16:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <3gyCLm0ytWz7LkS@mail.python.org> http://hg.python.org/cpython/rev/22cfc057a1f1 changeset: 91355:22cfc057a1f1 parent: 91354:e7fa8518e99a parent: 91353:fc68113cfb0c user: Benjamin Peterson date: Mon Jun 23 20:16:06 2014 -0700 summary: merge 3.4 files: Misc/ACKS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -415,6 +415,7 @@ Michael Foord Amaury Forgeot d'Arc Doug Fort +Chris Foster John Fouhy Andrew Francis Stefan Franke -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:04:01 2014 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 24 Jun 2014 22:04:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzExOTc0?= =?utf-8?q?=3A__Add_tutorial_section_on_class_and_instance_variables?= Message-ID: <3gydjY0RL5z7Llq@mail.python.org> http://hg.python.org/cpython/rev/8e5e04a1497f changeset: 91356:8e5e04a1497f branch: 3.4 parent: 91348:cc0f5d6ccb70 user: Raymond Hettinger date: Mon Jun 23 18:08:01 2014 -0700 summary: Issue #11974: Add tutorial section on class and instance variables (Based on a patch from Renee Chu.) files: Doc/tutorial/classes.rst | 71 ++++++++++++++++++++++++++++ Misc/ACKS | 1 + 2 files changed, 72 insertions(+), 0 deletions(-) diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -387,6 +387,77 @@ argument list. +.. _tut-class-and-instance-variables: + +Class and Instance Variables +---------------------------- + +Generally speaking, instance variables are for data unique to each instance +and class variables are for attributes and methods shared by all instances +of the class:: + + class Dog: + + kind = 'canine' # class variable shared by all instances + + def __init__(self, name): + self.name = name # instance variable unique to each instance + + >>> d = Dog('Fido') + >>> e = Dog('Buddy') + >>> d.kind # shared by all dogs + 'canine' + >>> e.kind # shared by all dogs + 'canine' + >>> d.name # unique to d + 'Fido' + >>> e.name # unique to e + 'Buddy' + +As discussed in :ref:`tut-object`, shared data can have possibly surprising +effects with involving :term:`mutable` objects such as lists and dictionaries. +For example, the *tricks* list in the following code should not be used as a +class variable because just a single list would be shared by all *Dog* +instances:: + + class Dog: + + tricks = [] # mistaken use of a class variable + + def __init__(self, name): + self.name = name + + def add_trick(self, trick): + self.tricks.append(trick) + + >>> d = Dog('Fido') + >>> e = Dog('Buddy') + >>> d.add_trick('roll over') + >>> e.add_trick('play dead') + >>> d.tricks # unexpectedly shared by all dogs + ['roll over', 'play dead'] + +Correct design of the class should use an instance variable instead:: + + class Dog: + + def __init__(self, name): + self.name = name + self.tricks = [] # creates a new empty list for each dog + + def add_trick(self, trick): + self.tricks.append(trick) + + >>> d = Dog('Fido') + >>> e = Dog('Buddy') + >>> d.add_trick('roll over') + >>> e.add_trick('play dead') + >>> d.tricks + ['roll over'] + >>> e.tricks + ['play dead'] + + .. _tut-remarks: Random Remarks diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -238,6 +238,7 @@ Lita Cho Anders Chrigstr?m Tom Christiansen +Renee Chu Vadim Chugunov Mauro Cicognini David Cinege -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:04:02 2014 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 24 Jun 2014 22:04:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy40IC0+IDMuNCk6?= =?utf-8?q?_merge?= Message-ID: <3gydjZ1sDLz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/a941bb617c2a changeset: 91357:a941bb617c2a branch: 3.4 parent: 91356:8e5e04a1497f parent: 91353:fc68113cfb0c user: Raymond Hettinger date: Tue Jun 24 13:03:24 2014 -0700 summary: merge files: Misc/ACKS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -415,6 +415,7 @@ Michael Foord Amaury Forgeot d'Arc Doug Fort +Chris Foster John Fouhy Andrew Francis Stefan Franke -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:04:03 2014 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 24 Jun 2014 22:04:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gydjb3Yqcz7Ll6@mail.python.org> http://hg.python.org/cpython/rev/3fa0d2b297c6 changeset: 91358:3fa0d2b297c6 parent: 91355:22cfc057a1f1 parent: 91357:a941bb617c2a user: Raymond Hettinger date: Tue Jun 24 13:03:54 2014 -0700 summary: merge files: Doc/tutorial/classes.rst | 71 ++++++++++++++++++++++++++++ Misc/ACKS | 1 + 2 files changed, 72 insertions(+), 0 deletions(-) diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -387,6 +387,77 @@ argument list. +.. _tut-class-and-instance-variables: + +Class and Instance Variables +---------------------------- + +Generally speaking, instance variables are for data unique to each instance +and class variables are for attributes and methods shared by all instances +of the class:: + + class Dog: + + kind = 'canine' # class variable shared by all instances + + def __init__(self, name): + self.name = name # instance variable unique to each instance + + >>> d = Dog('Fido') + >>> e = Dog('Buddy') + >>> d.kind # shared by all dogs + 'canine' + >>> e.kind # shared by all dogs + 'canine' + >>> d.name # unique to d + 'Fido' + >>> e.name # unique to e + 'Buddy' + +As discussed in :ref:`tut-object`, shared data can have possibly surprising +effects with involving :term:`mutable` objects such as lists and dictionaries. +For example, the *tricks* list in the following code should not be used as a +class variable because just a single list would be shared by all *Dog* +instances:: + + class Dog: + + tricks = [] # mistaken use of a class variable + + def __init__(self, name): + self.name = name + + def add_trick(self, trick): + self.tricks.append(trick) + + >>> d = Dog('Fido') + >>> e = Dog('Buddy') + >>> d.add_trick('roll over') + >>> e.add_trick('play dead') + >>> d.tricks # unexpectedly shared by all dogs + ['roll over', 'play dead'] + +Correct design of the class should use an instance variable instead:: + + class Dog: + + def __init__(self, name): + self.name = name + self.tricks = [] # creates a new empty list for each dog + + def add_trick(self, trick): + self.tricks.append(trick) + + >>> d = Dog('Fido') + >>> e = Dog('Buddy') + >>> d.add_trick('roll over') + >>> e.add_trick('play dead') + >>> d.tricks + ['roll over'] + >>> e.tricks + ['play dead'] + + .. _tut-remarks: Random Remarks diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -239,6 +239,7 @@ Lita Cho Anders Chrigstr?m Tom Christiansen +Renee Chu Vadim Chugunov Mauro Cicognini David Cinege -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:42:58 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 24 Jun 2014 22:42:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogTG9n?= =?utf-8?q?_an_error_if_a_Task_is_destroyed_while_it_is_still_pending?= Message-ID: <3gyfZV5mKKz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/978525270264 changeset: 91359:978525270264 branch: 3.4 parent: 91357:a941bb617c2a user: Victor Stinner date: Tue Jun 24 22:37:53 2014 +0200 summary: asyncio: Log an error if a Task is destroyed while it is still pending files: Lib/asyncio/futures.py | 3 + Lib/asyncio/tasks.py | 13 +++ Lib/test/test_asyncio/test_base_events.py | 3 +- Lib/test/test_asyncio/test_tasks.py | 45 ++++++++++- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -169,6 +169,9 @@ res += '<{}>'.format(self._state) return res + # On Python 3.3 or older, objects with a destructor part of a reference + # cycle are never destroyed. It's not more the case on Python 3.4 thanks to + # the PEP 442. if _PY34: def __del__(self): if not self._log_traceback: diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -32,6 +32,7 @@ _DEBUG = (not sys.flags.ignore_environment and bool(os.environ.get('PYTHONASYNCIODEBUG'))) +_PY34 = (sys.version_info >= (3, 4)) _PY35 = (sys.version_info >= (3, 5)) @@ -181,6 +182,18 @@ self._loop.call_soon(self._step) self.__class__._all_tasks.add(self) + # On Python 3.3 or older, objects with a destructor part of a reference + # cycle are never destroyed. It's not more the case on Python 3.4 thanks to + # the PEP 442. + if _PY34: + def __del__(self): + if self._state == futures._PENDING: + self._loop.call_exception_handler({ + 'task': self, + 'message': 'Task was destroyed but it is pending!', + }) + futures.Future.__del__(self) + def __repr__(self): res = super().__repr__() if (self._must_cancel and 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 @@ -244,7 +244,8 @@ @mock.patch('asyncio.base_events.logger') def test__run_once_logging(self, m_logger): def slow_select(timeout): - time.sleep(1.0) + # Sleep a bit longer than a second to avoid timer resolution issues. + time.sleep(1.1) return [] # logging needs debug flag 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 @@ -5,13 +5,16 @@ import types import unittest import weakref +from test import support from test.script_helper import assert_python_ok +from unittest import mock import asyncio from asyncio import tasks from asyncio import test_utils +PY34 = (sys.version_info >= (3, 4)) PY35 = (sys.version_info >= (3, 5)) @@ -1501,9 +1504,45 @@ def test_corowrapper_weakref(self): wd = weakref.WeakValueDictionary() def foo(): yield from [] - cw = asyncio.tasks.CoroWrapper(foo(), foo) - wd['cw'] = cw # Would fail without __weakref__ slot. - cw.gen = None # Suppress warning from __del__. + + @unittest.skipUnless(PY34, + 'need python 3.4 or later') + def test_log_destroyed_pending_task(self): + @asyncio.coroutine + def kill_me(loop): + future = asyncio.Future(loop=loop) + yield from future + # at this point, the only reference to kill_me() task is + # the Task._wakeup() method in future._callbacks + raise Exception("code never reached") + + mock_handler = mock.Mock() + self.loop.set_exception_handler(mock_handler) + + # schedule the task + coro = kill_me(self.loop) + task = asyncio.async(coro, loop=self.loop) + self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), {task}) + + # execute the task so it waits for future + self.loop._run_once() + self.assertEqual(len(self.loop._ready), 0) + + # remove the future used in kill_me(), and references to the task + del coro.gi_frame.f_locals['future'] + coro = None + task = None + + # no more reference to kill_me() task: the task is destroyed by the GC + support.gc_collect() + + self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), set()) + + mock_handler.assert_called_with(self.loop, { + 'message': 'Task was destroyed but it is pending!', + 'task': mock.ANY, + }) + mock_handler.reset_mock() class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:43:00 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 24 Jun 2014 22:43:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Log_an_error_if_a_Task_is_d?= =?utf-8?q?estroyed_while_it_is_still?= Message-ID: <3gyfZX16bNz7LkC@mail.python.org> http://hg.python.org/cpython/rev/e1d81c32f13d changeset: 91360:e1d81c32f13d parent: 91358:3fa0d2b297c6 parent: 91359:978525270264 user: Victor Stinner date: Tue Jun 24 22:38:31 2014 +0200 summary: (Merge 3.4) asyncio: Log an error if a Task is destroyed while it is still pending files: Lib/asyncio/futures.py | 3 + Lib/asyncio/tasks.py | 13 +++ Lib/test/test_asyncio/test_base_events.py | 3 +- Lib/test/test_asyncio/test_tasks.py | 45 ++++++++++- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -169,6 +169,9 @@ res += '<{}>'.format(self._state) return res + # On Python 3.3 or older, objects with a destructor part of a reference + # cycle are never destroyed. It's not more the case on Python 3.4 thanks to + # the PEP 442. if _PY34: def __del__(self): if not self._log_traceback: diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -32,6 +32,7 @@ _DEBUG = (not sys.flags.ignore_environment and bool(os.environ.get('PYTHONASYNCIODEBUG'))) +_PY34 = (sys.version_info >= (3, 4)) _PY35 = (sys.version_info >= (3, 5)) @@ -181,6 +182,18 @@ self._loop.call_soon(self._step) self.__class__._all_tasks.add(self) + # On Python 3.3 or older, objects with a destructor part of a reference + # cycle are never destroyed. It's not more the case on Python 3.4 thanks to + # the PEP 442. + if _PY34: + def __del__(self): + if self._state == futures._PENDING: + self._loop.call_exception_handler({ + 'task': self, + 'message': 'Task was destroyed but it is pending!', + }) + futures.Future.__del__(self) + def __repr__(self): res = super().__repr__() if (self._must_cancel and 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 @@ -244,7 +244,8 @@ @mock.patch('asyncio.base_events.logger') def test__run_once_logging(self, m_logger): def slow_select(timeout): - time.sleep(1.0) + # Sleep a bit longer than a second to avoid timer resolution issues. + time.sleep(1.1) return [] # logging needs debug flag 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 @@ -5,13 +5,16 @@ import types import unittest import weakref +from test import support from test.script_helper import assert_python_ok +from unittest import mock import asyncio from asyncio import tasks from asyncio import test_utils +PY34 = (sys.version_info >= (3, 4)) PY35 = (sys.version_info >= (3, 5)) @@ -1501,9 +1504,45 @@ def test_corowrapper_weakref(self): wd = weakref.WeakValueDictionary() def foo(): yield from [] - cw = asyncio.tasks.CoroWrapper(foo(), foo) - wd['cw'] = cw # Would fail without __weakref__ slot. - cw.gen = None # Suppress warning from __del__. + + @unittest.skipUnless(PY34, + 'need python 3.4 or later') + def test_log_destroyed_pending_task(self): + @asyncio.coroutine + def kill_me(loop): + future = asyncio.Future(loop=loop) + yield from future + # at this point, the only reference to kill_me() task is + # the Task._wakeup() method in future._callbacks + raise Exception("code never reached") + + mock_handler = mock.Mock() + self.loop.set_exception_handler(mock_handler) + + # schedule the task + coro = kill_me(self.loop) + task = asyncio.async(coro, loop=self.loop) + self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), {task}) + + # execute the task so it waits for future + self.loop._run_once() + self.assertEqual(len(self.loop._ready), 0) + + # remove the future used in kill_me(), and references to the task + del coro.gi_frame.f_locals['future'] + coro = None + task = None + + # no more reference to kill_me() task: the task is destroyed by the GC + support.gc_collect() + + self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), set()) + + mock_handler.assert_called_with(self.loop, { + 'message': 'Task was destroyed but it is pending!', + 'task': mock.ANY, + }) + mock_handler.reset_mock() class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:49:39 2014 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 24 Jun 2014 22:49:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgMjE4MzI6?= =?utf-8?q?__Require_named_tuple_inputs_to_be_exact_strings?= Message-ID: <3gyfkC5KLxz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/30063f97a44d changeset: 91361:30063f97a44d branch: 2.7 parent: 91352:e04eefef7820 user: Raymond Hettinger date: Tue Jun 24 13:49:24 2014 -0700 summary: Issue 21832: Require named tuple inputs to be exact strings files: Lib/collections.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/collections.py b/Lib/collections.py --- a/Lib/collections.py +++ b/Lib/collections.py @@ -314,6 +314,7 @@ if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = map(str, field_names) + typename = str(typename) if rename: seen = set() for index, name in enumerate(field_names): @@ -326,6 +327,8 @@ field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: + if type(name) != str: + raise TypeError('Type names and field names must be strings') if not all(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain ' 'alphanumeric characters and underscores: %r' % name) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:51:27 2014 From: python-checkins at python.org (r.david.murray) Date: Tue, 24 Jun 2014 22:51:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogIzIwMTU1OiB1c2Ug?= =?utf-8?q?fake_HTTP_method_names_so_windows_doesn=27t_hang_the_tests=2E?= Message-ID: <3gyfmH2nfjz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/ffdd2d0b0049 changeset: 91362:ffdd2d0b0049 branch: 3.4 parent: 91359:978525270264 user: R David Murray date: Tue Jun 24 16:39:49 2014 -0400 summary: #20155: use fake HTTP method names so windows doesn't hang the tests. Windows was seeing the 'GET' generated by these tests as invalid and forcibly closing the socket, causing the test to fail. Patch by Jeff Allen. files: Lib/test/test_httpservers.py | 5 +++-- Misc/ACKS | 1 + Misc/NEWS | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) 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 @@ -125,7 +125,7 @@ def test_request_line_trimming(self): self.con._http_vsn_str = 'HTTP/1.1\n' - self.con.putrequest('GET', '/') + self.con.putrequest('XYZBOGUS', '/') self.con.endheaders() res = self.con.getresponse() self.assertEqual(res.status, 501) @@ -152,8 +152,9 @@ self.assertEqual(res.status, 501) def test_version_none(self): + # Test that a valid method is rejected when not HTTP/1.x self.con._http_vsn_str = '' - self.con.putrequest('PUT', '/') + self.con.putrequest('CUSTOM', '/') self.con.endheaders() res = self.con.getresponse() self.assertEqual(res.status, 400) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -29,6 +29,7 @@ Jyrki Alakuijala Steve Alexander Fred Allen +Jeff Allen Ray Allen Billy G. Allie Kevin Altis diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -133,6 +133,10 @@ Tests ----- +- Issue #20155: Changed HTTP method names in failing tests in test_httpservers + so that packet filtering software (specifically Windows Base Filtering Engine) + does not interfere with the transaction semantics expected by the tests. + - Issue #19493: Refactored the ctypes test package to skip tests explicitly rather than silently. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:51:28 2014 From: python-checkins at python.org (r.david.murray) Date: Tue, 24 Jun 2014 22:51:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge=3A_=2320155=3A_use_fake_HTTP_method_names_so_windo?= =?utf-8?q?ws_doesn=27t_hang_the_tests=2E?= Message-ID: <3gyfmJ4RR9z7Ljp@mail.python.org> http://hg.python.org/cpython/rev/e67ad57eed26 changeset: 91363:e67ad57eed26 parent: 91360:e1d81c32f13d parent: 91362:ffdd2d0b0049 user: R David Murray date: Tue Jun 24 16:49:04 2014 -0400 summary: merge: #20155: use fake HTTP method names so windows doesn't hang the tests. files: Lib/test/test_httpservers.py | 5 +++-- Misc/ACKS | 1 + Misc/NEWS | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) 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 @@ -125,7 +125,7 @@ def test_request_line_trimming(self): self.con._http_vsn_str = 'HTTP/1.1\n' - self.con.putrequest('GET', '/') + self.con.putrequest('XYZBOGUS', '/') self.con.endheaders() res = self.con.getresponse() self.assertEqual(res.status, 501) @@ -152,8 +152,9 @@ self.assertEqual(res.status, 501) def test_version_none(self): + # Test that a valid method is rejected when not HTTP/1.x self.con._http_vsn_str = '' - self.con.putrequest('PUT', '/') + self.con.putrequest('CUSTOM', '/') self.con.endheaders() res = self.con.getresponse() self.assertEqual(res.status, 400) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -29,6 +29,7 @@ Jyrki Alakuijala Steve Alexander Fred Allen +Jeff Allen Ray Allen Billy G. Allie Kevin Altis diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -612,6 +612,10 @@ Tests ----- +- Issue #20155: Changed HTTP method names in failing tests in test_httpservers + so that packet filtering software (specifically Windows Base Filtering Engine) + does not interfere with the transaction semantics expected by the tests. + - Issue #19493: Refactored the ctypes test package to skip tests explicitly rather than silently. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:51:29 2014 From: python-checkins at python.org (r.david.murray) Date: Tue, 24 Jun 2014 22:51:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogIzIwMTU1OiB1c2Ug?= =?utf-8?q?fake_HTTP_method_names_so_windows_doesn=27t_hang_the_tests=2E?= Message-ID: <3gyfmK5sXlz7LkF@mail.python.org> http://hg.python.org/cpython/rev/b0526da56c54 changeset: 91364:b0526da56c54 branch: 2.7 parent: 91361:30063f97a44d user: R David Murray date: Tue Jun 24 16:49:24 2014 -0400 summary: #20155: use fake HTTP method names so windows doesn't hang the tests. Windows was seeing the 'GET' generated by these tests as invalid and forcibly closing the socket, causing the test to fail. Patch by Jeff Allen. files: Lib/test/test_httpservers.py | 5 +++-- Misc/ACKS | 1 + Misc/NEWS | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) 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 @@ -189,7 +189,7 @@ def test_request_line_trimming(self): self.con._http_vsn_str = 'HTTP/1.1\n' - self.con.putrequest('GET', '/') + self.con.putrequest('XYZBOGUS', '/') self.con.endheaders() res = self.con.getresponse() self.assertEqual(res.status, 501) @@ -216,8 +216,9 @@ self.assertEqual(res.status, 501) def test_version_none(self): + # Test that a valid method is rejected when not HTTP/1.x self.con._http_vsn_str = '' - self.con.putrequest('PUT', '/') + self.con.putrequest('CUSTOM', '/') self.con.endheaders() res = self.con.getresponse() self.assertEqual(res.status, 400) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -28,6 +28,7 @@ Jyrki Alakuijala Steve Alexander Fred Allen +Jeff Allen Ray Allen Billy G. Allie Kevin Altis diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -100,6 +100,10 @@ Tests ----- +- Issue #20155: Changed HTTP method names in failing tests in test_httpservers + so that packet filtering software (specifically Windows Base Filtering Engine) + does not interfere with the transaction semantics expected by the tests. + - Issue #19493: Refactored the ctypes test package to skip tests explicitly rather than silently. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:58:37 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 24 Jun 2014 22:58:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogcmVw?= =?utf-8?q?r=28Task=29_now_also_contains_the_line_number_even_if_the_corou?= =?utf-8?q?tine_is?= Message-ID: <3gyfwY3HPKz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/3bedc1846202 changeset: 91365:3bedc1846202 branch: 3.4 parent: 91362:ffdd2d0b0049 user: Victor Stinner date: Tue Jun 24 22:57:14 2014 +0200 summary: asyncio: repr(Task) now also contains the line number even if the coroutine is done: use the first line number of the code object instead of the current line number of the generator frame. The name of the coroutine is not enough because many coroutines may have the same name. It's a common case in asyncio tests for example. files: Lib/asyncio/tasks.py | 6 ++++-- Lib/test/test_asyncio/test_tasks.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -208,9 +208,11 @@ if iscoroutine(coro): filename = coro.gi_code.co_filename if coro.gi_frame is not None: - text += ' at %s:%s' % (filename, coro.gi_frame.f_lineno) + lineno = coro.gi_frame.f_lineno + text += ' at %s:%s' % (filename, lineno) else: - text += ' done at %s' % filename + lineno = coro.gi_code.co_firstlineno + text += ' done at %s:%s' % (filename, lineno) res = res[:i] + '(<{}>)'.format(text) + res[i:] return res 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 @@ -148,12 +148,14 @@ self.assertRaises(asyncio.CancelledError, self.loop.run_until_complete, t) self.assertEqual(repr(t), - 'Task()' % filename) + 'Task()' + % (filename, lineno)) t = asyncio.Task(notmuch(), loop=self.loop) self.loop.run_until_complete(t) self.assertEqual(repr(t), - "Task()" % filename) + "Task()" + % (filename, lineno)) def test_task_repr_custom(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Tue Jun 24 22:58:38 2014 From: python-checkins at python.org (victor.stinner) Date: Tue, 24 Jun 2014 22:58:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_repr=28Task=29_now_also_con?= =?utf-8?q?tains_the_line_number_even_if_the?= Message-ID: <3gyfwZ517dz7Ljp@mail.python.org> http://hg.python.org/cpython/rev/4a0a0d4d5fd5 changeset: 91366:4a0a0d4d5fd5 parent: 91363:e67ad57eed26 parent: 91365:3bedc1846202 user: Victor Stinner date: Tue Jun 24 22:58:23 2014 +0200 summary: (Merge 3.4) asyncio: repr(Task) now also contains the line number even if the coroutine is done: use the first line number of the code object instead of the current line number of the generator frame. The name of the coroutine is not enough because many coroutines may have the same name. It's a common case in asyncio tests for example. files: Lib/asyncio/tasks.py | 6 ++++-- Lib/test/test_asyncio/test_tasks.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -208,9 +208,11 @@ if iscoroutine(coro): filename = coro.gi_code.co_filename if coro.gi_frame is not None: - text += ' at %s:%s' % (filename, coro.gi_frame.f_lineno) + lineno = coro.gi_frame.f_lineno + text += ' at %s:%s' % (filename, lineno) else: - text += ' done at %s' % filename + lineno = coro.gi_code.co_firstlineno + text += ' done at %s:%s' % (filename, lineno) res = res[:i] + '(<{}>)'.format(text) + res[i:] return res 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 @@ -148,12 +148,14 @@ self.assertRaises(asyncio.CancelledError, self.loop.run_until_complete, t) self.assertEqual(repr(t), - 'Task()' % filename) + 'Task()' + % (filename, lineno)) t = asyncio.Task(notmuch(), loop=self.loop) self.loop.run_until_complete(t) self.assertEqual(repr(t), - "Task()" % filename) + "Task()" + % (filename, lineno)) def test_task_repr_custom(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:08:51 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:08:51 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgMjE4MzI6?= =?utf-8?q?__Require_named_tuple_inputs_to_be_exact_strings?= Message-ID: <3gymTH595Wz7LkH@mail.python.org> http://hg.python.org/cpython/rev/c238d2899d47 changeset: 91367:c238d2899d47 branch: 3.4 parent: 91357:a941bb617c2a user: Raymond Hettinger date: Tue Jun 24 13:44:03 2014 -0700 summary: Issue 21832: Require named tuple inputs to be exact strings files: Lib/collections/__init__.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -323,6 +323,7 @@ if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) + typename = str(typename) if rename: seen = set() for index, name in enumerate(field_names): @@ -333,6 +334,8 @@ field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: + if type(name) != str: + raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:08:52 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:08:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gymTJ6rLzz7LkP@mail.python.org> http://hg.python.org/cpython/rev/2f3a0ad5fe1f changeset: 91368:2f3a0ad5fe1f parent: 91358:3fa0d2b297c6 parent: 91367:c238d2899d47 user: Raymond Hettinger date: Tue Jun 24 13:44:39 2014 -0700 summary: merge files: Lib/collections/__init__.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -352,6 +352,7 @@ if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) + typename = str(typename) if rename: seen = set() for index, name in enumerate(field_names): @@ -362,6 +363,8 @@ field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: + if type(name) != str: + raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:08:54 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:08:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gymTL2SBvz7LkP@mail.python.org> http://hg.python.org/cpython/rev/a4095a895425 changeset: 91369:a4095a895425 parent: 91368:2f3a0ad5fe1f parent: 91360:e1d81c32f13d user: Raymond Hettinger date: Tue Jun 24 13:51:18 2014 -0700 summary: merge files: Lib/asyncio/futures.py | 3 + Lib/asyncio/tasks.py | 13 +++ Lib/test/test_asyncio/test_base_events.py | 3 +- Lib/test/test_asyncio/test_tasks.py | 45 ++++++++++- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -169,6 +169,9 @@ res += '<{}>'.format(self._state) return res + # On Python 3.3 or older, objects with a destructor part of a reference + # cycle are never destroyed. It's not more the case on Python 3.4 thanks to + # the PEP 442. if _PY34: def __del__(self): if not self._log_traceback: diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -32,6 +32,7 @@ _DEBUG = (not sys.flags.ignore_environment and bool(os.environ.get('PYTHONASYNCIODEBUG'))) +_PY34 = (sys.version_info >= (3, 4)) _PY35 = (sys.version_info >= (3, 5)) @@ -181,6 +182,18 @@ self._loop.call_soon(self._step) self.__class__._all_tasks.add(self) + # On Python 3.3 or older, objects with a destructor part of a reference + # cycle are never destroyed. It's not more the case on Python 3.4 thanks to + # the PEP 442. + if _PY34: + def __del__(self): + if self._state == futures._PENDING: + self._loop.call_exception_handler({ + 'task': self, + 'message': 'Task was destroyed but it is pending!', + }) + futures.Future.__del__(self) + def __repr__(self): res = super().__repr__() if (self._must_cancel and 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 @@ -244,7 +244,8 @@ @mock.patch('asyncio.base_events.logger') def test__run_once_logging(self, m_logger): def slow_select(timeout): - time.sleep(1.0) + # Sleep a bit longer than a second to avoid timer resolution issues. + time.sleep(1.1) return [] # logging needs debug flag 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 @@ -5,13 +5,16 @@ import types import unittest import weakref +from test import support from test.script_helper import assert_python_ok +from unittest import mock import asyncio from asyncio import tasks from asyncio import test_utils +PY34 = (sys.version_info >= (3, 4)) PY35 = (sys.version_info >= (3, 5)) @@ -1501,9 +1504,45 @@ def test_corowrapper_weakref(self): wd = weakref.WeakValueDictionary() def foo(): yield from [] - cw = asyncio.tasks.CoroWrapper(foo(), foo) - wd['cw'] = cw # Would fail without __weakref__ slot. - cw.gen = None # Suppress warning from __del__. + + @unittest.skipUnless(PY34, + 'need python 3.4 or later') + def test_log_destroyed_pending_task(self): + @asyncio.coroutine + def kill_me(loop): + future = asyncio.Future(loop=loop) + yield from future + # at this point, the only reference to kill_me() task is + # the Task._wakeup() method in future._callbacks + raise Exception("code never reached") + + mock_handler = mock.Mock() + self.loop.set_exception_handler(mock_handler) + + # schedule the task + coro = kill_me(self.loop) + task = asyncio.async(coro, loop=self.loop) + self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), {task}) + + # execute the task so it waits for future + self.loop._run_once() + self.assertEqual(len(self.loop._ready), 0) + + # remove the future used in kill_me(), and references to the task + del coro.gi_frame.f_locals['future'] + coro = None + task = None + + # no more reference to kill_me() task: the task is destroyed by the GC + support.gc_collect() + + self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), set()) + + mock_handler.assert_called_with(self.loop, { + 'message': 'Task was destroyed but it is pending!', + 'task': mock.ANY, + }) + mock_handler.reset_mock() class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:08:55 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:08:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy40IC0+IDMuNCk6?= =?utf-8?q?_merge?= Message-ID: <3gymTM5DxLz7LkM@mail.python.org> http://hg.python.org/cpython/rev/73cb193a6666 changeset: 91370:73cb193a6666 branch: 3.4 parent: 91367:c238d2899d47 parent: 91359:978525270264 user: Raymond Hettinger date: Tue Jun 24 13:51:42 2014 -0700 summary: merge files: Lib/asyncio/futures.py | 3 + Lib/asyncio/tasks.py | 13 +++ Lib/test/test_asyncio/test_base_events.py | 3 +- Lib/test/test_asyncio/test_tasks.py | 45 ++++++++++- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -169,6 +169,9 @@ res += '<{}>'.format(self._state) return res + # On Python 3.3 or older, objects with a destructor part of a reference + # cycle are never destroyed. It's not more the case on Python 3.4 thanks to + # the PEP 442. if _PY34: def __del__(self): if not self._log_traceback: diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -32,6 +32,7 @@ _DEBUG = (not sys.flags.ignore_environment and bool(os.environ.get('PYTHONASYNCIODEBUG'))) +_PY34 = (sys.version_info >= (3, 4)) _PY35 = (sys.version_info >= (3, 5)) @@ -181,6 +182,18 @@ self._loop.call_soon(self._step) self.__class__._all_tasks.add(self) + # On Python 3.3 or older, objects with a destructor part of a reference + # cycle are never destroyed. It's not more the case on Python 3.4 thanks to + # the PEP 442. + if _PY34: + def __del__(self): + if self._state == futures._PENDING: + self._loop.call_exception_handler({ + 'task': self, + 'message': 'Task was destroyed but it is pending!', + }) + futures.Future.__del__(self) + def __repr__(self): res = super().__repr__() if (self._must_cancel and 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 @@ -244,7 +244,8 @@ @mock.patch('asyncio.base_events.logger') def test__run_once_logging(self, m_logger): def slow_select(timeout): - time.sleep(1.0) + # Sleep a bit longer than a second to avoid timer resolution issues. + time.sleep(1.1) return [] # logging needs debug flag 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 @@ -5,13 +5,16 @@ import types import unittest import weakref +from test import support from test.script_helper import assert_python_ok +from unittest import mock import asyncio from asyncio import tasks from asyncio import test_utils +PY34 = (sys.version_info >= (3, 4)) PY35 = (sys.version_info >= (3, 5)) @@ -1501,9 +1504,45 @@ def test_corowrapper_weakref(self): wd = weakref.WeakValueDictionary() def foo(): yield from [] - cw = asyncio.tasks.CoroWrapper(foo(), foo) - wd['cw'] = cw # Would fail without __weakref__ slot. - cw.gen = None # Suppress warning from __del__. + + @unittest.skipUnless(PY34, + 'need python 3.4 or later') + def test_log_destroyed_pending_task(self): + @asyncio.coroutine + def kill_me(loop): + future = asyncio.Future(loop=loop) + yield from future + # at this point, the only reference to kill_me() task is + # the Task._wakeup() method in future._callbacks + raise Exception("code never reached") + + mock_handler = mock.Mock() + self.loop.set_exception_handler(mock_handler) + + # schedule the task + coro = kill_me(self.loop) + task = asyncio.async(coro, loop=self.loop) + self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), {task}) + + # execute the task so it waits for future + self.loop._run_once() + self.assertEqual(len(self.loop._ready), 0) + + # remove the future used in kill_me(), and references to the task + del coro.gi_frame.f_locals['future'] + coro = None + task = None + + # no more reference to kill_me() task: the task is destroyed by the GC + support.gc_collect() + + self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), set()) + + mock_handler.assert_called_with(self.loop, { + 'message': 'Task was destroyed but it is pending!', + 'task': mock.ANY, + }) + mock_handler.reset_mock() class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:08:56 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:08:56 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gymTN6vyjz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/e4249be10442 changeset: 91371:e4249be10442 parent: 91369:a4095a895425 parent: 91370:73cb193a6666 user: Raymond Hettinger date: Tue Jun 24 13:51:59 2014 -0700 summary: merge files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:08:58 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:08:58 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gymTQ1bmzz7Lks@mail.python.org> http://hg.python.org/cpython/rev/0039154be0c0 changeset: 91372:0039154be0c0 parent: 91371:e4249be10442 parent: 91363:e67ad57eed26 user: Raymond Hettinger date: Tue Jun 24 13:52:19 2014 -0700 summary: merge files: Lib/test/test_httpservers.py | 5 +++-- Misc/ACKS | 1 + Misc/NEWS | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) 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 @@ -125,7 +125,7 @@ def test_request_line_trimming(self): self.con._http_vsn_str = 'HTTP/1.1\n' - self.con.putrequest('GET', '/') + self.con.putrequest('XYZBOGUS', '/') self.con.endheaders() res = self.con.getresponse() self.assertEqual(res.status, 501) @@ -152,8 +152,9 @@ self.assertEqual(res.status, 501) def test_version_none(self): + # Test that a valid method is rejected when not HTTP/1.x self.con._http_vsn_str = '' - self.con.putrequest('PUT', '/') + self.con.putrequest('CUSTOM', '/') self.con.endheaders() res = self.con.getresponse() self.assertEqual(res.status, 400) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -29,6 +29,7 @@ Jyrki Alakuijala Steve Alexander Fred Allen +Jeff Allen Ray Allen Billy G. Allie Kevin Altis diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -612,6 +612,10 @@ Tests ----- +- Issue #20155: Changed HTTP method names in failing tests in test_httpservers + so that packet filtering software (specifically Windows Base Filtering Engine) + does not interfere with the transaction semantics expected by the tests. + - Issue #19493: Refactored the ctypes test package to skip tests explicitly rather than silently. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:08:59 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:08:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgMjE4MzI6?= =?utf-8?q?__Require_named_tuple_inputs_to_be_exact_strings?= Message-ID: <3gymTR3MXJz7LkK@mail.python.org> http://hg.python.org/cpython/rev/5c60dd518182 changeset: 91373:5c60dd518182 branch: 3.4 parent: 91365:3bedc1846202 user: Raymond Hettinger date: Tue Jun 24 15:20:55 2014 -0700 summary: Issue 21832: Require named tuple inputs to be exact strings files: Lib/collections/__init__.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -323,6 +323,7 @@ if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) + typename = str(typename) if rename: seen = set() for index, name in enumerate(field_names): @@ -333,6 +334,8 @@ field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: + if type(name) != str: + raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:09:00 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:09:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gymTS53yHz7Lkm@mail.python.org> http://hg.python.org/cpython/rev/f0f6650ba35e changeset: 91374:f0f6650ba35e parent: 91366:4a0a0d4d5fd5 parent: 91373:5c60dd518182 user: Raymond Hettinger date: Tue Jun 24 15:21:24 2014 -0700 summary: merge files: Lib/collections/__init__.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -352,6 +352,7 @@ if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) + typename = str(typename) if rename: seen = set() for index, name in enumerate(field_names): @@ -362,6 +363,8 @@ field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: + if type(name) != str: + raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:09:01 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:09:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy40IC0+IDMuNCk6?= =?utf-8?q?_merge?= Message-ID: <3gymTT6c9yz7LkK@mail.python.org> http://hg.python.org/cpython/rev/fa2638e5370f changeset: 91375:fa2638e5370f branch: 3.4 parent: 91373:5c60dd518182 parent: 91370:73cb193a6666 user: Raymond Hettinger date: Tue Jun 24 18:07:14 2014 -0700 summary: merge files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:09:03 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:09:03 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gymTW13SMz7LkK@mail.python.org> http://hg.python.org/cpython/rev/0b44d8b3412c changeset: 91376:0b44d8b3412c parent: 91374:f0f6650ba35e parent: 91372:0039154be0c0 user: Raymond Hettinger date: Tue Jun 24 18:07:33 2014 -0700 summary: merge files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:09:04 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:09:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gymTX2dF7z7LkP@mail.python.org> http://hg.python.org/cpython/rev/eee636e3edbe changeset: 91377:eee636e3edbe parent: 91376:0b44d8b3412c parent: 91375:fa2638e5370f user: Raymond Hettinger date: Tue Jun 24 18:07:57 2014 -0700 summary: merge files: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:13:40 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:13:40 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Add_news_entry?= =?utf-8?q?_for_=2321832?= Message-ID: <3gymZr5qlKz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/958e8bebda6d changeset: 91378:958e8bebda6d branch: 3.4 parent: 91375:fa2638e5370f user: Raymond Hettinger date: Tue Jun 24 18:11:48 2014 -0700 summary: Add news entry for #21832 files: Misc/NEWS | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,8 @@ - Issue #21491: socketserver: Fix a race condition in child processes reaping. +- Issue #21832: Require named tuple inputs to be exact strings. + - Issue #21812: turtle.shapetransform did not tranform the turtle on the first call. (Issue identified and fixed by Lita Cho.) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:13:42 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:13:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gymZt0MnKz7LkB@mail.python.org> http://hg.python.org/cpython/rev/c0a7db165344 changeset: 91379:c0a7db165344 parent: 91377:eee636e3edbe parent: 91378:958e8bebda6d user: Raymond Hettinger date: Tue Jun 24 18:13:31 2014 -0700 summary: merge files: Misc/NEWS | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -108,6 +108,8 @@ - Issue #21719: Added the ``st_file_attributes`` field to os.stat_result on Windows. +- Issue #21832: Require named tuple inputs to be exact strings. + - Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 03:15:33 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 03:15:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Add_news_entry?= =?utf-8?q?_for_21832?= Message-ID: <3gymd163wGz7Ljj@mail.python.org> http://hg.python.org/cpython/rev/69783040dd54 changeset: 91380:69783040dd54 branch: 2.7 parent: 91364:b0526da56c54 user: Raymond Hettinger date: Tue Jun 24 18:14:53 2014 -0700 summary: Add news entry for 21832 files: Misc/NEWS | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -34,6 +34,8 @@ - Issue #21672: Fix the behavior of ntpath.join on UNC-style paths. +- Issue #21832: Require named tuple inputs to be exact strings. + - Issue #8343: Named group error messages in the re module did not show the name of the erroneous group. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 04:22:18 2014 From: python-checkins at python.org (terry.reedy) Date: Wed, 25 Jun 2014 04:22:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE0MTE3?= =?utf-8?q?=3A_Inprove_help_text_and_docstrings=2C_some_for_clarity=2C_som?= =?utf-8?q?e_just_to?= Message-ID: <3gyp6240rfz7LjW@mail.python.org> http://hg.python.org/cpython/rev/129ec3d90a67 changeset: 91381:129ec3d90a67 branch: 2.7 user: Terry Jan Reedy date: Tue Jun 24 22:21:36 2014 -0400 summary: Issue #14117: Inprove help text and docstrings, some for clarity, some just to fit in the default width of the text window (45 chars). files: Demo/turtle/demohelp.txt | 40 ++++++++------- Demo/turtle/tdemo_paint.py | 12 +++- Demo/turtle/tdemo_peace.py | 16 ++---- Demo/turtle/tdemo_planet_and_moon.py | 9 +-- Demo/turtle/tdemo_tree.py | 6 +- 5 files changed, 42 insertions(+), 41 deletions(-) diff --git a/Demo/turtle/demohelp.txt b/Demo/turtle/demohelp.txt --- a/Demo/turtle/demohelp.txt +++ b/Demo/turtle/demohelp.txt @@ -2,7 +2,7 @@ ---------------------------------------------- - xturtleDemo - Help + turtleDemo - Help ---------------------------------------------- @@ -53,27 +53,29 @@ (2) How to add your own demos to the demo repository - - scriptname: must begin with tdemo_ , + - The script name must begin with tdemo_ , so it must have the form tdemo_.py - - place: same directory as xturtleDemo.py or some - subdirectory, the name of which must also begin with - tdemo_..... + - The code must contain a main() function which will + be executed by the viewer (see provided example scripts). + It may return a string which will be displayed in the Label below + the source code window (when execution has finished.) + + - In order to run mydemo.py by itself, such as during development, + add the following at the end of the file: - - requirements on source code: - code must contain a main() function which will - be executed by the viewer (see provided example scripts) - main() may return a string which will be displayed - in the Label below the source code window (when execution - has finished.) + if __name__ == '__main__': + main() + mainloop() # keep window - If the demo is EVENT DRIVEN, main must return the string - "EVENTLOOP". This informs the demo viewer that the script is - still running and must be stopped by the user! + python -m turtledemo.mydemo # will then run it - If an "EVENTLOOP" demo runs by itself, as with clock, which uses - ontimer, or minimal_hanoi, which loops by recursion, then the - code should catch the turtle.Terminator exception that will be - raised when the user presses the STOP button. (Paint is not such - a demo; it only acts in response to mouse clicks and movements.) + - If the demo is EVENT DRIVEN, main must return the string + "EVENTLOOP". This informs the demo viewer that the script is + still running and must be stopped by the user! + If an "EVENTLOOP" demo runs by itself, as with clock, which uses + ontimer, or minimal_hanoi, which loops by recursion, then the + code should catch the turtle.Terminator exception that will be + raised when the user presses the STOP button. (Paint is not such + a demo; it only acts in response to mouse clicks and movements.) diff --git a/Demo/turtle/tdemo_paint.py b/Demo/turtle/tdemo_paint.py --- a/Demo/turtle/tdemo_paint.py +++ b/Demo/turtle/tdemo_paint.py @@ -3,11 +3,15 @@ tdemo_paint.py -A simple eventdriven paint program +A simple event-driven paint program -- use left mouse button to move turtle -- middle mouse button to change color -- right mouse button do turn filling on/off +- left mouse button moves turtle +- middle mouse button changes color +- right mouse button toogles betweem pen up +(no line drawn when the turtle moves) and +pen down (line is drawn). If pen up follows +at least two pen-down moves, the polygon that +includes the starting point is filled. ------------------------------------------- Play around by clicking into the canvas using all three mouse buttons. diff --git a/Demo/turtle/tdemo_peace.py b/Demo/turtle/tdemo_peace.py --- a/Demo/turtle/tdemo_peace.py +++ b/Demo/turtle/tdemo_peace.py @@ -3,14 +3,10 @@ tdemo_peace.py -A very simple drawing suitable as a beginner's -programming example. - -Uses only commands, which are also available in -old turtle.py. - -Intentionally no variables are used except for the -colorloop: +A simple drawing suitable as a beginner's +programming example. Aside from the +peacecolors assignment and the for loop, +it only uses turtle commands. """ from turtle import * @@ -21,7 +17,7 @@ "royalblue1", "dodgerblue4") reset() - s = Screen() + Screen() up() goto(-320,-195) width(70) @@ -58,7 +54,7 @@ up() goto(0,300) # vanish if hideturtle() is not available ;-) - return "Done!!" + return "Done!" if __name__ == "__main__": main() diff --git a/Demo/turtle/tdemo_planet_and_moon.py b/Demo/turtle/tdemo_planet_and_moon.py --- a/Demo/turtle/tdemo_planet_and_moon.py +++ b/Demo/turtle/tdemo_planet_and_moon.py @@ -12,9 +12,9 @@ Planet has a circular orbit, moon a stable orbit around the planet. -You can hold the movement temporarily by pressing -the left mouse button with mouse over the -scrollbar of the canvas. +You can hold the movement temporarily by +pressing the left mouse button with the +mouse over the scrollbar of the canvas. """ from turtle import Shape, Turtle, mainloop, Vec2D as Vec @@ -108,6 +108,5 @@ return "Done!" if __name__ == '__main__': - msg = main() - print msg + main() mainloop() diff --git a/Demo/turtle/tdemo_tree.py b/Demo/turtle/tdemo_tree.py --- a/Demo/turtle/tdemo_tree.py +++ b/Demo/turtle/tdemo_tree.py @@ -11,9 +11,9 @@ (1) a tree-generator, where the drawing is quasi the side-effect, whereas the generator always yields None. -(2) Turtle-cloning: At each branching point the -current pen is cloned. So in the end there -are 1024 turtles. +(2) Turtle-cloning: At each branching point +the current pen is cloned. So in the end +there are 1024 turtles. """ from turtle import Turtle, mainloop from time import clock -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 04:22:19 2014 From: python-checkins at python.org (terry.reedy) Date: Wed, 25 Jun 2014 04:22:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE0MTE3?= =?utf-8?q?=3A_Inprove_help_text_and_docstrings=2C_some_for_clarity=2C_som?= =?utf-8?q?e_just_to?= Message-ID: <3gyp636lmTz7LkC@mail.python.org> http://hg.python.org/cpython/rev/713a774ca68a changeset: 91382:713a774ca68a branch: 3.4 parent: 91378:958e8bebda6d user: Terry Jan Reedy date: Tue Jun 24 22:21:41 2014 -0400 summary: Issue #14117: Inprove help text and docstrings, some for clarity, some just to fit in the default width of the text window (45 chars). files: Lib/turtledemo/demohelp.txt | 36 +++++++++++------- Lib/turtledemo/forest.py | 11 ++--- Lib/turtledemo/paint.py | 12 ++++-- Lib/turtledemo/peace.py | 16 +++----- Lib/turtledemo/planet_and_moon.py | 11 ++--- Lib/turtledemo/tree.py | 6 +- Lib/turtledemo/two_canvases.py | 16 ++++++-- 7 files changed, 60 insertions(+), 48 deletions(-) diff --git a/Lib/turtledemo/demohelp.txt b/Lib/turtledemo/demohelp.txt --- a/Lib/turtledemo/demohelp.txt +++ b/Lib/turtledemo/demohelp.txt @@ -53,22 +53,28 @@ (2) How to add your own demos to the demo repository - - place: same directory as turtledemo/__main__.py + - Place the file in the same directory as turtledemo/__main__.py - - requirements on source code: - code must contain a main() function which will - be executed by the viewer (see provided example scripts) - main() may return a string which will be displayed - in the Label below the source code window (when execution - has finished.) + - The code must contain a main() function which will + be executed by the viewer (see provided example scripts). + It may return a string which will be displayed in the Label below + the source code window (when execution has finished.) - If the demo is EVENT DRIVEN, main must return the string - "EVENTLOOP". This informs the demo viewer that the script is - still running and must be stopped by the user! + - In order to run mydemo.py by itself, such as during development, + add the following at the end of the file: - If an "EVENTLOOP" demo runs by itself, as with clock, which uses - ontimer, or minimal_hanoi, which loops by recursion, then the - code should catch the turtle.Terminator exception that will be - raised when the user presses the STOP button. (Paint is not such - a demo; it only acts in response to mouse clicks and movements.) + if __name__ == '__main__': + main() + mainloop() # keep window + python -m turtledemo.mydemo # will then run it + + - If the demo is EVENT DRIVEN, main must return the string + "EVENTLOOP". This informs the demo viewer that the script is + still running and must be stopped by the user! + + If an "EVENTLOOP" demo runs by itself, as with clock, which uses + ontimer, or minimal_hanoi, which loops by recursion, then the + code should catch the turtle.Terminator exception that will be + raised when the user presses the STOP button. (Paint is not such + a demo; it only acts in response to mouse clicks and movements.) diff --git a/Lib/turtledemo/forest.py b/Lib/turtledemo/forest.py --- a/Lib/turtledemo/forest.py +++ b/Lib/turtledemo/forest.py @@ -3,12 +3,12 @@ tdemo_forest.py -Displays a 'forest' of 3 'breadth-first-trees' -similar to the one from example tree. -For further remarks see xtx_tree.py +Displays a 'forest' of 3 breadth-first-trees +similar to the one in tree. +For further remarks see tree.py This example is a 'breadth-first'-rewrite of -a Logo program written by Erich Neuwirth. See: +a Logo program written by Erich Neuwirth. See http://homepage.univie.ac.at/erich.neuwirth/ """ from turtle import Turtle, colormode, tracer, mainloop @@ -104,6 +104,5 @@ return "runtime: %.2f sec." % (b-a) if __name__ == '__main__': - msg = main() - print(msg) + main() mainloop() diff --git a/Lib/turtledemo/paint.py b/Lib/turtledemo/paint.py --- a/Lib/turtledemo/paint.py +++ b/Lib/turtledemo/paint.py @@ -3,11 +3,15 @@ tdemo_paint.py -A simple eventdriven paint program +A simple event-driven paint program -- use left mouse button to move turtle -- middle mouse button to change color -- right mouse button do turn filling on/off +- left mouse button moves turtle +- middle mouse button changes color +- right mouse button toogles betweem pen up +(no line drawn when the turtle moves) and +pen down (line is drawn). If pen up follows +at least two pen-down moves, the polygon that +includes the starting point is filled. ------------------------------------------- Play around by clicking into the canvas using all three mouse buttons. diff --git a/Lib/turtledemo/peace.py b/Lib/turtledemo/peace.py --- a/Lib/turtledemo/peace.py +++ b/Lib/turtledemo/peace.py @@ -3,14 +3,10 @@ tdemo_peace.py -A very simple drawing suitable as a beginner's -programming example. - -Uses only commands, which are also available in -old turtle.py. - -Intentionally no variables are used except for the -colorloop: +A simple drawing suitable as a beginner's +programming example. Aside from the +peacecolors assignment and the for loop, +it only uses turtle commands. """ from turtle import * @@ -21,7 +17,7 @@ "royalblue1", "dodgerblue4") reset() - s = Screen() + Screen() up() goto(-320,-195) width(70) @@ -58,7 +54,7 @@ up() goto(0,300) # vanish if hideturtle() is not available ;-) - return "Done!!" + return "Done!" if __name__ == "__main__": main() diff --git a/Lib/turtledemo/planet_and_moon.py b/Lib/turtledemo/planet_and_moon.py --- a/Lib/turtledemo/planet_and_moon.py +++ b/Lib/turtledemo/planet_and_moon.py @@ -12,9 +12,9 @@ Planet has a circular orbit, moon a stable orbit around the planet. -You can hold the movement temporarily by pressing -the left mouse button with mouse over the -scrollbar of the canvas. +You can hold the movement temporarily by +pressing the left mouse button with the +mouse over the scrollbar of the canvas. """ from turtle import Shape, Turtle, mainloop, Vec2D as Vec @@ -108,6 +108,5 @@ return "Done!" if __name__ == '__main__': - msg = main() - print(msg) - #mainloop() + main() + mainloop() diff --git a/Lib/turtledemo/tree.py b/Lib/turtledemo/tree.py --- a/Lib/turtledemo/tree.py +++ b/Lib/turtledemo/tree.py @@ -11,9 +11,9 @@ (1) a tree-generator, where the drawing is quasi the side-effect, whereas the generator always yields None. -(2) Turtle-cloning: At each branching point the -current pen is cloned. So in the end there -are 1024 turtles. +(2) Turtle-cloning: At each branching point +the current pen is cloned. So in the end +there are 1024 turtles. """ from turtle import Turtle, mainloop from time import clock diff --git a/Lib/turtledemo/two_canvases.py b/Lib/turtledemo/two_canvases.py --- a/Lib/turtledemo/two_canvases.py +++ b/Lib/turtledemo/two_canvases.py @@ -1,8 +1,16 @@ -#!/usr/bin/env python3 -## DEMONSTRATES USE OF 2 CANVASES, SO CANNOT BE RUN IN DEMOVIEWER! -"""turtle example: Using TurtleScreen and RawTurtle -for drawing on two distinct canvases. +"""turtledemo.two_canvases + +Use TurtleScreen and RawTurtle to draw on two +distinct canvases. """ +#The final mainloop only serves to keep the window open. + +#TODO: This runs in its own two-canvas window when selected in the +#demoviewer examples menu but the text is not loaded and the previous +#example is left visible. If the ending mainloop is removed, the text +#Eis loaded, this run again in a third window, and if start is pressed, +#demoviewer raises an error because main is not found, and then freezes. + from turtle import TurtleScreen, RawTurtle, TK root = TK.Tk() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 04:22:21 2014 From: python-checkins at python.org (terry.reedy) Date: Wed, 25 Jun 2014 04:22:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gyp65217Jz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/37ff4f341de5 changeset: 91383:37ff4f341de5 parent: 91379:c0a7db165344 parent: 91382:713a774ca68a user: Terry Jan Reedy date: Tue Jun 24 22:21:56 2014 -0400 summary: Merge with 3.4 files: Lib/turtledemo/demohelp.txt | 36 +++++++++++------- Lib/turtledemo/forest.py | 11 ++--- Lib/turtledemo/paint.py | 12 ++++-- Lib/turtledemo/peace.py | 16 +++----- Lib/turtledemo/planet_and_moon.py | 11 ++--- Lib/turtledemo/tree.py | 6 +- Lib/turtledemo/two_canvases.py | 16 ++++++-- 7 files changed, 60 insertions(+), 48 deletions(-) diff --git a/Lib/turtledemo/demohelp.txt b/Lib/turtledemo/demohelp.txt --- a/Lib/turtledemo/demohelp.txt +++ b/Lib/turtledemo/demohelp.txt @@ -53,22 +53,28 @@ (2) How to add your own demos to the demo repository - - place: same directory as turtledemo/__main__.py + - Place the file in the same directory as turtledemo/__main__.py - - requirements on source code: - code must contain a main() function which will - be executed by the viewer (see provided example scripts) - main() may return a string which will be displayed - in the Label below the source code window (when execution - has finished.) + - The code must contain a main() function which will + be executed by the viewer (see provided example scripts). + It may return a string which will be displayed in the Label below + the source code window (when execution has finished.) - If the demo is EVENT DRIVEN, main must return the string - "EVENTLOOP". This informs the demo viewer that the script is - still running and must be stopped by the user! + - In order to run mydemo.py by itself, such as during development, + add the following at the end of the file: - If an "EVENTLOOP" demo runs by itself, as with clock, which uses - ontimer, or minimal_hanoi, which loops by recursion, then the - code should catch the turtle.Terminator exception that will be - raised when the user presses the STOP button. (Paint is not such - a demo; it only acts in response to mouse clicks and movements.) + if __name__ == '__main__': + main() + mainloop() # keep window + python -m turtledemo.mydemo # will then run it + + - If the demo is EVENT DRIVEN, main must return the string + "EVENTLOOP". This informs the demo viewer that the script is + still running and must be stopped by the user! + + If an "EVENTLOOP" demo runs by itself, as with clock, which uses + ontimer, or minimal_hanoi, which loops by recursion, then the + code should catch the turtle.Terminator exception that will be + raised when the user presses the STOP button. (Paint is not such + a demo; it only acts in response to mouse clicks and movements.) diff --git a/Lib/turtledemo/forest.py b/Lib/turtledemo/forest.py --- a/Lib/turtledemo/forest.py +++ b/Lib/turtledemo/forest.py @@ -3,12 +3,12 @@ tdemo_forest.py -Displays a 'forest' of 3 'breadth-first-trees' -similar to the one from example tree. -For further remarks see xtx_tree.py +Displays a 'forest' of 3 breadth-first-trees +similar to the one in tree. +For further remarks see tree.py This example is a 'breadth-first'-rewrite of -a Logo program written by Erich Neuwirth. See: +a Logo program written by Erich Neuwirth. See http://homepage.univie.ac.at/erich.neuwirth/ """ from turtle import Turtle, colormode, tracer, mainloop @@ -104,6 +104,5 @@ return "runtime: %.2f sec." % (b-a) if __name__ == '__main__': - msg = main() - print(msg) + main() mainloop() diff --git a/Lib/turtledemo/paint.py b/Lib/turtledemo/paint.py --- a/Lib/turtledemo/paint.py +++ b/Lib/turtledemo/paint.py @@ -3,11 +3,15 @@ tdemo_paint.py -A simple eventdriven paint program +A simple event-driven paint program -- use left mouse button to move turtle -- middle mouse button to change color -- right mouse button do turn filling on/off +- left mouse button moves turtle +- middle mouse button changes color +- right mouse button toogles betweem pen up +(no line drawn when the turtle moves) and +pen down (line is drawn). If pen up follows +at least two pen-down moves, the polygon that +includes the starting point is filled. ------------------------------------------- Play around by clicking into the canvas using all three mouse buttons. diff --git a/Lib/turtledemo/peace.py b/Lib/turtledemo/peace.py --- a/Lib/turtledemo/peace.py +++ b/Lib/turtledemo/peace.py @@ -3,14 +3,10 @@ tdemo_peace.py -A very simple drawing suitable as a beginner's -programming example. - -Uses only commands, which are also available in -old turtle.py. - -Intentionally no variables are used except for the -colorloop: +A simple drawing suitable as a beginner's +programming example. Aside from the +peacecolors assignment and the for loop, +it only uses turtle commands. """ from turtle import * @@ -21,7 +17,7 @@ "royalblue1", "dodgerblue4") reset() - s = Screen() + Screen() up() goto(-320,-195) width(70) @@ -58,7 +54,7 @@ up() goto(0,300) # vanish if hideturtle() is not available ;-) - return "Done!!" + return "Done!" if __name__ == "__main__": main() diff --git a/Lib/turtledemo/planet_and_moon.py b/Lib/turtledemo/planet_and_moon.py --- a/Lib/turtledemo/planet_and_moon.py +++ b/Lib/turtledemo/planet_and_moon.py @@ -12,9 +12,9 @@ Planet has a circular orbit, moon a stable orbit around the planet. -You can hold the movement temporarily by pressing -the left mouse button with mouse over the -scrollbar of the canvas. +You can hold the movement temporarily by +pressing the left mouse button with the +mouse over the scrollbar of the canvas. """ from turtle import Shape, Turtle, mainloop, Vec2D as Vec @@ -108,6 +108,5 @@ return "Done!" if __name__ == '__main__': - msg = main() - print(msg) - #mainloop() + main() + mainloop() diff --git a/Lib/turtledemo/tree.py b/Lib/turtledemo/tree.py --- a/Lib/turtledemo/tree.py +++ b/Lib/turtledemo/tree.py @@ -11,9 +11,9 @@ (1) a tree-generator, where the drawing is quasi the side-effect, whereas the generator always yields None. -(2) Turtle-cloning: At each branching point the -current pen is cloned. So in the end there -are 1024 turtles. +(2) Turtle-cloning: At each branching point +the current pen is cloned. So in the end +there are 1024 turtles. """ from turtle import Turtle, mainloop from time import clock diff --git a/Lib/turtledemo/two_canvases.py b/Lib/turtledemo/two_canvases.py --- a/Lib/turtledemo/two_canvases.py +++ b/Lib/turtledemo/two_canvases.py @@ -1,8 +1,16 @@ -#!/usr/bin/env python3 -## DEMONSTRATES USE OF 2 CANVASES, SO CANNOT BE RUN IN DEMOVIEWER! -"""turtle example: Using TurtleScreen and RawTurtle -for drawing on two distinct canvases. +"""turtledemo.two_canvases + +Use TurtleScreen and RawTurtle to draw on two +distinct canvases. """ +#The final mainloop only serves to keep the window open. + +#TODO: This runs in its own two-canvas window when selected in the +#demoviewer examples menu but the text is not loaded and the previous +#example is left visible. If the ending mainloop is removed, the text +#Eis loaded, this run again in a third window, and if start is pressed, +#demoviewer raises an error because main is not found, and then freezes. + from turtle import TurtleScreen, RawTurtle, TK root = TK.Tk() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 05:39:06 2014 From: python-checkins at python.org (jesus.cea) Date: Wed, 25 Jun 2014 05:39:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogQ2xvc2VzICMyMTQ0?= =?utf-8?q?1=3A_Reorder_elements_in_documentation_to_match_actual_order_in?= =?utf-8?q?_the?= Message-ID: <3gyqpf0y0Qz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/92d691c3ca00 changeset: 91384:92d691c3ca00 branch: 3.3 parent: 91237:7417d8854f93 user: Jesus Cea date: Wed Jun 25 05:37:17 2014 +0200 summary: Closes #21441: Reorder elements in documentation to match actual order in the code files: Doc/c-api/buffer.rst | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -89,6 +89,16 @@ .. c:type:: Py_buffer + .. c:member:: void \*buf + + A pointer to the start of the logical structure described by the buffer + fields. This can be any location within the underlying physical memory + block of the exporter. For example, with negative :c:member:`~Py_buffer.strides` + the value may point to the end of the memory block. + + For contiguous arrays, the value points to the beginning of the memory + block. + .. c:member:: void \*obj A new reference to the exporting object. The reference is owned by @@ -101,16 +111,6 @@ this field is *NULL*. In general, exporting objects MUST NOT use this scheme. - .. c:member:: void \*buf - - A pointer to the start of the logical structure described by the buffer - fields. This can be any location within the underlying physical memory - block of the exporter. For example, with negative :c:member:`~Py_buffer.strides` - the value may point to the end of the memory block. - - For contiguous arrays, the value points to the beginning of the memory - block. - .. c:member:: Py_ssize_t len ``product(shape) * itemsize``. For contiguous arrays, this is the length -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 05:39:07 2014 From: python-checkins at python.org (jesus.cea) Date: Wed, 25 Jun 2014 05:39:07 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy4zIC0+IDMuNCk6?= =?utf-8?q?_MERGE=3A_Closes_=2321441=3A_Reorder_elements_in_documentation_?= =?utf-8?q?to_match_actual_order?= Message-ID: <3gyqpg2gybz7Ljx@mail.python.org> http://hg.python.org/cpython/rev/d9da4b77624b changeset: 91385:d9da4b77624b branch: 3.4 parent: 91382:713a774ca68a parent: 91384:92d691c3ca00 user: Jesus Cea date: Wed Jun 25 05:38:06 2014 +0200 summary: MERGE: Closes #21441: Reorder elements in documentation to match actual order in the code files: Doc/c-api/buffer.rst | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -89,6 +89,16 @@ .. c:type:: Py_buffer + .. c:member:: void \*buf + + A pointer to the start of the logical structure described by the buffer + fields. This can be any location within the underlying physical memory + block of the exporter. For example, with negative :c:member:`~Py_buffer.strides` + the value may point to the end of the memory block. + + For contiguous arrays, the value points to the beginning of the memory + block. + .. c:member:: void \*obj A new reference to the exporting object. The reference is owned by @@ -101,16 +111,6 @@ this field is *NULL*. In general, exporting objects MUST NOT use this scheme. - .. c:member:: void \*buf - - A pointer to the start of the logical structure described by the buffer - fields. This can be any location within the underlying physical memory - block of the exporter. For example, with negative :c:member:`~Py_buffer.strides` - the value may point to the end of the memory block. - - For contiguous arrays, the value points to the beginning of the memory - block. - .. c:member:: Py_ssize_t len ``product(shape) * itemsize``. For contiguous arrays, this is the length -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 05:39:08 2014 From: python-checkins at python.org (jesus.cea) Date: Wed, 25 Jun 2014 05:39:08 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_MERGE=3A_Closes_=2321441=3A_Reorder_elements_in_document?= =?utf-8?q?ation_to_match_actual_order?= Message-ID: <3gyqph4Nfdz7LkV@mail.python.org> http://hg.python.org/cpython/rev/5e6c4070a785 changeset: 91386:5e6c4070a785 parent: 91383:37ff4f341de5 parent: 91385:d9da4b77624b user: Jesus Cea date: Wed Jun 25 05:38:40 2014 +0200 summary: MERGE: Closes #21441: Reorder elements in documentation to match actual order in the code files: Doc/c-api/buffer.rst | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -89,6 +89,16 @@ .. c:type:: Py_buffer + .. c:member:: void \*buf + + A pointer to the start of the logical structure described by the buffer + fields. This can be any location within the underlying physical memory + block of the exporter. For example, with negative :c:member:`~Py_buffer.strides` + the value may point to the end of the memory block. + + For contiguous arrays, the value points to the beginning of the memory + block. + .. c:member:: void \*obj A new reference to the exporting object. The reference is owned by @@ -101,16 +111,6 @@ this field is *NULL*. In general, exporting objects MUST NOT use this scheme. - .. c:member:: void \*buf - - A pointer to the start of the logical structure described by the buffer - fields. This can be any location within the underlying physical memory - block of the exporter. For example, with negative :c:member:`~Py_buffer.strides` - the value may point to the end of the memory block. - - For contiguous arrays, the value points to the beginning of the memory - block. - .. c:member:: Py_ssize_t len ``product(shape) * itemsize``. For contiguous arrays, this is the length -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 06:39:34 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 06:39:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE5MTQ1?= =?utf-8?q?=3A__Fix_handling_of_negative_values_for_a_=22times=22_keyword_?= =?utf-8?q?argument?= Message-ID: <3gys8Q5rQ1z7Lk5@mail.python.org> http://hg.python.org/cpython/rev/dce9dbc8e892 changeset: 91387:dce9dbc8e892 branch: 3.4 parent: 91385:d9da4b77624b user: Raymond Hettinger date: Tue Jun 24 21:36:58 2014 -0700 summary: Issue #19145: Fix handling of negative values for a "times" keyword argument to itertools.repeat()> (Patch contributed by Vajrasky Kok.) files: Lib/test/test_itertools.py | 13 +++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 4 ++++ Modules/itertoolsmodule.c | 7 +++++-- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -967,6 +967,12 @@ self.assertEqual(take(2, copy.deepcopy(c)), list('a' * 2)) self.pickletest(repeat(object='a', times=10)) + def test_repeat_with_negative_times(self): + self.assertEqual(repr(repeat('a', -1)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', -2)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', times=-1)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', times=-2)), "repeat('a', 0)") + def test_map(self): self.assertEqual(list(map(operator.pow, range(3), range(1,7))), [0**1, 1**2, 2**3]) @@ -1741,8 +1747,15 @@ def test_repeat(self): self.assertEqual(operator.length_hint(repeat(None, 50)), 50) + self.assertEqual(operator.length_hint(repeat(None, 0)), 0) self.assertEqual(operator.length_hint(repeat(None), 12), 12) + def test_repeat_with_negative_times(self): + self.assertEqual(operator.length_hint(repeat(None, -1)), 0) + self.assertEqual(operator.length_hint(repeat(None, -2)), 0) + self.assertEqual(operator.length_hint(repeat(None, times=-1)), 0) + self.assertEqual(operator.length_hint(repeat(None, times=-2)), 0) + class RegressionTests(unittest.TestCase): def test_sf_793826(self): diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1388,6 +1388,7 @@ Pauli Virtanen Frank Visser Johannes Vogel +Vajrasky Kok Alex Volkov Martijn Vries Sjoerd de Vries diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -31,6 +31,10 @@ - Issue #21832: Require named tuple inputs to be exact strings. +- Issue #19145: The times argument for itertools.repeat now handles + negative values the same way for keyword arguments as it does for + positional arguments. + - Issue #21812: turtle.shapetransform did not tranform the turtle on the first call. (Issue identified and fixed by Lita Cho.) diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -4109,14 +4109,17 @@ { repeatobject *ro; PyObject *element; - Py_ssize_t cnt = -1; + Py_ssize_t cnt = -1, n_kwds = 0; static char *kwargs[] = {"object", "times", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|n:repeat", kwargs, &element, &cnt)) return NULL; - if (PyTuple_Size(args) == 2 && cnt < 0) + if (kwds != NULL) + n_kwds = PyDict_Size(kwds); + /* Does user supply times argument? */ + if ((PyTuple_Size(args) + n_kwds == 2) && cnt < 0) cnt = 0; ro = (repeatobject *)type->tp_alloc(type, 0); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 06:39:36 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 06:39:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gys8S0XBvz7Lk3@mail.python.org> http://hg.python.org/cpython/rev/d002c4d60ed4 changeset: 91388:d002c4d60ed4 parent: 91386:5e6c4070a785 parent: 91387:dce9dbc8e892 user: Raymond Hettinger date: Tue Jun 24 21:39:27 2014 -0700 summary: merge files: Lib/test/test_itertools.py | 13 +++++++++++++ Misc/ACKS | 1 + Modules/itertoolsmodule.c | 7 +++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -967,6 +967,12 @@ self.assertEqual(take(2, copy.deepcopy(c)), list('a' * 2)) self.pickletest(repeat(object='a', times=10)) + def test_repeat_with_negative_times(self): + self.assertEqual(repr(repeat('a', -1)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', -2)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', times=-1)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', times=-2)), "repeat('a', 0)") + def test_map(self): self.assertEqual(list(map(operator.pow, range(3), range(1,7))), [0**1, 1**2, 2**3]) @@ -1741,8 +1747,15 @@ def test_repeat(self): self.assertEqual(operator.length_hint(repeat(None, 50)), 50) + self.assertEqual(operator.length_hint(repeat(None, 0)), 0) self.assertEqual(operator.length_hint(repeat(None), 12), 12) + def test_repeat_with_negative_times(self): + self.assertEqual(operator.length_hint(repeat(None, -1)), 0) + self.assertEqual(operator.length_hint(repeat(None, -2)), 0) + self.assertEqual(operator.length_hint(repeat(None, times=-1)), 0) + self.assertEqual(operator.length_hint(repeat(None, times=-2)), 0) + class RegressionTests(unittest.TestCase): def test_sf_793826(self): diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1399,6 +1399,7 @@ Pauli Virtanen Frank Visser Johannes Vogel +Vajrasky Kok Alex Volkov Martijn Vries Sjoerd de Vries diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -4109,14 +4109,17 @@ { repeatobject *ro; PyObject *element; - Py_ssize_t cnt = -1; + Py_ssize_t cnt = -1, n_kwds = 0; static char *kwargs[] = {"object", "times", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|n:repeat", kwargs, &element, &cnt)) return NULL; - if (PyTuple_Size(args) == 2 && cnt < 0) + if (kwds != NULL) + n_kwds = PyDict_Size(kwds); + /* Does user supply times argument? */ + if ((PyTuple_Size(args) + n_kwds == 2) && cnt < 0) cnt = 0; ro = (repeatobject *)type->tp_alloc(type, 0); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 06:53:53 2014 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 25 Jun 2014 06:53:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5MTQ1?= =?utf-8?q?=3A__Fix_handling_of_negative_values_for_a_=22times=22_keyword_?= =?utf-8?q?argument?= Message-ID: <3gysSx5gKMz7LjW@mail.python.org> http://hg.python.org/cpython/rev/85dc4684c83e changeset: 91389:85dc4684c83e branch: 2.7 parent: 91381:129ec3d90a67 user: Raymond Hettinger date: Tue Jun 24 21:53:45 2014 -0700 summary: Issue #19145: Fix handling of negative values for a "times" keyword argument to itertools.repeat()> (Patch contributed by Vajrasky Kok.) files: Lib/test/test_itertools.py | 9 +++++++++ Misc/ACKS | 1 + Misc/NEWS | 4 ++++ Modules/itertoolsmodule.c | 7 +++++-- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -698,6 +698,9 @@ def test_repeat(self): self.assertEqual(list(repeat(object='a', times=3)), ['a', 'a', 'a']) + self.assertEqual(list(repeat(object='a', times=0)), []) + self.assertEqual(list(repeat(object='a', times=-1)), []) + self.assertEqual(list(repeat(object='a', times=-2)), []) self.assertEqual(zip(xrange(3),repeat('a')), [(0, 'a'), (1, 'a'), (2, 'a')]) self.assertEqual(list(repeat('a', 3)), ['a', 'a', 'a']) @@ -714,6 +717,12 @@ list(r) self.assertEqual(repr(r), 'repeat((1+0j), 0)') + def test_repeat_with_negative_times(self): + self.assertEqual(repr(repeat('a', -1)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', -2)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', times=-1)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', times=-2)), "repeat('a', 0)") + def test_imap(self): self.assertEqual(list(imap(operator.pow, range(3), range(1,7))), [0**1, 1**2, 2**3]) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1384,6 +1384,7 @@ Pauli Virtanen Frank Visser Johannes Vogel +Vajrasky Kok Alex Volkov Guido Vranken Martijn Vries diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -34,6 +34,10 @@ - Issue #21672: Fix the behavior of ntpath.join on UNC-style paths. +- Issue #19145: The times argument for itertools.repeat now handles + negative values the same way for keyword arguments as it does for + positional arguments. + - Issue #21832: Require named tuple inputs to be exact strings. - Issue #8343: Named group error messages in the re module did not show diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -3683,14 +3683,17 @@ { repeatobject *ro; PyObject *element; - Py_ssize_t cnt = -1; + Py_ssize_t cnt = -1, n_kwds = 0; static char *kwargs[] = {"object", "times", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|n:repeat", kwargs, &element, &cnt)) return NULL; - if (PyTuple_Size(args) == 2 && cnt < 0) + if (kwds != NULL) + n_kwds = PyDict_Size(kwds); + /* Does user supply times argument? */ + if ((PyTuple_Size(args) + n_kwds == 2) && cnt < 0) cnt = 0; ro = (repeatobject *)type->tp_alloc(type, 0); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 08:31:38 2014 From: python-checkins at python.org (vinay.sajip) Date: Wed, 25 Jun 2014 08:31:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Fixed_typo_in_?= =?utf-8?q?documentation=2E?= Message-ID: <3gyvdk0GY9z7Ljk@mail.python.org> http://hg.python.org/cpython/rev/a353a1f1368a changeset: 91390:a353a1f1368a branch: 2.7 user: Vinay Sajip date: Wed Jun 25 07:29:19 2014 +0100 summary: Fixed typo in documentation. files: Doc/howto/logging.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -996,7 +996,7 @@ so that if the logger's threshold is set above ``DEBUG``, the calls to :func:`expensive_func1` and :func:`expensive_func2` are never made. -.. note:: In some cases, :meth:`~Logger.isEnabledFor` can iself be more +.. note:: In some cases, :meth:`~Logger.isEnabledFor` can itself be more expensive than you'd like (e.g. for deeply nested loggers where an explicit level is only set high up in the logger hierarchy). In such cases (or if you want to avoid calling a method in tight loops), you can cache the result of a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 08:31:39 2014 From: python-checkins at python.org (vinay.sajip) Date: Wed, 25 Jun 2014 08:31:39 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fixed_typo_in_?= =?utf-8?q?documentation=2E?= Message-ID: <3gyvdl25xZz7LkF@mail.python.org> http://hg.python.org/cpython/rev/77fbbb5a7306 changeset: 91391:77fbbb5a7306 branch: 3.4 parent: 91387:dce9dbc8e892 user: Vinay Sajip date: Wed Jun 25 07:30:46 2014 +0100 summary: Fixed typo in documentation. files: Doc/howto/logging.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -1027,7 +1027,7 @@ so that if the logger's threshold is set above ``DEBUG``, the calls to :func:`expensive_func1` and :func:`expensive_func2` are never made. -.. note:: In some cases, :meth:`~Logger.isEnabledFor` can iself be more +.. note:: In some cases, :meth:`~Logger.isEnabledFor` can itself be more expensive than you'd like (e.g. for deeply nested loggers where an explicit level is only set high up in the logger hierarchy). In such cases (or if you want to avoid calling a method in tight loops), you can cache the result of a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 08:31:40 2014 From: python-checkins at python.org (vinay.sajip) Date: Wed, 25 Jun 2014 08:31:40 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merged_documentation_update_from_3=2E4=2E?= Message-ID: <3gyvdm3nwQz7Lkf@mail.python.org> http://hg.python.org/cpython/rev/1df113f07de1 changeset: 91392:1df113f07de1 parent: 91388:d002c4d60ed4 parent: 91391:77fbbb5a7306 user: Vinay Sajip date: Wed Jun 25 07:31:21 2014 +0100 summary: Merged documentation update from 3.4. files: Doc/howto/logging.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -1027,7 +1027,7 @@ so that if the logger's threshold is set above ``DEBUG``, the calls to :func:`expensive_func1` and :func:`expensive_func2` are never made. -.. note:: In some cases, :meth:`~Logger.isEnabledFor` can iself be more +.. note:: In some cases, :meth:`~Logger.isEnabledFor` can itself be more expensive than you'd like (e.g. for deeply nested loggers where an explicit level is only set high up in the logger hierarchy). In such cases (or if you want to avoid calling a method in tight loops), you can cache the result of a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 10:13:28 2014 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 25 Jun 2014 10:13:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Correct_the_qu?= =?utf-8?q?opri_module_documentation=2E_Mention_the_correct_types_of_the?= Message-ID: <3gyxvD29Xwz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/606a18938476 changeset: 91393:606a18938476 branch: 3.4 parent: 91391:77fbbb5a7306 user: Senthil Kumaran date: Wed Jun 25 01:12:03 2014 -0700 summary: Correct the quopri module documentation. Mention the correct types of the parameters on encodestring and decodestring. Patch by Petri Lehtinen. files: Doc/library/quopri.rst | 32 +++++++++++++++--------------- Lib/quopri.py | 14 +++++------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Doc/library/quopri.rst b/Doc/library/quopri.rst --- a/Doc/library/quopri.rst +++ b/Doc/library/quopri.rst @@ -24,9 +24,8 @@ .. function:: decode(input, output, header=False) Decode the contents of the *input* file and write the resulting decoded binary - data to the *output* file. *input* and *output* must be :term:`file objects - `. *input* will be read until ``input.readline()`` returns an - empty string. If the optional argument *header* is present and true, underscore + data to the *output* file. *input* and *output* must be :term:`binary file objects + `. If the optional argument *header* is present and true, underscore will be decoded as space. This is used to decode "Q"-encoded headers as described in :rfc:`1522`: "MIME (Multipurpose Internet Mail Extensions) Part Two: Message Header Extensions for Non-ASCII Text". @@ -34,27 +33,28 @@ .. function:: encode(input, output, quotetabs, header=False) - Encode the contents of the *input* file and write the resulting quoted-printable - data to the *output* file. *input* and *output* must be :term:`file objects - `. *input* will be read until ``input.readline()`` returns an - empty string. *quotetabs* is a flag which controls whether to encode embedded - spaces and tabs; when true it encodes such embedded whitespace, and when - false it leaves them unencoded. Note that spaces and tabs appearing at the - end of lines are always encoded, as per :rfc:`1521`. *header* is a flag - which controls if spaces are encoded as underscores as per :rfc:`1522`. + Encode the contents of the *input* file and write the resulting quoted- + printable data to the *output* file. *input* and *output* must be + :term:`binary file objects `. *quotetabs*, a flag which controls + whether to encode embedded spaces and tabs must be provideda and when true it + encodes such embedded whitespace, and when false it leaves them unencoded. + Note that spaces and tabs appearing at the end of lines are always encoded, + as per :rfc:`1521`. *header* is a flag which controls if spaces are encoded + as underscores as per :rfc:`1522`. .. function:: decodestring(s, header=False) - Like :func:`decode`, except that it accepts a source string and returns the - corresponding decoded string. + Like :func:`decode`, except that it accepts a source :class:`bytes` and + returns the corresponding decoded :class:`bytes`. .. function:: encodestring(s, quotetabs=False, header=False) - Like :func:`encode`, except that it accepts a source string and returns the - corresponding encoded string. *quotetabs* and *header* are optional - (defaulting to ``False``), and are passed straight through to :func:`encode`. + Like :func:`encode`, except that it accepts a source :class:`bytes` and + returns the corresponding encoded :class:`bytes`. By default, it sends a + False value to *quotetabs* parameter of the :func:`encode` function. + .. seealso:: diff --git a/Lib/quopri.py b/Lib/quopri.py --- a/Lib/quopri.py +++ b/Lib/quopri.py @@ -44,13 +44,11 @@ def encode(input, output, quotetabs, header=False): """Read 'input', apply quoted-printable encoding, and write to 'output'. - 'input' and 'output' are files with readline() and write() methods. - The 'quotetabs' flag indicates whether embedded tabs and spaces should be - quoted. Note that line-ending tabs and spaces are always encoded, as per - RFC 1521. - The 'header' flag indicates whether we are encoding spaces as _ as per - RFC 1522. - """ + 'input' and 'output' are binary file objects. The 'quotetabs' flag + indicates whether embedded tabs and spaces should be quoted. Note that + line-ending tabs and spaces are always encoded, as per RFC 1521. + The 'header' flag indicates whether we are encoding spaces as _ as per RFC + 1522.""" if b2a_qp is not None: data = input.read() @@ -118,7 +116,7 @@ def decode(input, output, header=False): """Read 'input', apply quoted-printable decoding, and write to 'output'. - 'input' and 'output' are files with readline() and write() methods. + 'input' and 'output' are binary file objects. If 'header' is true, decode underscore as space (per RFC 1522).""" if a2b_qp is not None: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 10:13:29 2014 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 25 Jun 2014 10:13:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E4?= Message-ID: <3gyxvF4ww5z7LkN@mail.python.org> http://hg.python.org/cpython/rev/b4130b2f7748 changeset: 91394:b4130b2f7748 parent: 91392:1df113f07de1 parent: 91393:606a18938476 user: Senthil Kumaran date: Wed Jun 25 01:13:19 2014 -0700 summary: merge from 3.4 issue15588 - Correct the quopri module documentation. Mention the correct types of the parameters on encodestring and decodestring. Patch by Petri Lehtinen. files: Doc/library/quopri.rst | 32 +++++++++++++++--------------- Lib/quopri.py | 14 +++++------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Doc/library/quopri.rst b/Doc/library/quopri.rst --- a/Doc/library/quopri.rst +++ b/Doc/library/quopri.rst @@ -24,9 +24,8 @@ .. function:: decode(input, output, header=False) Decode the contents of the *input* file and write the resulting decoded binary - data to the *output* file. *input* and *output* must be :term:`file objects - `. *input* will be read until ``input.readline()`` returns an - empty string. If the optional argument *header* is present and true, underscore + data to the *output* file. *input* and *output* must be :term:`binary file objects + `. If the optional argument *header* is present and true, underscore will be decoded as space. This is used to decode "Q"-encoded headers as described in :rfc:`1522`: "MIME (Multipurpose Internet Mail Extensions) Part Two: Message Header Extensions for Non-ASCII Text". @@ -34,27 +33,28 @@ .. function:: encode(input, output, quotetabs, header=False) - Encode the contents of the *input* file and write the resulting quoted-printable - data to the *output* file. *input* and *output* must be :term:`file objects - `. *input* will be read until ``input.readline()`` returns an - empty string. *quotetabs* is a flag which controls whether to encode embedded - spaces and tabs; when true it encodes such embedded whitespace, and when - false it leaves them unencoded. Note that spaces and tabs appearing at the - end of lines are always encoded, as per :rfc:`1521`. *header* is a flag - which controls if spaces are encoded as underscores as per :rfc:`1522`. + Encode the contents of the *input* file and write the resulting quoted- + printable data to the *output* file. *input* and *output* must be + :term:`binary file objects `. *quotetabs*, a flag which controls + whether to encode embedded spaces and tabs must be provideda and when true it + encodes such embedded whitespace, and when false it leaves them unencoded. + Note that spaces and tabs appearing at the end of lines are always encoded, + as per :rfc:`1521`. *header* is a flag which controls if spaces are encoded + as underscores as per :rfc:`1522`. .. function:: decodestring(s, header=False) - Like :func:`decode`, except that it accepts a source string and returns the - corresponding decoded string. + Like :func:`decode`, except that it accepts a source :class:`bytes` and + returns the corresponding decoded :class:`bytes`. .. function:: encodestring(s, quotetabs=False, header=False) - Like :func:`encode`, except that it accepts a source string and returns the - corresponding encoded string. *quotetabs* and *header* are optional - (defaulting to ``False``), and are passed straight through to :func:`encode`. + Like :func:`encode`, except that it accepts a source :class:`bytes` and + returns the corresponding encoded :class:`bytes`. By default, it sends a + False value to *quotetabs* parameter of the :func:`encode` function. + .. seealso:: diff --git a/Lib/quopri.py b/Lib/quopri.py --- a/Lib/quopri.py +++ b/Lib/quopri.py @@ -44,13 +44,11 @@ def encode(input, output, quotetabs, header=False): """Read 'input', apply quoted-printable encoding, and write to 'output'. - 'input' and 'output' are files with readline() and write() methods. - The 'quotetabs' flag indicates whether embedded tabs and spaces should be - quoted. Note that line-ending tabs and spaces are always encoded, as per - RFC 1521. - The 'header' flag indicates whether we are encoding spaces as _ as per - RFC 1522. - """ + 'input' and 'output' are binary file objects. The 'quotetabs' flag + indicates whether embedded tabs and spaces should be quoted. Note that + line-ending tabs and spaces are always encoded, as per RFC 1521. + The 'header' flag indicates whether we are encoding spaces as _ as per RFC + 1522.""" if b2a_qp is not None: data = input.read() @@ -118,7 +116,7 @@ def decode(input, output, header=False): """Read 'input', apply quoted-printable decoding, and write to 'output'. - 'input' and 'output' are files with readline() and write() methods. + 'input' and 'output' are binary file objects. If 'header' is true, decode underscore as space (per RFC 1522).""" if a2b_qp is not None: -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Wed Jun 25 10:45:11 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 25 Jun 2014 10:45:11 +0200 Subject: [Python-checkins] Daily reference leaks (c0a7db165344): sum=13 Message-ID: results for c0a7db165344 on branch "default" -------------------------------------------- test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 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/reflogwh85sD', '-x'] From python-checkins at python.org Wed Jun 25 11:59:04 2014 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 25 Jun 2014 11:59:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_issue20753_-_r?= =?utf-8?q?obotparser_tests_should_not_rely_upon_external_resource_when_no?= =?utf-8?q?t?= Message-ID: <3gz0F44t3Qz7LkF@mail.python.org> http://hg.python.org/cpython/rev/16d8240ff841 changeset: 91395:16d8240ff841 branch: 3.4 parent: 91393:606a18938476 user: Senthil Kumaran date: Wed Jun 25 02:58:15 2014 -0700 summary: issue20753 - robotparser tests should not rely upon external resource when not required. Specifically, it was relying a URL which gave HTTP 403 and used it to assert it's methods, this changes undoes that and provides a local http server with similar properties. Patch contributed by Vajrasky Kok. files: Lib/test/test_robotparser.py | 72 +++++++++++++++-------- 1 files changed, 47 insertions(+), 25 deletions(-) diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -4,6 +4,9 @@ from urllib.error import URLError, HTTPError from urllib.request import urlopen from test import support +import threading +from http.server import BaseHTTPRequestHandler, HTTPServer + class RobotTestCase(unittest.TestCase): def __init__(self, index=None, parser=None, url=None, good=None, agent=None): @@ -247,33 +250,51 @@ RobotTest(16, doc, good, bad) -class NetworkTestCase(unittest.TestCase): +class RobotHandler(BaseHTTPRequestHandler): + + def do_GET(self): + self.send_error(403, "Forbidden access") + + def log_message(self, format, *args): + pass + + +class PasswordProtectedSiteTestCase(unittest.TestCase): + + def setUp(self): + self.server = HTTPServer((support.HOST, 0), RobotHandler) + + self.t = threading.Thread( + name='HTTPServer serving', + target=self.server.serve_forever, + # Short poll interval to make the test finish quickly. + # Time between requests is short enough that we won't wake + # up spuriously too many times. + kwargs={'poll_interval':0.01}) + self.t.daemon = True # In case this function raises. + self.t.start() + + def tearDown(self): + self.server.shutdown() + self.t.join() + self.server.server_close() + + def runTest(self): + self.testPasswordProtectedSite() def testPasswordProtectedSite(self): - support.requires('network') - with support.transient_internet('mueblesmoraleda.com'): - url = 'http://mueblesmoraleda.com' - robots_url = url + "/robots.txt" - # First check the URL is usable for our purposes, since the - # test site is a bit flaky. - try: - urlopen(robots_url) - except HTTPError as e: - if e.code not in {401, 403}: - self.skipTest( - "%r should return a 401 or 403 HTTP error, not %r" - % (robots_url, e.code)) - else: - self.skipTest( - "%r should return a 401 or 403 HTTP error, not succeed" - % (robots_url)) - parser = urllib.robotparser.RobotFileParser() - parser.set_url(url) - try: - parser.read() - except URLError: - self.skipTest('%s is unavailable' % url) - self.assertEqual(parser.can_fetch("*", robots_url), False) + addr = self.server.server_address + url = 'http://' + support.HOST + ':' + str(addr[1]) + robots_url = url + "/robots.txt" + parser = urllib.robotparser.RobotFileParser() + parser.set_url(url) + parser.read() + self.assertFalse(parser.can_fetch("*", robots_url)) + + def __str__(self): + return '%s' % self.__class__.__name__ + +class NetworkTestCase(unittest.TestCase): @unittest.skip('does not handle the gzip encoding delivered by pydotorg') def testPythonOrg(self): @@ -288,6 +309,7 @@ def load_tests(loader, suite, pattern): suite = unittest.makeSuite(NetworkTestCase) suite.addTest(tests) + suite.addTest(PasswordProtectedSiteTestCase()) return suite if __name__=='__main__': -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 11:59:06 2014 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 25 Jun 2014 11:59:06 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge_from_3=2E4?= Message-ID: <3gz0F604JDz7Lkq@mail.python.org> http://hg.python.org/cpython/rev/74cd8abcc302 changeset: 91396:74cd8abcc302 parent: 91394:b4130b2f7748 parent: 91395:16d8240ff841 user: Senthil Kumaran date: Wed Jun 25 02:58:53 2014 -0700 summary: merge from 3.4 issue20753 - robotparser tests should not rely upon external resource when not required. Specifically, it was relying a URL which gave HTTP 403 and used it to assert it's methods, this changes undoes that and provides a local http server with similar properties. Patch contributed by Vajrasky Kok. files: Lib/test/test_robotparser.py | 72 +++++++++++++++-------- 1 files changed, 47 insertions(+), 25 deletions(-) diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -4,6 +4,9 @@ from urllib.error import URLError, HTTPError from urllib.request import urlopen from test import support +import threading +from http.server import BaseHTTPRequestHandler, HTTPServer + class RobotTestCase(unittest.TestCase): def __init__(self, index=None, parser=None, url=None, good=None, agent=None): @@ -247,33 +250,51 @@ RobotTest(16, doc, good, bad) -class NetworkTestCase(unittest.TestCase): +class RobotHandler(BaseHTTPRequestHandler): + + def do_GET(self): + self.send_error(403, "Forbidden access") + + def log_message(self, format, *args): + pass + + +class PasswordProtectedSiteTestCase(unittest.TestCase): + + def setUp(self): + self.server = HTTPServer((support.HOST, 0), RobotHandler) + + self.t = threading.Thread( + name='HTTPServer serving', + target=self.server.serve_forever, + # Short poll interval to make the test finish quickly. + # Time between requests is short enough that we won't wake + # up spuriously too many times. + kwargs={'poll_interval':0.01}) + self.t.daemon = True # In case this function raises. + self.t.start() + + def tearDown(self): + self.server.shutdown() + self.t.join() + self.server.server_close() + + def runTest(self): + self.testPasswordProtectedSite() def testPasswordProtectedSite(self): - support.requires('network') - with support.transient_internet('mueblesmoraleda.com'): - url = 'http://mueblesmoraleda.com' - robots_url = url + "/robots.txt" - # First check the URL is usable for our purposes, since the - # test site is a bit flaky. - try: - urlopen(robots_url) - except HTTPError as e: - if e.code not in {401, 403}: - self.skipTest( - "%r should return a 401 or 403 HTTP error, not %r" - % (robots_url, e.code)) - else: - self.skipTest( - "%r should return a 401 or 403 HTTP error, not succeed" - % (robots_url)) - parser = urllib.robotparser.RobotFileParser() - parser.set_url(url) - try: - parser.read() - except URLError: - self.skipTest('%s is unavailable' % url) - self.assertEqual(parser.can_fetch("*", robots_url), False) + addr = self.server.server_address + url = 'http://' + support.HOST + ':' + str(addr[1]) + robots_url = url + "/robots.txt" + parser = urllib.robotparser.RobotFileParser() + parser.set_url(url) + parser.read() + self.assertFalse(parser.can_fetch("*", robots_url)) + + def __str__(self): + return '%s' % self.__class__.__name__ + +class NetworkTestCase(unittest.TestCase): @unittest.skip('does not handle the gzip encoding delivered by pydotorg') def testPythonOrg(self): @@ -288,6 +309,7 @@ def load_tests(loader, suite, pattern): suite = unittest.makeSuite(NetworkTestCase) suite.addTest(tests) + suite.addTest(PasswordProtectedSiteTestCase()) return suite if __name__=='__main__': -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 13:06:52 2014 From: python-checkins at python.org (jesus.cea) Date: Wed, 25 Jun 2014 13:06:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogQ2xvc2VzICMyMDg3?= =?utf-8?q?2=3A_dbm/gdbm/ndbm_close_methods_are_not_documented?= Message-ID: <3gz1lJ07Jnz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/de44bc26a00a changeset: 91397:de44bc26a00a branch: 2.7 parent: 91390:a353a1f1368a user: Jesus Cea date: Wed Jun 25 12:55:48 2014 +0200 summary: Closes #20872: dbm/gdbm/ndbm close methods are not documented files: Doc/library/anydbm.rst | 8 ++++++++ Doc/library/dbm.rst | 8 ++++++++ Doc/library/dumbdbm.rst | 8 ++++++++ Doc/library/gdbm.rst | 5 +++++ 4 files changed, 29 insertions(+), 0 deletions(-) diff --git a/Doc/library/anydbm.rst b/Doc/library/anydbm.rst --- a/Doc/library/anydbm.rst +++ b/Doc/library/anydbm.rst @@ -92,6 +92,14 @@ db.close() +In addition to the dictionary-like methods, ``anydbm`` objects +provide the following method: + +.. function:: close() + + Close the ``anydbm`` database. + + .. seealso:: Module :mod:`dbhash` diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -64,6 +64,14 @@ database has to be created. It defaults to octal ``0666`` (and will be modified by the prevailing umask). + In addition to the dictionary-like methods, ``dbm`` objects + provide the following method: + + + .. function:: close() + + Close the ``dbm`` database. + .. seealso:: diff --git a/Doc/library/dumbdbm.rst b/Doc/library/dumbdbm.rst --- a/Doc/library/dumbdbm.rst +++ b/Doc/library/dumbdbm.rst @@ -49,6 +49,14 @@ .. versionchanged:: 2.2 The *mode* argument was ignored in earlier versions. +In addition to the dictionary-like methods, ``dumbdm`` objects +provide the following method: + + +.. function:: close() + + Close the ``dumbdm`` database. + .. seealso:: diff --git a/Doc/library/gdbm.rst b/Doc/library/gdbm.rst --- a/Doc/library/gdbm.rst +++ b/Doc/library/gdbm.rst @@ -116,6 +116,11 @@ unwritten data to be written to the disk. +.. function:: close() + + Close the ``gdbm`` database. + + .. seealso:: Module :mod:`anydbm` -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 13:06:53 2014 From: python-checkins at python.org (jesus.cea) Date: Wed, 25 Jun 2014 13:06:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogQ2xvc2VzICMyMDg3?= =?utf-8?q?2=3A_dbm/gdbm/ndbm_close_methods_are_not_documented?= Message-ID: <3gz1lK1lYkz7Lk0@mail.python.org> http://hg.python.org/cpython/rev/cf156cfb12e7 changeset: 91398:cf156cfb12e7 branch: 3.3 parent: 91384:92d691c3ca00 user: Jesus Cea date: Wed Jun 25 13:05:31 2014 +0200 summary: Closes #20872: dbm/gdbm/ndbm close methods are not documented files: Doc/library/dbm.rst | 18 ++++++++++++++++-- 1 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -216,6 +216,9 @@ When the database has been opened in fast mode, this method forces any unwritten data to be written to the disk. + .. method:: gdbm.close() + + Close the ``gdbm`` database. :mod:`dbm.ndbm` --- Interface based on ndbm ------------------------------------------- @@ -247,7 +250,7 @@ .. function:: open(filename[, flag[, mode]]) - Open a dbm database and return a ``dbm`` object. The *filename* argument is the + Open a dbm database and return a ``ndbm`` object. The *filename* argument is the name of the database file (without the :file:`.dir` or :file:`.pag` extensions). The optional *flag* argument must be one of these values: @@ -272,6 +275,12 @@ database has to be created. It defaults to octal ``0o666`` (and will be modified by the prevailing umask). + In addition to the dictionary-like methods, ``ndbm`` objects + provide the following method: + + .. method:: ndbm.close() + + Close the ``ndbm`` database. :mod:`dbm.dumb` --- Portable DBM implementation @@ -319,9 +328,14 @@ In addition to the methods provided by the :class:`collections.abc.MutableMapping` class, :class:`dumbdbm` objects - provide the following method: + provide the following methods: .. method:: dumbdbm.sync() Synchronize the on-disk directory and data files. This method is called by the :meth:`Shelve.sync` method. + + .. method:: dumbdbm.close() + + Close the ``dumbdbm`` database. + -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 13:06:54 2014 From: python-checkins at python.org (jesus.cea) Date: Wed, 25 Jun 2014 13:06:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy4zIC0+IDMuNCk6?= =?utf-8?q?_MERGE=3A_Closes_=2320872=3A_dbm/gdbm/ndbm_close_methods_are_no?= =?utf-8?q?t_documented?= Message-ID: <3gz1lL35C9z7LkM@mail.python.org> http://hg.python.org/cpython/rev/88fb216b99fb changeset: 91399:88fb216b99fb branch: 3.4 parent: 91395:16d8240ff841 parent: 91398:cf156cfb12e7 user: Jesus Cea date: Wed Jun 25 13:06:07 2014 +0200 summary: MERGE: Closes #20872: dbm/gdbm/ndbm close methods are not documented files: Doc/library/dbm.rst | 18 ++++++++++++++++-- 1 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -222,6 +222,9 @@ When the database has been opened in fast mode, this method forces any unwritten data to be written to the disk. + .. method:: gdbm.close() + + Close the ``gdbm`` database. :mod:`dbm.ndbm` --- Interface based on ndbm ------------------------------------------- @@ -253,7 +256,7 @@ .. function:: open(filename[, flag[, mode]]) - Open a dbm database and return a ``dbm`` object. The *filename* argument is the + Open a dbm database and return a ``ndbm`` object. The *filename* argument is the name of the database file (without the :file:`.dir` or :file:`.pag` extensions). The optional *flag* argument must be one of these values: @@ -278,6 +281,12 @@ database has to be created. It defaults to octal ``0o666`` (and will be modified by the prevailing umask). + In addition to the dictionary-like methods, ``ndbm`` objects + provide the following method: + + .. method:: ndbm.close() + + Close the ``ndbm`` database. :mod:`dbm.dumb` --- Portable DBM implementation @@ -325,9 +334,14 @@ In addition to the methods provided by the :class:`collections.abc.MutableMapping` class, :class:`dumbdbm` objects - provide the following method: + provide the following methods: .. method:: dumbdbm.sync() Synchronize the on-disk directory and data files. This method is called by the :meth:`Shelve.sync` method. + + .. method:: dumbdbm.close() + + Close the ``dumbdbm`` database. + -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 13:06:55 2014 From: python-checkins at python.org (jesus.cea) Date: Wed, 25 Jun 2014 13:06:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_MERGE=3A_Closes_=2320872=3A_dbm/gdbm/ndbm_close_methods_?= =?utf-8?q?are_not_documented?= Message-ID: <3gz1lM4m94z7LlG@mail.python.org> http://hg.python.org/cpython/rev/90dd9eec1230 changeset: 91400:90dd9eec1230 parent: 91396:74cd8abcc302 parent: 91399:88fb216b99fb user: Jesus Cea date: Wed Jun 25 13:06:32 2014 +0200 summary: MERGE: Closes #20872: dbm/gdbm/ndbm close methods are not documented files: Doc/library/dbm.rst | 18 ++++++++++++++++-- 1 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -222,6 +222,9 @@ When the database has been opened in fast mode, this method forces any unwritten data to be written to the disk. + .. method:: gdbm.close() + + Close the ``gdbm`` database. :mod:`dbm.ndbm` --- Interface based on ndbm ------------------------------------------- @@ -253,7 +256,7 @@ .. function:: open(filename[, flag[, mode]]) - Open a dbm database and return a ``dbm`` object. The *filename* argument is the + Open a dbm database and return a ``ndbm`` object. The *filename* argument is the name of the database file (without the :file:`.dir` or :file:`.pag` extensions). The optional *flag* argument must be one of these values: @@ -278,6 +281,12 @@ database has to be created. It defaults to octal ``0o666`` (and will be modified by the prevailing umask). + In addition to the dictionary-like methods, ``ndbm`` objects + provide the following method: + + .. method:: ndbm.close() + + Close the ``ndbm`` database. :mod:`dbm.dumb` --- Portable DBM implementation @@ -330,9 +339,14 @@ In addition to the methods provided by the :class:`collections.abc.MutableMapping` class, :class:`dumbdbm` objects - provide the following method: + provide the following methods: .. method:: dumbdbm.sync() Synchronize the on-disk directory and data files. This method is called by the :meth:`Shelve.sync` method. + + .. method:: dumbdbm.close() + + Close the ``dumbdbm`` database. + -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 19:40:12 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 25 Jun 2014 19:40:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzI5?= =?utf-8?q?=3A_Used_the_=22with=22_statement_in_the_dbm=2Edumb_module_to_e?= =?utf-8?q?nsure?= Message-ID: <3gzBT85dwFz7LjV@mail.python.org> http://hg.python.org/cpython/rev/fdbcb11e0323 changeset: 91401:fdbcb11e0323 branch: 3.4 parent: 91399:88fb216b99fb user: Serhiy Storchaka date: Wed Jun 25 20:35:31 2014 +0300 summary: Issue #21729: Used the "with" statement in the dbm.dumb module to ensure files closing. Patch by Claudiu Popa. files: Lib/dbm/dumb.py | 69 +++++++++++++++++------------------- Misc/NEWS | 3 + 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -68,9 +68,10 @@ try: f = _io.open(self._datfile, 'r', encoding="Latin-1") except OSError: - f = _io.open(self._datfile, 'w', encoding="Latin-1") - self._chmod(self._datfile) - f.close() + with _io.open(self._datfile, 'w', encoding="Latin-1") as f: + self._chmod(self._datfile) + else: + f.close() self._update() # Read directory file into the in-memory index dict. @@ -81,12 +82,12 @@ except OSError: pass else: - for line in f: - line = line.rstrip() - key, pos_and_siz_pair = eval(line) - key = key.encode('Latin-1') - self._index[key] = pos_and_siz_pair - f.close() + with f: + for line in f: + line = line.rstrip() + key, pos_and_siz_pair = eval(line) + key = key.encode('Latin-1') + self._index[key] = pos_and_siz_pair # Write the index dict to the directory file. The original directory # file (if any) is renamed with a .bak extension first. If a .bak @@ -108,13 +109,13 @@ except OSError: pass - f = self._io.open(self._dirfile, 'w', encoding="Latin-1") - self._chmod(self._dirfile) - for key, pos_and_siz_pair in self._index.items(): - # Use Latin-1 since it has no qualms with any value in any - # position; UTF-8, though, does care sometimes. - f.write("%r, %r\n" % (key.decode('Latin-1'), pos_and_siz_pair)) - f.close() + with self._io.open(self._dirfile, 'w', encoding="Latin-1") as f: + self._chmod(self._dirfile) + for key, pos_and_siz_pair in self._index.items(): + # Use Latin-1 since it has no qualms with any value in any + # position; UTF-8, though, does care sometimes. + entry = "%r, %r\n" % (key.decode('Latin-1'), pos_and_siz_pair) + f.write(entry) sync = _commit @@ -127,10 +128,9 @@ key = key.encode('utf-8') self._verify_open() pos, siz = self._index[key] # may raise KeyError - f = _io.open(self._datfile, 'rb') - f.seek(pos) - dat = f.read(siz) - f.close() + with _io.open(self._datfile, 'rb') as f: + f.seek(pos) + dat = f.read(siz) return dat # Append val to the data file, starting at a _BLOCKSIZE-aligned @@ -138,14 +138,13 @@ # to get to an aligned offset. Return pair # (starting offset of val, len(val)) def _addval(self, val): - f = _io.open(self._datfile, 'rb+') - f.seek(0, 2) - pos = int(f.tell()) - npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE - f.write(b'\0'*(npos-pos)) - pos = npos - f.write(val) - f.close() + with _io.open(self._datfile, 'rb+') as f: + f.seek(0, 2) + pos = int(f.tell()) + npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE + f.write(b'\0'*(npos-pos)) + pos = npos + f.write(val) return (pos, len(val)) # Write val to the data file, starting at offset pos. The caller @@ -153,10 +152,9 @@ # pos to hold val, without overwriting some other value. Return # pair (pos, len(val)). def _setval(self, pos, val): - f = _io.open(self._datfile, 'rb+') - f.seek(pos) - f.write(val) - f.close() + with _io.open(self._datfile, 'rb+') as f: + f.seek(pos) + f.write(val) return (pos, len(val)) # key is a new key whose associated value starts in the data file @@ -164,10 +162,9 @@ # the in-memory index dict, and append one to the directory file. def _addkey(self, key, pos_and_siz_pair): self._index[key] = pos_and_siz_pair - f = _io.open(self._dirfile, 'a', encoding="Latin-1") - self._chmod(self._dirfile) - f.write("%r, %r\n" % (key.decode("Latin-1"), pos_and_siz_pair)) - f.close() + with _io.open(self._dirfile, 'a', encoding="Latin-1") as f: + self._chmod(self._dirfile) + f.write("%r, %r\n" % (key.decode("Latin-1"), pos_and_siz_pair)) def __setitem__(self, key, val): if isinstance(key, str): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,9 @@ Library ------- +- Issue #21729: Used the "with" statement in the dbm.dumb module to ensure + files closing. Patch by Claudiu Popa. + - Issue #21491: socketserver: Fix a race condition in child processes reaping. - Issue #21832: Require named tuple inputs to be exact strings. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 19:40:14 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 25 Jun 2014 19:40:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321729=3A_Used_the_=22with=22_statement_in_the_d?= =?utf-8?q?bm=2Edumb_module_to_ensure?= Message-ID: <3gzBTB14Nhz7LjV@mail.python.org> http://hg.python.org/cpython/rev/e41b4e8c0c1d changeset: 91402:e41b4e8c0c1d parent: 91400:90dd9eec1230 parent: 91401:fdbcb11e0323 user: Serhiy Storchaka date: Wed Jun 25 20:37:30 2014 +0300 summary: Issue #21729: Used the "with" statement in the dbm.dumb module to ensure files closing. Patch by Claudiu Popa. files: Lib/dbm/dumb.py | 69 +++++++++++++++++------------------- Misc/NEWS | 3 + 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -79,9 +79,10 @@ try: f = _io.open(self._datfile, 'r', encoding="Latin-1") except OSError: - f = _io.open(self._datfile, 'w', encoding="Latin-1") - self._chmod(self._datfile) - f.close() + with _io.open(self._datfile, 'w', encoding="Latin-1") as f: + self._chmod(self._datfile) + else: + f.close() # Read directory file into the in-memory index dict. def _update(self): @@ -91,12 +92,12 @@ except OSError: pass else: - for line in f: - line = line.rstrip() - key, pos_and_siz_pair = eval(line) - key = key.encode('Latin-1') - self._index[key] = pos_and_siz_pair - f.close() + with f: + for line in f: + line = line.rstrip() + key, pos_and_siz_pair = eval(line) + key = key.encode('Latin-1') + self._index[key] = pos_and_siz_pair # Write the index dict to the directory file. The original directory # file (if any) is renamed with a .bak extension first. If a .bak @@ -118,13 +119,13 @@ except OSError: pass - f = self._io.open(self._dirfile, 'w', encoding="Latin-1") - self._chmod(self._dirfile) - for key, pos_and_siz_pair in self._index.items(): - # Use Latin-1 since it has no qualms with any value in any - # position; UTF-8, though, does care sometimes. - f.write("%r, %r\n" % (key.decode('Latin-1'), pos_and_siz_pair)) - f.close() + with self._io.open(self._dirfile, 'w', encoding="Latin-1") as f: + self._chmod(self._dirfile) + for key, pos_and_siz_pair in self._index.items(): + # Use Latin-1 since it has no qualms with any value in any + # position; UTF-8, though, does care sometimes. + entry = "%r, %r\n" % (key.decode('Latin-1'), pos_and_siz_pair) + f.write(entry) sync = _commit @@ -137,10 +138,9 @@ key = key.encode('utf-8') self._verify_open() pos, siz = self._index[key] # may raise KeyError - f = _io.open(self._datfile, 'rb') - f.seek(pos) - dat = f.read(siz) - f.close() + with _io.open(self._datfile, 'rb') as f: + f.seek(pos) + dat = f.read(siz) return dat # Append val to the data file, starting at a _BLOCKSIZE-aligned @@ -148,14 +148,13 @@ # to get to an aligned offset. Return pair # (starting offset of val, len(val)) def _addval(self, val): - f = _io.open(self._datfile, 'rb+') - f.seek(0, 2) - pos = int(f.tell()) - npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE - f.write(b'\0'*(npos-pos)) - pos = npos - f.write(val) - f.close() + with _io.open(self._datfile, 'rb+') as f: + f.seek(0, 2) + pos = int(f.tell()) + npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE + f.write(b'\0'*(npos-pos)) + pos = npos + f.write(val) return (pos, len(val)) # Write val to the data file, starting at offset pos. The caller @@ -163,10 +162,9 @@ # pos to hold val, without overwriting some other value. Return # pair (pos, len(val)). def _setval(self, pos, val): - f = _io.open(self._datfile, 'rb+') - f.seek(pos) - f.write(val) - f.close() + with _io.open(self._datfile, 'rb+') as f: + f.seek(pos) + f.write(val) return (pos, len(val)) # key is a new key whose associated value starts in the data file @@ -174,10 +172,9 @@ # the in-memory index dict, and append one to the directory file. def _addkey(self, key, pos_and_siz_pair): self._index[key] = pos_and_siz_pair - f = _io.open(self._dirfile, 'a', encoding="Latin-1") - self._chmod(self._dirfile) - f.write("%r, %r\n" % (key.decode("Latin-1"), pos_and_siz_pair)) - f.close() + with _io.open(self._dirfile, 'a', encoding="Latin-1") as f: + self._chmod(self._dirfile) + f.write("%r, %r\n" % (key.decode("Latin-1"), pos_and_siz_pair)) def __setitem__(self, key, val): if isinstance(key, str): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,9 @@ Library ------- +- Issue #21729: Used the "with" statement in the dbm.dumb module to ensure + files closing. Patch by Claudiu Popa. + - Issue #21491: socketserver: Fix a race condition in child processes reaping. - Issue #21719: Added the ``st_file_attributes`` field to os.stat_result on -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 19:40:15 2014 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 25 Jun 2014 19:40:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxNzI5?= =?utf-8?q?=3A_Used_the_=22with=22_statement_in_the_dbm=2Edumb_module_to_e?= =?utf-8?q?nsure?= Message-ID: <3gzBTC46Csz7Ljr@mail.python.org> http://hg.python.org/cpython/rev/893e79196fb3 changeset: 91403:893e79196fb3 branch: 2.7 parent: 91397:de44bc26a00a user: Serhiy Storchaka date: Wed Jun 25 20:37:49 2014 +0300 summary: Issue #21729: Used the "with" statement in the dbm.dumb module to ensure files closing. Patch by Claudiu Popa. files: Lib/dumbdbm.py | 62 +++++++++++++++++-------------------- Misc/NEWS | 3 + 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/Lib/dumbdbm.py b/Lib/dumbdbm.py --- a/Lib/dumbdbm.py +++ b/Lib/dumbdbm.py @@ -68,9 +68,10 @@ try: f = _open(self._datfile, 'r') except IOError: - f = _open(self._datfile, 'w') - self._chmod(self._datfile) - f.close() + with _open(self._datfile, 'w') as f: + self._chmod(self._datfile) + else: + f.close() self._update() # Read directory file into the in-memory index dict. @@ -81,11 +82,11 @@ except IOError: pass else: - for line in f: - line = line.rstrip() - key, pos_and_siz_pair = eval(line) - self._index[key] = pos_and_siz_pair - f.close() + with f: + for line in f: + line = line.rstrip() + key, pos_and_siz_pair = eval(line) + self._index[key] = pos_and_siz_pair # Write the index dict to the directory file. The original directory # file (if any) is renamed with a .bak extension first. If a .bak @@ -107,20 +108,18 @@ except self._os.error: pass - f = self._open(self._dirfile, 'w') - self._chmod(self._dirfile) - for key, pos_and_siz_pair in self._index.iteritems(): - f.write("%r, %r\n" % (key, pos_and_siz_pair)) - f.close() + with self._open(self._dirfile, 'w') as f: + self._chmod(self._dirfile) + for key, pos_and_siz_pair in self._index.iteritems(): + f.write("%r, %r\n" % (key, pos_and_siz_pair)) sync = _commit def __getitem__(self, key): pos, siz = self._index[key] # may raise KeyError - f = _open(self._datfile, 'rb') - f.seek(pos) - dat = f.read(siz) - f.close() + with _open(self._datfile, 'rb') as f: + f.seek(pos) + dat = f.read(siz) return dat # Append val to the data file, starting at a _BLOCKSIZE-aligned @@ -128,14 +127,13 @@ # to get to an aligned offset. Return pair # (starting offset of val, len(val)) def _addval(self, val): - f = _open(self._datfile, 'rb+') - f.seek(0, 2) - pos = int(f.tell()) - npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE - f.write('\0'*(npos-pos)) - pos = npos - f.write(val) - f.close() + with _open(self._datfile, 'rb+') as f: + f.seek(0, 2) + pos = int(f.tell()) + npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE + f.write('\0'*(npos-pos)) + pos = npos + f.write(val) return (pos, len(val)) # Write val to the data file, starting at offset pos. The caller @@ -143,10 +141,9 @@ # pos to hold val, without overwriting some other value. Return # pair (pos, len(val)). def _setval(self, pos, val): - f = _open(self._datfile, 'rb+') - f.seek(pos) - f.write(val) - f.close() + with _open(self._datfile, 'rb+') as f: + f.seek(pos) + f.write(val) return (pos, len(val)) # key is a new key whose associated value starts in the data file @@ -154,10 +151,9 @@ # the in-memory index dict, and append one to the directory file. def _addkey(self, key, pos_and_siz_pair): self._index[key] = pos_and_siz_pair - f = _open(self._dirfile, 'a') - self._chmod(self._dirfile) - f.write("%r, %r\n" % (key, pos_and_siz_pair)) - f.close() + with _open(self._dirfile, 'a') as f: + self._chmod(self._dirfile) + f.write("%r, %r\n" % (key, pos_and_siz_pair)) def __setitem__(self, key, val): if not type(key) == type('') == type(val): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -32,6 +32,9 @@ Library ------- +- Issue #21729: Used the "with" statement in the dbm.dumb module to ensure + files closing. + - Issue #21672: Fix the behavior of ntpath.join on UNC-style paths. - Issue #19145: The times argument for itertools.repeat now handles -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 21:44:12 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 25 Jun 2014 21:44:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbywgVHVs?= =?utf-8?q?ip_issue_177=3A_Rewite_repr=28=29_of_Future=2C_Task=2C_Handle_a?= =?utf-8?q?nd_TimerHandle?= Message-ID: <3gzFDD4WDgz7LjT@mail.python.org> http://hg.python.org/cpython/rev/dead2b526c3b changeset: 91404:dead2b526c3b branch: 3.4 parent: 91401:fdbcb11e0323 user: Victor Stinner date: Wed Jun 25 21:41:58 2014 +0200 summary: asyncio, Tulip issue 177: Rewite repr() of Future, Task, Handle and TimerHandle - Uniformize repr() output to format "" - On Python 3.5+, repr(Task) uses the qualified name instead of the short name of the coroutine files: Lib/asyncio/events.py | 56 ++- Lib/asyncio/futures.py | 48 ++- Lib/asyncio/tasks.py | 51 ++- Lib/test/test_asyncio/test_base_events.py | 4 +- Lib/test/test_asyncio/test_events.py | 66 ++-- Lib/test/test_asyncio/test_futures.py | 56 +++- Lib/test/test_asyncio/test_tasks.py | 134 +++++---- 7 files changed, 259 insertions(+), 156 deletions(-) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -18,6 +18,7 @@ _PY34 = sys.version_info >= (3, 4) + def _get_function_source(func): if _PY34: func = inspect.unwrap(func) @@ -33,6 +34,35 @@ return None +def _format_args(args): + # function formatting ('hello',) as ('hello') + args_repr = repr(args) + if len(args) == 1 and args_repr.endswith(',)'): + args_repr = args_repr[:-2] + ')' + return args_repr + + +def _format_callback(func, args, suffix=''): + if isinstance(func, functools.partial): + if args is not None: + suffix = _format_args(args) + suffix + return _format_callback(func.func, func.args, suffix) + + func_repr = getattr(func, '__qualname__', None) + if not func_repr: + func_repr = repr(func) + + if args is not None: + func_repr += _format_args(args) + if suffix: + func_repr += suffix + + source = _get_function_source(func) + if source: + func_repr += ' at %s:%s' % source + return func_repr + + class Handle: """Object returned by callback registration methods.""" @@ -46,18 +76,11 @@ self._cancelled = False def __repr__(self): - cb_repr = getattr(self._callback, '__qualname__', None) - if not cb_repr: - cb_repr = str(self._callback) - - source = _get_function_source(self._callback) - if source: - cb_repr += ' at %s:%s' % source - - res = 'Handle({}, {})'.format(cb_repr, self._args) + info = [] if self._cancelled: - res += '' - return res + info.append('cancelled') + info.append(_format_callback(self._callback, self._args)) + return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) def cancel(self): self._cancelled = True @@ -88,13 +111,12 @@ self._when = when def __repr__(self): - res = 'TimerHandle({}, {}, {})'.format(self._when, - self._callback, - self._args) + info = [] if self._cancelled: - res += '' - - return res + info.append('cancelled') + info.append('when=%s' % self._when) + info.append(_format_callback(self._callback, self._args)) + return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) def __hash__(self): return hash(self._when) diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -150,24 +150,40 @@ self._loop = loop self._callbacks = [] + def _format_callbacks(self): + cb = self._callbacks + size = len(cb) + if not size: + cb = '' + + def format_cb(callback): + return events._format_callback(callback, ()) + + if size == 1: + cb = format_cb(cb[0]) + elif size == 2: + cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1])) + elif size > 2: + cb = '{}, <{} more>, {}'.format(format_cb(cb[0]), + size-2, + format_cb(cb[-1])) + return 'cb=[%s]' % cb + + def _format_result(self): + if self._state != _FINISHED: + return None + elif self._exception is not None: + return 'exception={!r}'.format(self._exception) + else: + return 'result={!r}'.format(self._result) + def __repr__(self): - res = self.__class__.__name__ + info = [self._state.lower()] if self._state == _FINISHED: - if self._exception is not None: - res += ''.format(self._exception) - else: - res += ''.format(self._result) - elif self._callbacks: - size = len(self._callbacks) - if size > 2: - res += '<{}, [{}, <{} more>, {}]>'.format( - self._state, self._callbacks[0], - size-2, self._callbacks[-1]) - else: - res += '<{}, {}>'.format(self._state, self._callbacks) - else: - res += '<{}>'.format(self._state) - return res + info.append(self._format_result()) + if self._callbacks: + info.append(self._format_callbacks()) + return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) # On Python 3.3 or older, objects with a destructor part of a reference # cycle are never destroyed. It's not more the case on Python 3.4 thanks to diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -132,6 +132,22 @@ return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj) +def _format_coroutine(coro): + assert iscoroutine(coro) + if _PY35: + coro_name = coro.__qualname__ + else: + coro_name = coro.__name__ + + filename = coro.gi_code.co_filename + if coro.gi_frame is not None: + lineno = coro.gi_frame.f_lineno + return '%s() at %s:%s' % (coro_name, filename, lineno) + else: + lineno = coro.gi_code.co_firstlineno + return '%s() done at %s:%s' % (coro_name, filename, lineno) + + class Task(futures.Future): """A coroutine wrapped in a Future.""" @@ -195,26 +211,21 @@ futures.Future.__del__(self) def __repr__(self): - res = super().__repr__() - if (self._must_cancel and - self._state == futures._PENDING and - ')'.format(text) + res[i:] - return res + info = [] + if self._must_cancel: + info.append('cancelling') + else: + info.append(self._state.lower()) + + info.append(_format_coroutine(self._coro)) + + if self._state == futures._FINISHED: + info.append(self._format_result()) + + if self._callbacks: + info.append(self._format_callbacks()) + + return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) def get_stack(self, *, limit=None): """Return the list of stack frames for this task's coroutine. 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 @@ -1016,14 +1016,14 @@ self.loop.run_forever() fmt, *args = m_logger.warning.call_args[0] self.assertRegex(fmt % tuple(args), - "^Executing Handle.*stop_loop_cb.* took .* seconds$") + "^Executing took .* seconds$") # slow task asyncio.async(stop_loop_coro(self.loop), loop=self.loop) self.loop.run_forever() fmt, *args = m_logger.warning.call_args[0] self.assertRegex(fmt % tuple(args), - "^Executing Task.*stop_loop_coro.* took .* seconds$") + "^Executing took .* seconds$") if __name__ == '__main__': 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 @@ -1747,7 +1747,7 @@ return asyncio.SelectorEventLoop(selectors.SelectSelector()) -def noop(): +def noop(*args): pass @@ -1797,49 +1797,51 @@ h = asyncio.Handle(lambda: None, (), self.loop) wd['h'] = h # Would fail without __weakref__ slot. - def test_repr(self): + def test_handle_repr(self): # simple function h = asyncio.Handle(noop, (), self.loop) src = test_utils.get_function_source(noop) self.assertEqual(repr(h), - 'Handle(noop at %s:%s, ())' % src) + '' % src) # cancelled handle h.cancel() self.assertEqual(repr(h), - 'Handle(noop at %s:%s, ())' % src) + '' % src) # decorated function cb = asyncio.coroutine(noop) h = asyncio.Handle(cb, (), self.loop) self.assertEqual(repr(h), - 'Handle(noop at %s:%s, ())' % src) + '' % src) # partial function - cb = functools.partial(noop) - h = asyncio.Handle(cb, (), self.loop) + cb = functools.partial(noop, 1, 2) + h = asyncio.Handle(cb, (3,), self.loop) filename, lineno = src - regex = (r'^Handle\(functools.partial\(' - r'\) at %s:%s, ' - r'\(\)\)$' % (re.escape(filename), lineno)) + regex = (r'^$' + % (re.escape(filename), lineno)) self.assertRegex(repr(h), regex) # partial method if sys.version_info >= (3, 4): - method = HandleTests.test_repr + method = HandleTests.test_handle_repr cb = functools.partialmethod(method) src = test_utils.get_function_source(method) h = asyncio.Handle(cb, (), self.loop) filename, lineno = src - regex = (r'^Handle\(functools.partialmethod\(' - r', , \) at %s:%s, ' - r'\(\)\)$' % (re.escape(filename), lineno)) + cb_regex = r'' + cb_regex = (r'functools.partialmethod\(%s, , \)\(\)' % cb_regex) + regex = (r'^$' + % (cb_regex, re.escape(filename), lineno)) self.assertRegex(repr(h), regex) +class TimerTests(unittest.TestCase): -class TimerTests(unittest.TestCase): + def setUp(self): + self.loop = mock.Mock() def test_hash(self): when = time.monotonic() @@ -1858,29 +1860,37 @@ self.assertIs(h._args, args) self.assertFalse(h._cancelled) - r = repr(h) - self.assertTrue(r.endswith('())')) - + # cancel h.cancel() self.assertTrue(h._cancelled) - r = repr(h) - self.assertTrue(r.endswith('())'), r) + # when cannot be None self.assertRaises(AssertionError, asyncio.TimerHandle, None, callback, args, - mock.Mock()) + self.loop) + + def test_timer_repr(self): + # simple function + h = asyncio.TimerHandle(123, noop, (), self.loop) + src = test_utils.get_function_source(noop) + self.assertEqual(repr(h), + '' % src) + + # cancelled handle + h.cancel() + self.assertEqual(repr(h), + '' + % src) def test_timer_comparison(self): - loop = mock.Mock() - def callback(*args): return args when = time.monotonic() - h1 = asyncio.TimerHandle(when, callback, (), loop) - h2 = asyncio.TimerHandle(when, callback, (), loop) + h1 = asyncio.TimerHandle(when, callback, (), self.loop) + h2 = asyncio.TimerHandle(when, callback, (), self.loop) # TODO: Use assertLess etc. self.assertFalse(h1 < h2) self.assertFalse(h2 < h1) @@ -1896,8 +1906,8 @@ h2.cancel() self.assertFalse(h1 == h2) - h1 = asyncio.TimerHandle(when, callback, (), loop) - h2 = asyncio.TimerHandle(when + 10.0, callback, (), loop) + h1 = asyncio.TimerHandle(when, callback, (), self.loop) + h2 = asyncio.TimerHandle(when + 10.0, callback, (), self.loop) self.assertTrue(h1 < h2) self.assertFalse(h2 < h1) self.assertTrue(h1 <= h2) @@ -1909,7 +1919,7 @@ self.assertFalse(h1 == h2) self.assertTrue(h1 != h2) - h3 = asyncio.Handle(callback, (), loop) + h3 = asyncio.Handle(callback, (), self.loop) self.assertIs(NotImplemented, h1.__eq__(h3)) self.assertIs(NotImplemented, h1.__ne__(h3)) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -1,6 +1,7 @@ """Tests for futures.py.""" import concurrent.futures +import re import threading import unittest from unittest import mock @@ -12,6 +13,12 @@ def _fakefunc(f): return f +def first_cb(): + pass + +def last_cb(): + pass + class FutureTests(test_utils.TestCase): @@ -95,39 +102,60 @@ # The second "yield from f" does not yield f. self.assertEqual(next(g), ('C', 42)) # yield 'C', y. - def test_repr(self): + def test_future_repr(self): f_pending = asyncio.Future(loop=self.loop) - self.assertEqual(repr(f_pending), 'Future') + self.assertEqual(repr(f_pending), '') f_pending.cancel() f_cancelled = asyncio.Future(loop=self.loop) f_cancelled.cancel() - self.assertEqual(repr(f_cancelled), 'Future') + self.assertEqual(repr(f_cancelled), '') f_result = asyncio.Future(loop=self.loop) f_result.set_result(4) - self.assertEqual(repr(f_result), 'Future') + self.assertEqual(repr(f_result), '') self.assertEqual(f_result.result(), 4) exc = RuntimeError() f_exception = asyncio.Future(loop=self.loop) f_exception.set_exception(exc) - self.assertEqual(repr(f_exception), 'Future') + self.assertEqual(repr(f_exception), '') self.assertIs(f_exception.exception(), exc) - f_few_callbacks = asyncio.Future(loop=self.loop) - f_few_callbacks.add_done_callback(_fakefunc) - self.assertIn('Future' % fake_repr) + f_one_callbacks.cancel() + self.assertEqual(repr(f_one_callbacks), + '') + + f_two_callbacks = asyncio.Future(loop=self.loop) + f_two_callbacks.add_done_callback(first_cb) + f_two_callbacks.add_done_callback(last_cb) + first_repr = func_repr(first_cb) + last_repr = func_repr(last_cb) + self.assertRegex(repr(f_two_callbacks), + r'' + % (first_repr, last_repr)) f_many_callbacks = asyncio.Future(loop=self.loop) - for i in range(20): + f_many_callbacks.add_done_callback(first_cb) + for i in range(8): f_many_callbacks.add_done_callback(_fakefunc) - r = repr(f_many_callbacks) - self.assertIn('Future', r) + f_many_callbacks.add_done_callback(last_cb) + cb_regex = r'%s, <8 more>, %s' % (first_repr, last_repr) + self.assertRegex(repr(f_many_callbacks), + r'' % cb_regex) f_many_callbacks.cancel() + self.assertEqual(repr(f_many_callbacks), + '') def test_copy_state(self): # Test the internal _copy_state method since it's being directly 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 @@ -26,7 +26,7 @@ class Dummy: def __repr__(self): - return 'Dummy()' + return '' def __call__(self, *args): pass @@ -122,6 +122,7 @@ yield from [] return 'abc' + # test coroutine function self.assertEqual(notmuch.__name__, 'notmuch') if PY35: self.assertEqual(notmuch.__qualname__, @@ -131,72 +132,87 @@ filename, lineno = test_utils.get_function_source(notmuch) src = "%s:%s" % (filename, lineno) + # test coroutine object gen = notmuch() + if PY35: + coro_qualname = 'TaskTests.test_task_repr..notmuch' + else: + coro_qualname = 'notmuch' self.assertEqual(gen.__name__, 'notmuch') if PY35: self.assertEqual(gen.__qualname__, - 'TaskTests.test_task_repr..notmuch') + coro_qualname) + # test pending Task + t = asyncio.Task(gen, loop=self.loop) + t.add_done_callback(Dummy()) + coro = '%s() at %s' % (coro_qualname, src) + self.assertEqual(repr(t), + '()]>' % coro) + + # test cancelling Task + t.cancel() # Does not take immediate effect! + self.assertEqual(repr(t), + '()]>' % coro) + + # test cancelled Task + self.assertRaises(asyncio.CancelledError, + self.loop.run_until_complete, t) + coro = '%s() done at %s' % (coro_qualname, src) + self.assertEqual(repr(t), + '' % coro) + + # test finished Task + t = asyncio.Task(notmuch(), loop=self.loop) + self.loop.run_until_complete(t) + self.assertEqual(repr(t), + "" % coro) + + def test_task_repr_coro_decorator(self): + @asyncio.coroutine + def notmuch(): + # notmuch() function doesn't use yield from: it will be wrapped by + # @coroutine decorator + return 123 + + # test coroutine function + self.assertEqual(notmuch.__name__, 'notmuch') + if PY35: + self.assertEqual(notmuch.__qualname__, + 'TaskTests.test_task_repr_coro_decorator..notmuch') + self.assertEqual(notmuch.__module__, __name__) + + # test coroutine object + gen = notmuch() + if PY35: + # On Python >= 3.5, generators now inherit the name of the + # function, as expected, and have a qualified name (__qualname__ + # attribute). + coro_name = 'notmuch' + coro_qualname = 'TaskTests.test_task_repr_coro_decorator..notmuch' + elif tasks._DEBUG: + # In debug mode, @coroutine decorator uses CoroWrapper which gets + # its name (__name__ attribute) from the wrapped coroutine + # function. + coro_name = coro_qualname = 'notmuch' + else: + # On Python < 3.5, generators inherit the name of the code, not of + # the function. See: http://bugs.python.org/issue21205 + coro_name = coro_qualname = 'coro' + self.assertEqual(gen.__name__, coro_name) + if PY35: + self.assertEqual(gen.__qualname__, coro_qualname) + + # format the coroutine object + code = gen.gi_code + coro = ('%s() at %s:%s' + % (coro_qualname, code.co_filename, code.co_firstlineno)) + + # test pending Task t = asyncio.Task(gen, loop=self.loop) t.add_done_callback(Dummy()) self.assertEqual(repr(t), - 'Task()' % src) - - t.cancel() # Does not take immediate effect! - self.assertEqual(repr(t), - 'Task()' % src) - self.assertRaises(asyncio.CancelledError, - self.loop.run_until_complete, t) - self.assertEqual(repr(t), - 'Task()' - % (filename, lineno)) - - t = asyncio.Task(notmuch(), loop=self.loop) - self.loop.run_until_complete(t) - self.assertEqual(repr(t), - "Task()" - % (filename, lineno)) - - def test_task_repr_custom(self): - @asyncio.coroutine - def notmuch(): - pass - - self.assertEqual(notmuch.__name__, 'notmuch') - self.assertEqual(notmuch.__module__, __name__) - if PY35: - self.assertEqual(notmuch.__qualname__, - 'TaskTests.test_task_repr_custom..notmuch') - - class T(asyncio.Future): - def __repr__(self): - return 'T[]' - - class MyTask(asyncio.Task, T): - def __repr__(self): - return super().__repr__() - - gen = notmuch() - if PY35 or tasks._DEBUG: - # On Python >= 3.5, generators now inherit the name of the - # function, as expected, and have a qualified name (__qualname__ - # attribute). In debug mode, @coroutine decorator uses CoroWrapper - # which gets its name (__name__ attribute) from the wrapped - # coroutine function. - coro_name = 'notmuch' - else: - # On Python < 3.5, generators inherit the name of the code, not of - # the function. See: http://bugs.python.org/issue21205 - coro_name = 'coro' - self.assertEqual(gen.__name__, coro_name) - if PY35: - self.assertEqual(gen.__qualname__, - 'TaskTests.test_task_repr_custom..notmuch') - - t = MyTask(gen, loop=self.loop) - filename = gen.gi_code.co_filename - lineno = gen.gi_frame.f_lineno - self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (coro_name, filename, lineno)) + '()]>' % coro) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 21:44:14 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 25 Jun 2014 21:44:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=2C_Tulip_issue_177=3A_Rewite_r?= =?utf-8?q?epr=28=29_of_Future=2C_Task=2C_Handle_and?= Message-ID: <3gzFDG1FMcz7Lk9@mail.python.org> http://hg.python.org/cpython/rev/df8788082d2f changeset: 91405:df8788082d2f parent: 91402:e41b4e8c0c1d parent: 91404:dead2b526c3b user: Victor Stinner date: Wed Jun 25 21:43:21 2014 +0200 summary: (Merge 3.4) asyncio, Tulip issue 177: Rewite repr() of Future, Task, Handle and TimerHandle - Uniformize repr() output to format "" - On Python 3.5+, repr(Task) uses the qualified name instead of the short name of the coroutine files: Lib/asyncio/events.py | 56 ++- Lib/asyncio/futures.py | 48 ++- Lib/asyncio/tasks.py | 51 ++- Lib/test/test_asyncio/test_base_events.py | 4 +- Lib/test/test_asyncio/test_events.py | 66 ++-- Lib/test/test_asyncio/test_futures.py | 56 +++- Lib/test/test_asyncio/test_tasks.py | 134 +++++---- 7 files changed, 259 insertions(+), 156 deletions(-) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -18,6 +18,7 @@ _PY34 = sys.version_info >= (3, 4) + def _get_function_source(func): if _PY34: func = inspect.unwrap(func) @@ -33,6 +34,35 @@ return None +def _format_args(args): + # function formatting ('hello',) as ('hello') + args_repr = repr(args) + if len(args) == 1 and args_repr.endswith(',)'): + args_repr = args_repr[:-2] + ')' + return args_repr + + +def _format_callback(func, args, suffix=''): + if isinstance(func, functools.partial): + if args is not None: + suffix = _format_args(args) + suffix + return _format_callback(func.func, func.args, suffix) + + func_repr = getattr(func, '__qualname__', None) + if not func_repr: + func_repr = repr(func) + + if args is not None: + func_repr += _format_args(args) + if suffix: + func_repr += suffix + + source = _get_function_source(func) + if source: + func_repr += ' at %s:%s' % source + return func_repr + + class Handle: """Object returned by callback registration methods.""" @@ -46,18 +76,11 @@ self._cancelled = False def __repr__(self): - cb_repr = getattr(self._callback, '__qualname__', None) - if not cb_repr: - cb_repr = str(self._callback) - - source = _get_function_source(self._callback) - if source: - cb_repr += ' at %s:%s' % source - - res = 'Handle({}, {})'.format(cb_repr, self._args) + info = [] if self._cancelled: - res += '' - return res + info.append('cancelled') + info.append(_format_callback(self._callback, self._args)) + return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) def cancel(self): self._cancelled = True @@ -88,13 +111,12 @@ self._when = when def __repr__(self): - res = 'TimerHandle({}, {}, {})'.format(self._when, - self._callback, - self._args) + info = [] if self._cancelled: - res += '' - - return res + info.append('cancelled') + info.append('when=%s' % self._when) + info.append(_format_callback(self._callback, self._args)) + return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) def __hash__(self): return hash(self._when) diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -150,24 +150,40 @@ self._loop = loop self._callbacks = [] + def _format_callbacks(self): + cb = self._callbacks + size = len(cb) + if not size: + cb = '' + + def format_cb(callback): + return events._format_callback(callback, ()) + + if size == 1: + cb = format_cb(cb[0]) + elif size == 2: + cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1])) + elif size > 2: + cb = '{}, <{} more>, {}'.format(format_cb(cb[0]), + size-2, + format_cb(cb[-1])) + return 'cb=[%s]' % cb + + def _format_result(self): + if self._state != _FINISHED: + return None + elif self._exception is not None: + return 'exception={!r}'.format(self._exception) + else: + return 'result={!r}'.format(self._result) + def __repr__(self): - res = self.__class__.__name__ + info = [self._state.lower()] if self._state == _FINISHED: - if self._exception is not None: - res += ''.format(self._exception) - else: - res += ''.format(self._result) - elif self._callbacks: - size = len(self._callbacks) - if size > 2: - res += '<{}, [{}, <{} more>, {}]>'.format( - self._state, self._callbacks[0], - size-2, self._callbacks[-1]) - else: - res += '<{}, {}>'.format(self._state, self._callbacks) - else: - res += '<{}>'.format(self._state) - return res + info.append(self._format_result()) + if self._callbacks: + info.append(self._format_callbacks()) + return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) # On Python 3.3 or older, objects with a destructor part of a reference # cycle are never destroyed. It's not more the case on Python 3.4 thanks to diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -132,6 +132,22 @@ return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj) +def _format_coroutine(coro): + assert iscoroutine(coro) + if _PY35: + coro_name = coro.__qualname__ + else: + coro_name = coro.__name__ + + filename = coro.gi_code.co_filename + if coro.gi_frame is not None: + lineno = coro.gi_frame.f_lineno + return '%s() at %s:%s' % (coro_name, filename, lineno) + else: + lineno = coro.gi_code.co_firstlineno + return '%s() done at %s:%s' % (coro_name, filename, lineno) + + class Task(futures.Future): """A coroutine wrapped in a Future.""" @@ -195,26 +211,21 @@ futures.Future.__del__(self) def __repr__(self): - res = super().__repr__() - if (self._must_cancel and - self._state == futures._PENDING and - ')'.format(text) + res[i:] - return res + info = [] + if self._must_cancel: + info.append('cancelling') + else: + info.append(self._state.lower()) + + info.append(_format_coroutine(self._coro)) + + if self._state == futures._FINISHED: + info.append(self._format_result()) + + if self._callbacks: + info.append(self._format_callbacks()) + + return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) def get_stack(self, *, limit=None): """Return the list of stack frames for this task's coroutine. 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 @@ -1016,14 +1016,14 @@ self.loop.run_forever() fmt, *args = m_logger.warning.call_args[0] self.assertRegex(fmt % tuple(args), - "^Executing Handle.*stop_loop_cb.* took .* seconds$") + "^Executing took .* seconds$") # slow task asyncio.async(stop_loop_coro(self.loop), loop=self.loop) self.loop.run_forever() fmt, *args = m_logger.warning.call_args[0] self.assertRegex(fmt % tuple(args), - "^Executing Task.*stop_loop_coro.* took .* seconds$") + "^Executing took .* seconds$") if __name__ == '__main__': 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 @@ -1747,7 +1747,7 @@ return asyncio.SelectorEventLoop(selectors.SelectSelector()) -def noop(): +def noop(*args): pass @@ -1797,49 +1797,51 @@ h = asyncio.Handle(lambda: None, (), self.loop) wd['h'] = h # Would fail without __weakref__ slot. - def test_repr(self): + def test_handle_repr(self): # simple function h = asyncio.Handle(noop, (), self.loop) src = test_utils.get_function_source(noop) self.assertEqual(repr(h), - 'Handle(noop at %s:%s, ())' % src) + '' % src) # cancelled handle h.cancel() self.assertEqual(repr(h), - 'Handle(noop at %s:%s, ())' % src) + '' % src) # decorated function cb = asyncio.coroutine(noop) h = asyncio.Handle(cb, (), self.loop) self.assertEqual(repr(h), - 'Handle(noop at %s:%s, ())' % src) + '' % src) # partial function - cb = functools.partial(noop) - h = asyncio.Handle(cb, (), self.loop) + cb = functools.partial(noop, 1, 2) + h = asyncio.Handle(cb, (3,), self.loop) filename, lineno = src - regex = (r'^Handle\(functools.partial\(' - r'\) at %s:%s, ' - r'\(\)\)$' % (re.escape(filename), lineno)) + regex = (r'^$' + % (re.escape(filename), lineno)) self.assertRegex(repr(h), regex) # partial method if sys.version_info >= (3, 4): - method = HandleTests.test_repr + method = HandleTests.test_handle_repr cb = functools.partialmethod(method) src = test_utils.get_function_source(method) h = asyncio.Handle(cb, (), self.loop) filename, lineno = src - regex = (r'^Handle\(functools.partialmethod\(' - r', , \) at %s:%s, ' - r'\(\)\)$' % (re.escape(filename), lineno)) + cb_regex = r'' + cb_regex = (r'functools.partialmethod\(%s, , \)\(\)' % cb_regex) + regex = (r'^$' + % (cb_regex, re.escape(filename), lineno)) self.assertRegex(repr(h), regex) +class TimerTests(unittest.TestCase): -class TimerTests(unittest.TestCase): + def setUp(self): + self.loop = mock.Mock() def test_hash(self): when = time.monotonic() @@ -1858,29 +1860,37 @@ self.assertIs(h._args, args) self.assertFalse(h._cancelled) - r = repr(h) - self.assertTrue(r.endswith('())')) - + # cancel h.cancel() self.assertTrue(h._cancelled) - r = repr(h) - self.assertTrue(r.endswith('())'), r) + # when cannot be None self.assertRaises(AssertionError, asyncio.TimerHandle, None, callback, args, - mock.Mock()) + self.loop) + + def test_timer_repr(self): + # simple function + h = asyncio.TimerHandle(123, noop, (), self.loop) + src = test_utils.get_function_source(noop) + self.assertEqual(repr(h), + '' % src) + + # cancelled handle + h.cancel() + self.assertEqual(repr(h), + '' + % src) def test_timer_comparison(self): - loop = mock.Mock() - def callback(*args): return args when = time.monotonic() - h1 = asyncio.TimerHandle(when, callback, (), loop) - h2 = asyncio.TimerHandle(when, callback, (), loop) + h1 = asyncio.TimerHandle(when, callback, (), self.loop) + h2 = asyncio.TimerHandle(when, callback, (), self.loop) # TODO: Use assertLess etc. self.assertFalse(h1 < h2) self.assertFalse(h2 < h1) @@ -1896,8 +1906,8 @@ h2.cancel() self.assertFalse(h1 == h2) - h1 = asyncio.TimerHandle(when, callback, (), loop) - h2 = asyncio.TimerHandle(when + 10.0, callback, (), loop) + h1 = asyncio.TimerHandle(when, callback, (), self.loop) + h2 = asyncio.TimerHandle(when + 10.0, callback, (), self.loop) self.assertTrue(h1 < h2) self.assertFalse(h2 < h1) self.assertTrue(h1 <= h2) @@ -1909,7 +1919,7 @@ self.assertFalse(h1 == h2) self.assertTrue(h1 != h2) - h3 = asyncio.Handle(callback, (), loop) + h3 = asyncio.Handle(callback, (), self.loop) self.assertIs(NotImplemented, h1.__eq__(h3)) self.assertIs(NotImplemented, h1.__ne__(h3)) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -1,6 +1,7 @@ """Tests for futures.py.""" import concurrent.futures +import re import threading import unittest from unittest import mock @@ -12,6 +13,12 @@ def _fakefunc(f): return f +def first_cb(): + pass + +def last_cb(): + pass + class FutureTests(test_utils.TestCase): @@ -95,39 +102,60 @@ # The second "yield from f" does not yield f. self.assertEqual(next(g), ('C', 42)) # yield 'C', y. - def test_repr(self): + def test_future_repr(self): f_pending = asyncio.Future(loop=self.loop) - self.assertEqual(repr(f_pending), 'Future') + self.assertEqual(repr(f_pending), '') f_pending.cancel() f_cancelled = asyncio.Future(loop=self.loop) f_cancelled.cancel() - self.assertEqual(repr(f_cancelled), 'Future') + self.assertEqual(repr(f_cancelled), '') f_result = asyncio.Future(loop=self.loop) f_result.set_result(4) - self.assertEqual(repr(f_result), 'Future') + self.assertEqual(repr(f_result), '') self.assertEqual(f_result.result(), 4) exc = RuntimeError() f_exception = asyncio.Future(loop=self.loop) f_exception.set_exception(exc) - self.assertEqual(repr(f_exception), 'Future') + self.assertEqual(repr(f_exception), '') self.assertIs(f_exception.exception(), exc) - f_few_callbacks = asyncio.Future(loop=self.loop) - f_few_callbacks.add_done_callback(_fakefunc) - self.assertIn('Future' % fake_repr) + f_one_callbacks.cancel() + self.assertEqual(repr(f_one_callbacks), + '') + + f_two_callbacks = asyncio.Future(loop=self.loop) + f_two_callbacks.add_done_callback(first_cb) + f_two_callbacks.add_done_callback(last_cb) + first_repr = func_repr(first_cb) + last_repr = func_repr(last_cb) + self.assertRegex(repr(f_two_callbacks), + r'' + % (first_repr, last_repr)) f_many_callbacks = asyncio.Future(loop=self.loop) - for i in range(20): + f_many_callbacks.add_done_callback(first_cb) + for i in range(8): f_many_callbacks.add_done_callback(_fakefunc) - r = repr(f_many_callbacks) - self.assertIn('Future', r) + f_many_callbacks.add_done_callback(last_cb) + cb_regex = r'%s, <8 more>, %s' % (first_repr, last_repr) + self.assertRegex(repr(f_many_callbacks), + r'' % cb_regex) f_many_callbacks.cancel() + self.assertEqual(repr(f_many_callbacks), + '') def test_copy_state(self): # Test the internal _copy_state method since it's being directly 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 @@ -26,7 +26,7 @@ class Dummy: def __repr__(self): - return 'Dummy()' + return '' def __call__(self, *args): pass @@ -122,6 +122,7 @@ yield from [] return 'abc' + # test coroutine function self.assertEqual(notmuch.__name__, 'notmuch') if PY35: self.assertEqual(notmuch.__qualname__, @@ -131,72 +132,87 @@ filename, lineno = test_utils.get_function_source(notmuch) src = "%s:%s" % (filename, lineno) + # test coroutine object gen = notmuch() + if PY35: + coro_qualname = 'TaskTests.test_task_repr..notmuch' + else: + coro_qualname = 'notmuch' self.assertEqual(gen.__name__, 'notmuch') if PY35: self.assertEqual(gen.__qualname__, - 'TaskTests.test_task_repr..notmuch') + coro_qualname) + # test pending Task + t = asyncio.Task(gen, loop=self.loop) + t.add_done_callback(Dummy()) + coro = '%s() at %s' % (coro_qualname, src) + self.assertEqual(repr(t), + '()]>' % coro) + + # test cancelling Task + t.cancel() # Does not take immediate effect! + self.assertEqual(repr(t), + '()]>' % coro) + + # test cancelled Task + self.assertRaises(asyncio.CancelledError, + self.loop.run_until_complete, t) + coro = '%s() done at %s' % (coro_qualname, src) + self.assertEqual(repr(t), + '' % coro) + + # test finished Task + t = asyncio.Task(notmuch(), loop=self.loop) + self.loop.run_until_complete(t) + self.assertEqual(repr(t), + "" % coro) + + def test_task_repr_coro_decorator(self): + @asyncio.coroutine + def notmuch(): + # notmuch() function doesn't use yield from: it will be wrapped by + # @coroutine decorator + return 123 + + # test coroutine function + self.assertEqual(notmuch.__name__, 'notmuch') + if PY35: + self.assertEqual(notmuch.__qualname__, + 'TaskTests.test_task_repr_coro_decorator..notmuch') + self.assertEqual(notmuch.__module__, __name__) + + # test coroutine object + gen = notmuch() + if PY35: + # On Python >= 3.5, generators now inherit the name of the + # function, as expected, and have a qualified name (__qualname__ + # attribute). + coro_name = 'notmuch' + coro_qualname = 'TaskTests.test_task_repr_coro_decorator..notmuch' + elif tasks._DEBUG: + # In debug mode, @coroutine decorator uses CoroWrapper which gets + # its name (__name__ attribute) from the wrapped coroutine + # function. + coro_name = coro_qualname = 'notmuch' + else: + # On Python < 3.5, generators inherit the name of the code, not of + # the function. See: http://bugs.python.org/issue21205 + coro_name = coro_qualname = 'coro' + self.assertEqual(gen.__name__, coro_name) + if PY35: + self.assertEqual(gen.__qualname__, coro_qualname) + + # format the coroutine object + code = gen.gi_code + coro = ('%s() at %s:%s' + % (coro_qualname, code.co_filename, code.co_firstlineno)) + + # test pending Task t = asyncio.Task(gen, loop=self.loop) t.add_done_callback(Dummy()) self.assertEqual(repr(t), - 'Task()' % src) - - t.cancel() # Does not take immediate effect! - self.assertEqual(repr(t), - 'Task()' % src) - self.assertRaises(asyncio.CancelledError, - self.loop.run_until_complete, t) - self.assertEqual(repr(t), - 'Task()' - % (filename, lineno)) - - t = asyncio.Task(notmuch(), loop=self.loop) - self.loop.run_until_complete(t) - self.assertEqual(repr(t), - "Task()" - % (filename, lineno)) - - def test_task_repr_custom(self): - @asyncio.coroutine - def notmuch(): - pass - - self.assertEqual(notmuch.__name__, 'notmuch') - self.assertEqual(notmuch.__module__, __name__) - if PY35: - self.assertEqual(notmuch.__qualname__, - 'TaskTests.test_task_repr_custom..notmuch') - - class T(asyncio.Future): - def __repr__(self): - return 'T[]' - - class MyTask(asyncio.Task, T): - def __repr__(self): - return super().__repr__() - - gen = notmuch() - if PY35 or tasks._DEBUG: - # On Python >= 3.5, generators now inherit the name of the - # function, as expected, and have a qualified name (__qualname__ - # attribute). In debug mode, @coroutine decorator uses CoroWrapper - # which gets its name (__name__ attribute) from the wrapped - # coroutine function. - coro_name = 'notmuch' - else: - # On Python < 3.5, generators inherit the name of the code, not of - # the function. See: http://bugs.python.org/issue21205 - coro_name = 'coro' - self.assertEqual(gen.__name__, coro_name) - if PY35: - self.assertEqual(gen.__qualname__, - 'TaskTests.test_task_repr_custom..notmuch') - - t = MyTask(gen, loop=self.loop) - filename = gen.gi_code.co_filename - lineno = gen.gi_frame.f_lineno - self.assertEqual(repr(t), 'T[](<%s at %s:%s>)' % (coro_name, filename, lineno)) + '()]>' % coro) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 22:50:14 2014 From: python-checkins at python.org (ned.deily) Date: Wed, 25 Jun 2014 22:50:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxODEx?= =?utf-8?q?=3A_Anticipated_fixes_to_3=2Ex_and_2=2E7_for_OS_X_10=2E10_Yosem?= =?utf-8?q?ite=2E?= Message-ID: <3gzGhQ2X07z7LkC@mail.python.org> http://hg.python.org/cpython/rev/a7ab09e00dbc changeset: 91406:a7ab09e00dbc branch: 2.7 parent: 91403:893e79196fb3 user: Ned Deily date: Wed Jun 25 13:33:57 2014 -0700 summary: Issue #21811: Anticipated fixes to 3.x and 2.7 for OS X 10.10 Yosemite. files: Lib/_osx_support.py | 12 ++++++- Lib/distutils/tests/test_build_ext.py | 12 ++++++- Lib/test/test__osx_support.py | 4 +- Lib/test/test_posix.py | 2 +- Mac/BuildScript/build-installer.py | 24 ++++++++------ setup.py | 4 +- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -450,8 +450,16 @@ # case and disallow installs. cflags = _config_vars.get(_INITPRE+'CFLAGS', _config_vars.get('CFLAGS', '')) - if ((macrelease + '.') >= '10.4.' and - '-arch' in cflags.strip()): + if macrelease: + try: + macrelease = tuple(int(i) for i in macrelease.split('.')[0:2]) + except ValueError: + macrelease = (10, 0) + else: + # assume no universal support + macrelease = (10, 0) + + if (macrelease >= (10, 4)) and '-arch' in cflags.strip(): # The universal build will build fat binaries, but not on # systems before 10.4 diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py @@ -479,8 +479,16 @@ # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - target = tuple(map(int, target.split('.'))) - target = '%02d%01d0' % target + target = tuple(map(int, target.split('.')[0:2])) + # format the target value as defined in the Apple + # Availability Macros. We can't use the macro names since + # at least one value we test with will not exist yet. + if target[1] < 10: + # for 10.1 through 10.9.x -> "10n0" + target = '%02d%01d0' % target + else: + # for 10.10 and beyond -> "10nn00" + target = '%02d%02d00' % target deptarget_ext = Extension( 'deptarget', [deptarget_c], diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -109,7 +109,9 @@ def test__supports_universal_builds(self): import platform - self.assertEqual(platform.mac_ver()[0].split('.') >= ['10', '4'], + mac_ver_tuple = tuple(int(i) for i in + platform.mac_ver()[0].split('.')[0:2]) + self.assertEqual(mac_ver_tuple >= (10, 4), _osx_support._supports_universal_builds()) def test__find_appropriate_compiler(self): 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 @@ -520,7 +520,7 @@ if sys.platform == 'darwin': import sysconfig dt = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') or '10.0' - if float(dt) < 10.6: + if tuple(int(n) for n in dt.split('.')[0:2]) < (10, 6): raise unittest.SkipTest("getgroups(2) is broken prior to 10.6") # 'id -G' and 'os.getgroups()' should return the same 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 @@ -150,17 +150,19 @@ # $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level DEPTARGET = '10.3' -target_cc_map = { +def getDeptargetTuple(): + return tuple([int(n) for n in DEPTARGET.split('.')[0:2]]) + +def getTargetCompilers(): + target_cc_map = { '10.3': ('gcc-4.0', 'g++-4.0'), '10.4': ('gcc-4.0', 'g++-4.0'), '10.5': ('gcc-4.2', 'g++-4.2'), '10.6': ('gcc-4.2', 'g++-4.2'), - '10.7': ('clang', 'clang++'), - '10.8': ('clang', 'clang++'), - '10.9': ('clang', 'clang++'), -} + } + return target_cc_map.get(DEPTARGET, ('clang', 'clang++') ) -CC, CXX = target_cc_map[DEPTARGET] +CC, CXX = getTargetCompilers() PYTHON_3 = getVersionTuple() >= (3, 0) @@ -193,10 +195,10 @@ def library_recipes(): result = [] - LT_10_5 = bool(DEPTARGET < '10.5') + LT_10_5 = bool(getDeptargetTuple() < (10, 5)) # Disable for now - if False: # if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 5)): + if False: # if (getDeptargetTuple() > (10, 5)) and (getVersionTuple() >= (3, 5)): result.extend([ dict( name="Tcl 8.5.15", @@ -304,7 +306,7 @@ ), ]) - if DEPTARGET < '10.5': + if getDeptargetTuple() < (10, 5): result.extend([ dict( name="Bzip2 1.0.6", @@ -458,7 +460,7 @@ ) ) - if DEPTARGET < '10.4' and not PYTHON_3: + if getDeptargetTuple() < (10, 4) and not PYTHON_3: result.append( dict( name="PythonSystemFixes", @@ -679,7 +681,7 @@ SDKPATH=os.path.abspath(SDKPATH) DEPSRC=os.path.abspath(DEPSRC) - CC, CXX=target_cc_map[DEPTARGET] + CC, CXX = getTargetCompilers() print("Settings:") print(" * Source directory:", SRCDIR) diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -733,7 +733,9 @@ if host_platform == 'darwin': os_release = int(os.uname()[2].split('.')[0]) dep_target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - if dep_target and dep_target.split('.') < ['10', '5']: + if (dep_target and + (tuple(int(n) for n in dep_target.split('.')[0:2]) + < (10, 5) ) ): os_release = 8 if os_release < 9: # MacOSX 10.4 has a broken readline. Don't try to build -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 22:50:15 2014 From: python-checkins at python.org (ned.deily) Date: Wed, 25 Jun 2014 22:50:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxODEx?= =?utf-8?q?=3A_Anticipated_fixes_to_2=2E7_configure_for_OS_X_10=2E10_Yosem?= =?utf-8?q?ite=2E?= Message-ID: <3gzGhR5SDvz7LkG@mail.python.org> http://hg.python.org/cpython/rev/2672e30d9095 changeset: 91407:2672e30d9095 branch: 2.7 user: Ned Deily date: Wed Jun 25 13:48:46 2014 -0700 summary: Issue #21811: Anticipated fixes to 2.7 configure for OS X 10.10 Yosemite. files: configure | 33 +++++++++++++++++++++++---------- configure.ac | 33 +++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/configure b/configure --- a/configure +++ b/configure @@ -6045,8 +6045,14 @@ # Calculate the right deployment target for this build. # - cur_target=`sw_vers -productVersion | sed 's/\(10\.[0-9]*\).*/\1/'` - if test ${cur_target} '>' 10.2; then + cur_target_major=`sw_vers -productVersion | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\1/'` + cur_target_minor=`sw_vers -productVersion | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\2/'` + cur_target="${cur_target_major}.${cur_target_minor}" + if test ${cur_target_major} -eq 10 && \ + test ${cur_target_minor} -ge 3 + then cur_target=10.3 if test ${enable_universalsdk}; then if test "${UNIVERSAL_ARCHS}" = "all"; then @@ -8211,15 +8217,14 @@ # Use -undefined dynamic_lookup whenever possible (10.3 and later). # This allows an extension to be used in any Python - if test ${MACOSX_DEPLOYMENT_TARGET} '>' 10.2 + dep_target_major=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\1/'` + dep_target_minor=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\2/'` + if test ${dep_target_major} -eq 10 && \ + test ${dep_target_minor} -le 2 then - if test "${enable_universalsdk}"; then - LDFLAGS="${UNIVERSAL_ARCH_FLAGS} -isysroot ${UNIVERSALSDK} ${LDFLAGS}" - fi - LDSHARED='$(CC) -bundle -undefined dynamic_lookup' - LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' - BLDSHARED="$LDSHARED" - else + # building for OS X 10.0 through 10.2 LDSHARED='$(CC) -bundle' LDCXXSHARED='$(CXX) -bundle' if test "$enable_framework" ; then @@ -8233,6 +8238,14 @@ LDSHARED="$LDSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' LDCXXSHARED="$LDCXXSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' fi + else + # building for OS X 10.3 and later + if test "${enable_universalsdk}"; then + LDFLAGS="${UNIVERSAL_ARCH_FLAGS} -isysroot ${UNIVERSALSDK} ${LDFLAGS}" + fi + LDSHARED='$(CC) -bundle -undefined dynamic_lookup' + LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' + BLDSHARED="$LDSHARED" fi ;; Linux*|GNU*|QNX*) diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -1149,8 +1149,14 @@ # Calculate the right deployment target for this build. # - cur_target=`sw_vers -productVersion | sed 's/\(10\.[[0-9]]*\).*/\1/'` - if test ${cur_target} '>' 10.2; then + cur_target_major=`sw_vers -productVersion | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` + cur_target_minor=`sw_vers -productVersion | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` + cur_target="${cur_target_major}.${cur_target_minor}" + if test ${cur_target_major} -eq 10 && \ + test ${cur_target_minor} -ge 3 + then cur_target=10.3 if test ${enable_universalsdk}; then if test "${UNIVERSAL_ARCHS}" = "all"; then @@ -1993,15 +1999,14 @@ # Use -undefined dynamic_lookup whenever possible (10.3 and later). # This allows an extension to be used in any Python - if test ${MACOSX_DEPLOYMENT_TARGET} '>' 10.2 + dep_target_major=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` + dep_target_minor=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` + if test ${dep_target_major} -eq 10 && \ + test ${dep_target_minor} -le 2 then - if test "${enable_universalsdk}"; then - LDFLAGS="${UNIVERSAL_ARCH_FLAGS} -isysroot ${UNIVERSALSDK} ${LDFLAGS}" - fi - LDSHARED='$(CC) -bundle -undefined dynamic_lookup' - LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' - BLDSHARED="$LDSHARED" - else + # building for OS X 10.0 through 10.2 LDSHARED='$(CC) -bundle' LDCXXSHARED='$(CXX) -bundle' if test "$enable_framework" ; then @@ -2015,6 +2020,14 @@ LDSHARED="$LDSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' LDCXXSHARED="$LDCXXSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' fi + else + # building for OS X 10.3 and later + if test "${enable_universalsdk}"; then + LDFLAGS="${UNIVERSAL_ARCH_FLAGS} -isysroot ${UNIVERSALSDK} ${LDFLAGS}" + fi + LDSHARED='$(CC) -bundle -undefined dynamic_lookup' + LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' + BLDSHARED="$LDSHARED" fi ;; Linux*|GNU*|QNX*) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 22:50:17 2014 From: python-checkins at python.org (ned.deily) Date: Wed, 25 Jun 2014 22:50:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxODEx?= =?utf-8?q?=3A_Anticipated_fixes_to_3=2Ex_and_2=2E7_for_OS_X_10=2E10_Yosem?= =?utf-8?q?ite=2E?= Message-ID: <3gzGhT19Q1z7LkW@mail.python.org> http://hg.python.org/cpython/rev/14198fda1c70 changeset: 91408:14198fda1c70 branch: 3.4 parent: 91404:dead2b526c3b user: Ned Deily date: Wed Jun 25 13:36:14 2014 -0700 summary: Issue #21811: Anticipated fixes to 3.x and 2.7 for OS X 10.10 Yosemite. files: Lib/_osx_support.py | 12 ++++++- Lib/distutils/tests/test_build_ext.py | 12 ++++++- Lib/test/test__osx_support.py | 4 +- Lib/test/test_posix.py | 2 +- Mac/BuildScript/build-installer.py | 24 ++++++++------ setup.py | 4 +- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -450,8 +450,16 @@ # case and disallow installs. cflags = _config_vars.get(_INITPRE+'CFLAGS', _config_vars.get('CFLAGS', '')) - if ((macrelease + '.') >= '10.4.' and - '-arch' in cflags.strip()): + if macrelease: + try: + macrelease = tuple(int(i) for i in macrelease.split('.')[0:2]) + except ValueError: + macrelease = (10, 0) + else: + # assume no universal support + macrelease = (10, 0) + + if (macrelease >= (10, 4)) and '-arch' in cflags.strip(): # The universal build will build fat binaries, but not on # systems before 10.4 diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py @@ -444,8 +444,16 @@ # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - target = tuple(map(int, target.split('.'))) - target = '%02d%01d0' % target + target = tuple(map(int, target.split('.')[0:2])) + # format the target value as defined in the Apple + # Availability Macros. We can't use the macro names since + # at least one value we test with will not exist yet. + if target[1] < 10: + # for 10.1 through 10.9.x -> "10n0" + target = '%02d%01d0' % target + else: + # for 10.10 and beyond -> "10nn00" + target = '%02d%02d00' % target deptarget_ext = Extension( 'deptarget', [deptarget_c], diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -109,7 +109,9 @@ def test__supports_universal_builds(self): import platform - self.assertEqual(platform.mac_ver()[0].split('.') >= ['10', '4'], + mac_ver_tuple = tuple(int(i) for i in + platform.mac_ver()[0].split('.')[0:2]) + self.assertEqual(mac_ver_tuple >= (10, 4), _osx_support._supports_universal_builds()) def test__find_appropriate_compiler(self): 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 @@ -768,7 +768,7 @@ if sys.platform == 'darwin': import sysconfig dt = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') or '10.0' - if float(dt) < 10.6: + if tuple(int(n) for n in dt.split('.')[0:2]) < (10, 6): raise unittest.SkipTest("getgroups(2) is broken prior to 10.6") # 'id -G' and 'os.getgroups()' should return the same 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 @@ -150,17 +150,19 @@ # $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level DEPTARGET = '10.3' -target_cc_map = { +def getDeptargetTuple(): + return tuple([int(n) for n in DEPTARGET.split('.')[0:2]]) + +def getTargetCompilers(): + target_cc_map = { '10.3': ('gcc-4.0', 'g++-4.0'), '10.4': ('gcc-4.0', 'g++-4.0'), '10.5': ('gcc-4.2', 'g++-4.2'), '10.6': ('gcc-4.2', 'g++-4.2'), - '10.7': ('clang', 'clang++'), - '10.8': ('clang', 'clang++'), - '10.9': ('clang', 'clang++'), -} + } + return target_cc_map.get(DEPTARGET, ('clang', 'clang++') ) -CC, CXX = target_cc_map[DEPTARGET] +CC, CXX = getTargetCompilers() PYTHON_3 = getVersionTuple() >= (3, 0) @@ -193,10 +195,10 @@ def library_recipes(): result = [] - LT_10_5 = bool(DEPTARGET < '10.5') + LT_10_5 = bool(getDeptargetTuple() < (10, 5)) # Disable for now - if False: # if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 5)): + if False: # if (getDeptargetTuple() > (10, 5)) and (getVersionTuple() >= (3, 5)): result.extend([ dict( name="Tcl 8.5.15", @@ -304,7 +306,7 @@ ), ]) - if DEPTARGET < '10.5': + if getDeptargetTuple() < (10, 5): result.extend([ dict( name="Bzip2 1.0.6", @@ -458,7 +460,7 @@ ) ) - if DEPTARGET < '10.4' and not PYTHON_3: + if getDeptargetTuple() < (10, 4) and not PYTHON_3: result.append( dict( name="PythonSystemFixes", @@ -679,7 +681,7 @@ SDKPATH=os.path.abspath(SDKPATH) DEPSRC=os.path.abspath(DEPSRC) - CC, CXX=target_cc_map[DEPTARGET] + CC, CXX = getTargetCompilers() print("Settings:") print(" * Source directory:", SRCDIR) diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -697,7 +697,9 @@ if host_platform == 'darwin': os_release = int(os.uname()[2].split('.')[0]) dep_target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - if dep_target and dep_target.split('.') < ['10', '5']: + if (dep_target and + (tuple(int(n) for n in dep_target.split('.')[0:2]) + < (10, 5) ) ): os_release = 8 if os_release < 9: # MacOSX 10.4 has a broken readline. Don't try to build -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 22:50:18 2014 From: python-checkins at python.org (ned.deily) Date: Wed, 25 Jun 2014 22:50:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321811=3A_Anticipated_fixes_to_3=2Ex_and_2=2E7_f?= =?utf-8?q?or_OS_X_10=2E10_Yosemite=2E?= Message-ID: <3gzGhV4871z7Lkm@mail.python.org> http://hg.python.org/cpython/rev/3583b2bedbe7 changeset: 91409:3583b2bedbe7 parent: 91405:df8788082d2f parent: 91408:14198fda1c70 user: Ned Deily date: Wed Jun 25 13:42:22 2014 -0700 summary: Issue #21811: Anticipated fixes to 3.x and 2.7 for OS X 10.10 Yosemite. files: Lib/_osx_support.py | 12 ++++++- Lib/distutils/tests/test_build_ext.py | 12 ++++++- Lib/test/test__osx_support.py | 4 +- Lib/test/test_posix.py | 2 +- Mac/BuildScript/build-installer.py | 24 ++++++++------ setup.py | 4 +- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -450,8 +450,16 @@ # case and disallow installs. cflags = _config_vars.get(_INITPRE+'CFLAGS', _config_vars.get('CFLAGS', '')) - if ((macrelease + '.') >= '10.4.' and - '-arch' in cflags.strip()): + if macrelease: + try: + macrelease = tuple(int(i) for i in macrelease.split('.')[0:2]) + except ValueError: + macrelease = (10, 0) + else: + # assume no universal support + macrelease = (10, 0) + + if (macrelease >= (10, 4)) and '-arch' in cflags.strip(): # The universal build will build fat binaries, but not on # systems before 10.4 diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py @@ -444,8 +444,16 @@ # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - target = tuple(map(int, target.split('.'))) - target = '%02d%01d0' % target + target = tuple(map(int, target.split('.')[0:2])) + # format the target value as defined in the Apple + # Availability Macros. We can't use the macro names since + # at least one value we test with will not exist yet. + if target[1] < 10: + # for 10.1 through 10.9.x -> "10n0" + target = '%02d%01d0' % target + else: + # for 10.10 and beyond -> "10nn00" + target = '%02d%02d00' % target deptarget_ext = Extension( 'deptarget', [deptarget_c], diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -109,7 +109,9 @@ def test__supports_universal_builds(self): import platform - self.assertEqual(platform.mac_ver()[0].split('.') >= ['10', '4'], + mac_ver_tuple = tuple(int(i) for i in + platform.mac_ver()[0].split('.')[0:2]) + self.assertEqual(mac_ver_tuple >= (10, 4), _osx_support._supports_universal_builds()) def test__find_appropriate_compiler(self): 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 @@ -768,7 +768,7 @@ if sys.platform == 'darwin': import sysconfig dt = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') or '10.0' - if float(dt) < 10.6: + if tuple(int(n) for n in dt.split('.')[0:2]) < (10, 6): raise unittest.SkipTest("getgroups(2) is broken prior to 10.6") # 'id -G' and 'os.getgroups()' should return the same 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 @@ -150,17 +150,19 @@ # $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level DEPTARGET = '10.3' -target_cc_map = { +def getDeptargetTuple(): + return tuple([int(n) for n in DEPTARGET.split('.')[0:2]]) + +def getTargetCompilers(): + target_cc_map = { '10.3': ('gcc-4.0', 'g++-4.0'), '10.4': ('gcc-4.0', 'g++-4.0'), '10.5': ('gcc-4.2', 'g++-4.2'), '10.6': ('gcc-4.2', 'g++-4.2'), - '10.7': ('clang', 'clang++'), - '10.8': ('clang', 'clang++'), - '10.9': ('clang', 'clang++'), -} + } + return target_cc_map.get(DEPTARGET, ('clang', 'clang++') ) -CC, CXX = target_cc_map[DEPTARGET] +CC, CXX = getTargetCompilers() PYTHON_3 = getVersionTuple() >= (3, 0) @@ -193,10 +195,10 @@ def library_recipes(): result = [] - LT_10_5 = bool(DEPTARGET < '10.5') + LT_10_5 = bool(getDeptargetTuple() < (10, 5)) # Disable for now - if False: # if (DEPTARGET > '10.5') and (getVersionTuple() >= (3, 5)): + if False: # if (getDeptargetTuple() > (10, 5)) and (getVersionTuple() >= (3, 5)): result.extend([ dict( name="Tcl 8.5.15", @@ -304,7 +306,7 @@ ), ]) - if DEPTARGET < '10.5': + if getDeptargetTuple() < (10, 5): result.extend([ dict( name="Bzip2 1.0.6", @@ -458,7 +460,7 @@ ) ) - if DEPTARGET < '10.4' and not PYTHON_3: + if getDeptargetTuple() < (10, 4) and not PYTHON_3: result.append( dict( name="PythonSystemFixes", @@ -679,7 +681,7 @@ SDKPATH=os.path.abspath(SDKPATH) DEPSRC=os.path.abspath(DEPSRC) - CC, CXX=target_cc_map[DEPTARGET] + CC, CXX = getTargetCompilers() print("Settings:") print(" * Source directory:", SRCDIR) diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -707,7 +707,9 @@ if host_platform == 'darwin': os_release = int(os.uname()[2].split('.')[0]) dep_target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - if dep_target and dep_target.split('.') < ['10', '5']: + if (dep_target and + (tuple(int(n) for n in dep_target.split('.')[0:2]) + < (10, 5) ) ): os_release = 8 if os_release < 9: # MacOSX 10.4 has a broken readline. Don't try to build -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 22:50:19 2014 From: python-checkins at python.org (ned.deily) Date: Wed, 25 Jun 2014 22:50:19 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxODEx?= =?utf-8?q?=3A_Anticipated_fixes_to_3=2Ex_configure_for_OS_X_10=2E10_Yosem?= =?utf-8?q?ite=2E?= Message-ID: <3gzGhW71FFz7LkG@mail.python.org> http://hg.python.org/cpython/rev/69ae7e4939f2 changeset: 91410:69ae7e4939f2 branch: 3.4 parent: 91408:14198fda1c70 user: Ned Deily date: Wed Jun 25 13:44:22 2014 -0700 summary: Issue #21811: Anticipated fixes to 3.x configure for OS X 10.10 Yosemite. files: configure | 29 +++++++++++++++++++++-------- configure.ac | 29 +++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/configure b/configure --- a/configure +++ b/configure @@ -6551,10 +6551,16 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: checking which MACOSX_DEPLOYMENT_TARGET to use" >&5 $as_echo_n "checking which MACOSX_DEPLOYMENT_TARGET to use... " >&6; } - cur_target=`sw_vers -productVersion | sed 's/\(10\.[0-9]*\).*/\1/'` - if test ${cur_target} '>' 10.2 && \ - test ${cur_target} '<' 10.6 + cur_target_major=`sw_vers -productVersion | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\1/'` + cur_target_minor=`sw_vers -productVersion | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\2/'` + cur_target="${cur_target_major}.${cur_target_minor}" + if test ${cur_target_major} -eq 10 && \ + test ${cur_target_minor} -ge 3 && \ + test ${cur_target_minor} -le 5 then + # OS X 10.3 through 10.5 cur_target=10.3 if test ${enable_universalsdk} then @@ -8540,12 +8546,14 @@ # Use -undefined dynamic_lookup whenever possible (10.3 and later). # This allows an extension to be used in any Python - if test ${MACOSX_DEPLOYMENT_TARGET} '>' 10.2 + dep_target_major=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\1/'` + dep_target_minor=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\2/'` + if test ${dep_target_major} -eq 10 && \ + test ${dep_target_minor} -le 2 then - LDSHARED='$(CC) -bundle -undefined dynamic_lookup' - LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' - BLDSHARED="$LDSHARED" - else + # building for OS X 10.0 through 10.2 LDSHARED='$(CC) -bundle' LDCXXSHARED='$(CXX) -bundle' if test "$enable_framework" ; then @@ -8559,6 +8567,11 @@ LDSHARED="$LDSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' LDCXXSHARED="$LDCXXSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' fi + else + # building for OS X 10.3 and later + LDSHARED='$(CC) -bundle -undefined dynamic_lookup' + LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' + BLDSHARED="$LDSHARED" fi ;; Linux*|GNU*|QNX*) diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -1318,10 +1318,16 @@ # 4. If we are running on OS X 10.2 or earlier, good luck! AC_MSG_CHECKING(which MACOSX_DEPLOYMENT_TARGET to use) - cur_target=`sw_vers -productVersion | sed 's/\(10\.[[0-9]]*\).*/\1/'` - if test ${cur_target} '>' 10.2 && \ - test ${cur_target} '<' 10.6 + cur_target_major=`sw_vers -productVersion | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` + cur_target_minor=`sw_vers -productVersion | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` + cur_target="${cur_target_major}.${cur_target_minor}" + if test ${cur_target_major} -eq 10 && \ + test ${cur_target_minor} -ge 3 && \ + test ${cur_target_minor} -le 5 then + # OS X 10.3 through 10.5 cur_target=10.3 if test ${enable_universalsdk} then @@ -2017,12 +2023,14 @@ # Use -undefined dynamic_lookup whenever possible (10.3 and later). # This allows an extension to be used in any Python - if test ${MACOSX_DEPLOYMENT_TARGET} '>' 10.2 + dep_target_major=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` + dep_target_minor=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` + if test ${dep_target_major} -eq 10 && \ + test ${dep_target_minor} -le 2 then - LDSHARED='$(CC) -bundle -undefined dynamic_lookup' - LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' - BLDSHARED="$LDSHARED" - else + # building for OS X 10.0 through 10.2 LDSHARED='$(CC) -bundle' LDCXXSHARED='$(CXX) -bundle' if test "$enable_framework" ; then @@ -2036,6 +2044,11 @@ LDSHARED="$LDSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' LDCXXSHARED="$LDCXXSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' fi + else + # building for OS X 10.3 and later + LDSHARED='$(CC) -bundle -undefined dynamic_lookup' + LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' + BLDSHARED="$LDSHARED" fi ;; Linux*|GNU*|QNX*) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 22:50:21 2014 From: python-checkins at python.org (ned.deily) Date: Wed, 25 Jun 2014 22:50:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321811=3A_Anticipated_fixes_to_3=2Ex_configure_f?= =?utf-8?q?or_OS_X_10=2E10_Yosemite=2E?= Message-ID: <3gzGhY2tXMz7LkG@mail.python.org> http://hg.python.org/cpython/rev/7346ba934097 changeset: 91411:7346ba934097 parent: 91409:3583b2bedbe7 parent: 91410:69ae7e4939f2 user: Ned Deily date: Wed Jun 25 13:46:33 2014 -0700 summary: Issue #21811: Anticipated fixes to 3.x configure for OS X 10.10 Yosemite. files: configure | 29 +++++++++++++++++++++-------- configure.ac | 29 +++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/configure b/configure --- a/configure +++ b/configure @@ -6606,10 +6606,16 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: checking which MACOSX_DEPLOYMENT_TARGET to use" >&5 $as_echo_n "checking which MACOSX_DEPLOYMENT_TARGET to use... " >&6; } - cur_target=`sw_vers -productVersion | sed 's/\(10\.[0-9]*\).*/\1/'` - if test ${cur_target} '>' 10.2 && \ - test ${cur_target} '<' 10.6 + cur_target_major=`sw_vers -productVersion | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\1/'` + cur_target_minor=`sw_vers -productVersion | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\2/'` + cur_target="${cur_target_major}.${cur_target_minor}" + if test ${cur_target_major} -eq 10 && \ + test ${cur_target_minor} -ge 3 && \ + test ${cur_target_minor} -le 5 then + # OS X 10.3 through 10.5 cur_target=10.3 if test ${enable_universalsdk} then @@ -8595,12 +8601,14 @@ # Use -undefined dynamic_lookup whenever possible (10.3 and later). # This allows an extension to be used in any Python - if test ${MACOSX_DEPLOYMENT_TARGET} '>' 10.2 + dep_target_major=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\1/'` + dep_target_minor=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\2/'` + if test ${dep_target_major} -eq 10 && \ + test ${dep_target_minor} -le 2 then - LDSHARED='$(CC) -bundle -undefined dynamic_lookup' - LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' - BLDSHARED="$LDSHARED" - else + # building for OS X 10.0 through 10.2 LDSHARED='$(CC) -bundle' LDCXXSHARED='$(CXX) -bundle' if test "$enable_framework" ; then @@ -8614,6 +8622,11 @@ LDSHARED="$LDSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' LDCXXSHARED="$LDCXXSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' fi + else + # building for OS X 10.3 and later + LDSHARED='$(CC) -bundle -undefined dynamic_lookup' + LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' + BLDSHARED="$LDSHARED" fi ;; Linux*|GNU*|QNX*) diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -1327,10 +1327,16 @@ # 4. If we are running on OS X 10.2 or earlier, good luck! AC_MSG_CHECKING(which MACOSX_DEPLOYMENT_TARGET to use) - cur_target=`sw_vers -productVersion | sed 's/\(10\.[[0-9]]*\).*/\1/'` - if test ${cur_target} '>' 10.2 && \ - test ${cur_target} '<' 10.6 + cur_target_major=`sw_vers -productVersion | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` + cur_target_minor=`sw_vers -productVersion | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` + cur_target="${cur_target_major}.${cur_target_minor}" + if test ${cur_target_major} -eq 10 && \ + test ${cur_target_minor} -ge 3 && \ + test ${cur_target_minor} -le 5 then + # OS X 10.3 through 10.5 cur_target=10.3 if test ${enable_universalsdk} then @@ -2026,12 +2032,14 @@ # Use -undefined dynamic_lookup whenever possible (10.3 and later). # This allows an extension to be used in any Python - if test ${MACOSX_DEPLOYMENT_TARGET} '>' 10.2 + dep_target_major=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` + dep_target_minor=`echo ${MACOSX_DEPLOYMENT_TARGET} | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` + if test ${dep_target_major} -eq 10 && \ + test ${dep_target_minor} -le 2 then - LDSHARED='$(CC) -bundle -undefined dynamic_lookup' - LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' - BLDSHARED="$LDSHARED" - else + # building for OS X 10.0 through 10.2 LDSHARED='$(CC) -bundle' LDCXXSHARED='$(CXX) -bundle' if test "$enable_framework" ; then @@ -2045,6 +2053,11 @@ LDSHARED="$LDSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' LDCXXSHARED="$LDCXXSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' fi + else + # building for OS X 10.3 and later + LDSHARED='$(CC) -bundle -undefined dynamic_lookup' + LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' + BLDSHARED="$LDSHARED" fi ;; Linux*|GNU*|QNX*) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 23:14:11 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 25 Jun 2014 23:14:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxMTYz?= =?utf-8?q?=2C_asyncio=3A_Fix_some_=22Task_was_destroyed_but_it_is_pending?= =?utf-8?q?!=22_logs_in?= Message-ID: <3gzHD33hPvz7Ljl@mail.python.org> http://hg.python.org/cpython/rev/1088023d971c changeset: 91412:1088023d971c branch: 3.4 parent: 91410:69ae7e4939f2 user: Victor Stinner date: Wed Jun 25 23:11:21 2014 +0200 summary: Issue #21163, asyncio: Fix some "Task was destroyed but it is pending!" logs in tests files: Lib/test/test_asyncio/test_locks.py | 1 + Lib/test/test_asyncio/test_queues.py | 27 ++++++++++----- Lib/test/test_asyncio/test_tasks.py | 10 ++--- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -783,6 +783,7 @@ # cleanup locked semaphore sem.release() + self.loop.run_until_complete(t4) def test_acquire_cancel(self): sem = asyncio.Semaphore(loop=self.loop) diff --git a/Lib/test/test_asyncio/test_queues.py b/Lib/test/test_asyncio/test_queues.py --- a/Lib/test/test_asyncio/test_queues.py +++ b/Lib/test/test_asyncio/test_queues.py @@ -362,16 +362,21 @@ def test_put_cancelled_race(self): q = asyncio.Queue(loop=self.loop, maxsize=1) - asyncio.Task(q.put('a'), loop=self.loop) - asyncio.Task(q.put('c'), loop=self.loop) - t = asyncio.Task(q.put('b'), loop=self.loop) + put_a = asyncio.Task(q.put('a'), loop=self.loop) + put_b = asyncio.Task(q.put('b'), loop=self.loop) + put_c = asyncio.Task(q.put('X'), loop=self.loop) test_utils.run_briefly(self.loop) - t.cancel() + self.assertTrue(put_a.done()) + self.assertFalse(put_b.done()) + + put_c.cancel() test_utils.run_briefly(self.loop) - self.assertTrue(t.done()) + self.assertTrue(put_c.done()) self.assertEqual(q.get_nowait(), 'a') - self.assertEqual(q.get_nowait(), 'c') + self.assertEqual(q.get_nowait(), 'b') + + self.loop.run_until_complete(put_b) def test_put_with_waiting_getters(self): q = asyncio.Queue(loop=self.loop) @@ -431,18 +436,20 @@ @asyncio.coroutine def test(): - for _ in range(2): - asyncio.Task(worker(), loop=self.loop) + tasks = [asyncio.Task(worker(), loop=self.loop) + for index in range(2)] yield from q.join() + return tasks - self.loop.run_until_complete(test()) + tasks = self.loop.run_until_complete(test()) self.assertEqual(sum(range(100)), accumulator) # close running generators running = False - for i in range(2): + for i in range(len(tasks)): q.put_nowait(0) + self.loop.run_until_complete(asyncio.wait(tasks, loop=self.loop)) def test_join_empty_queue(self): q = asyncio.JoinableQueue(loop=self.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 @@ -1763,16 +1763,14 @@ gen2 = coro() fut = asyncio.gather(gen1, gen2) self.assertIs(fut._loop, self.one_loop) - gen1.close() - gen2.close() + self.one_loop.run_until_complete(fut) self.set_event_loop(self.other_loop, cleanup=False) gen3 = coro() gen4 = coro() - fut = asyncio.gather(gen3, gen4, loop=self.other_loop) - self.assertIs(fut._loop, self.other_loop) - gen3.close() - gen4.close() + fut2 = asyncio.gather(gen3, gen4, loop=self.other_loop) + self.assertIs(fut2._loop, self.other_loop) + self.other_loop.run_until_complete(fut2) def test_duplicate_coroutines(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 23:14:12 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 25 Jun 2014 23:14:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321163=2C_asyncio=3A_Fix_some_?= =?utf-8?q?=22Task_was_destroyed_but_it_is?= Message-ID: <3gzHD46T2Kz7LkC@mail.python.org> http://hg.python.org/cpython/rev/7877aab90c61 changeset: 91413:7877aab90c61 parent: 91411:7346ba934097 parent: 91412:1088023d971c user: Victor Stinner date: Wed Jun 25 23:12:58 2014 +0200 summary: (Merge 3.4) Issue #21163, asyncio: Fix some "Task was destroyed but it is pending!" logs in tests files: Lib/test/test_asyncio/test_locks.py | 1 + Lib/test/test_asyncio/test_queues.py | 27 ++++++++++----- Lib/test/test_asyncio/test_tasks.py | 10 ++--- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -783,6 +783,7 @@ # cleanup locked semaphore sem.release() + self.loop.run_until_complete(t4) def test_acquire_cancel(self): sem = asyncio.Semaphore(loop=self.loop) diff --git a/Lib/test/test_asyncio/test_queues.py b/Lib/test/test_asyncio/test_queues.py --- a/Lib/test/test_asyncio/test_queues.py +++ b/Lib/test/test_asyncio/test_queues.py @@ -362,16 +362,21 @@ def test_put_cancelled_race(self): q = asyncio.Queue(loop=self.loop, maxsize=1) - asyncio.Task(q.put('a'), loop=self.loop) - asyncio.Task(q.put('c'), loop=self.loop) - t = asyncio.Task(q.put('b'), loop=self.loop) + put_a = asyncio.Task(q.put('a'), loop=self.loop) + put_b = asyncio.Task(q.put('b'), loop=self.loop) + put_c = asyncio.Task(q.put('X'), loop=self.loop) test_utils.run_briefly(self.loop) - t.cancel() + self.assertTrue(put_a.done()) + self.assertFalse(put_b.done()) + + put_c.cancel() test_utils.run_briefly(self.loop) - self.assertTrue(t.done()) + self.assertTrue(put_c.done()) self.assertEqual(q.get_nowait(), 'a') - self.assertEqual(q.get_nowait(), 'c') + self.assertEqual(q.get_nowait(), 'b') + + self.loop.run_until_complete(put_b) def test_put_with_waiting_getters(self): q = asyncio.Queue(loop=self.loop) @@ -431,18 +436,20 @@ @asyncio.coroutine def test(): - for _ in range(2): - asyncio.Task(worker(), loop=self.loop) + tasks = [asyncio.Task(worker(), loop=self.loop) + for index in range(2)] yield from q.join() + return tasks - self.loop.run_until_complete(test()) + tasks = self.loop.run_until_complete(test()) self.assertEqual(sum(range(100)), accumulator) # close running generators running = False - for i in range(2): + for i in range(len(tasks)): q.put_nowait(0) + self.loop.run_until_complete(asyncio.wait(tasks, loop=self.loop)) def test_join_empty_queue(self): q = asyncio.JoinableQueue(loop=self.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 @@ -1763,16 +1763,14 @@ gen2 = coro() fut = asyncio.gather(gen1, gen2) self.assertIs(fut._loop, self.one_loop) - gen1.close() - gen2.close() + self.one_loop.run_until_complete(fut) self.set_event_loop(self.other_loop, cleanup=False) gen3 = coro() gen4 = coro() - fut = asyncio.gather(gen3, gen4, loop=self.other_loop) - self.assertIs(fut._loop, self.other_loop) - gen3.close() - gen4.close() + fut2 = asyncio.gather(gen3, gen4, loop=self.other_loop) + self.assertIs(fut2._loop, self.other_loop) + self.other_loop.run_until_complete(fut2) def test_duplicate_coroutines(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 23:34:49 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 25 Jun 2014 23:34:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogc3lu?= =?utf-8?q?c_with_Tulip?= Message-ID: <3gzHgs2R9jz7LjP@mail.python.org> http://hg.python.org/cpython/rev/e9150fdf068a changeset: 91414:e9150fdf068a branch: 3.4 parent: 91412:1088023d971c user: Victor Stinner date: Wed Jun 25 23:32:25 2014 +0200 summary: asyncio: sync with Tulip - Python issue 21163: Fix more "Task was destroyed but it is pending!" logs in tests - Add test to check that run_until_complete() checks the loop of the future files: Lib/test/test_asyncio/test_base_events.py | 6 ++++++ Lib/test/test_asyncio/test_tasks.py | 3 +++ 2 files changed, 9 insertions(+), 0 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 @@ -288,6 +288,12 @@ self.assertRaises(TypeError, self.loop.run_until_complete, 'blah') + def test_run_until_complete_loop(self): + task = asyncio.Future(loop=self.loop) + other_loop = self.new_test_loop() + self.assertRaises(ValueError, + other_loop.run_until_complete, task) + def test_subprocess_exec_invalid_args(self): args = [sys.executable, '-c', 'pass'] 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 @@ -51,6 +51,7 @@ self.set_event_loop(loop) t = asyncio.Task(notmuch(), loop=loop) self.assertIs(t._loop, loop) + loop.run_until_complete(t) loop.close() def test_async_coroutine(self): @@ -67,6 +68,7 @@ self.set_event_loop(loop) t = asyncio.async(notmuch(), loop=loop) self.assertIs(t._loop, loop) + loop.run_until_complete(t) loop.close() def test_async_future(self): @@ -213,6 +215,7 @@ t.add_done_callback(Dummy()) self.assertEqual(repr(t), '()]>' % coro) + self.loop.run_until_complete(t) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Wed Jun 25 23:34:50 2014 From: python-checkins at python.org (victor.stinner) Date: Wed, 25 Jun 2014 23:34:50 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_sync_with_Tulip?= Message-ID: <3gzHgt3m6Zz7Ljy@mail.python.org> http://hg.python.org/cpython/rev/d92dc4462d26 changeset: 91415:d92dc4462d26 parent: 91413:7877aab90c61 parent: 91414:e9150fdf068a user: Victor Stinner date: Wed Jun 25 23:33:02 2014 +0200 summary: (Merge 3.4) asyncio: sync with Tulip - Python issue 21163: Fix more "Task was destroyed but it is pending!" logs in tests - Add test to check that run_until_complete() checks the loop of the future files: Lib/test/test_asyncio/test_base_events.py | 6 ++++++ Lib/test/test_asyncio/test_tasks.py | 3 +++ 2 files changed, 9 insertions(+), 0 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 @@ -288,6 +288,12 @@ self.assertRaises(TypeError, self.loop.run_until_complete, 'blah') + def test_run_until_complete_loop(self): + task = asyncio.Future(loop=self.loop) + other_loop = self.new_test_loop() + self.assertRaises(ValueError, + other_loop.run_until_complete, task) + def test_subprocess_exec_invalid_args(self): args = [sys.executable, '-c', 'pass'] 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 @@ -51,6 +51,7 @@ self.set_event_loop(loop) t = asyncio.Task(notmuch(), loop=loop) self.assertIs(t._loop, loop) + loop.run_until_complete(t) loop.close() def test_async_coroutine(self): @@ -67,6 +68,7 @@ self.set_event_loop(loop) t = asyncio.async(notmuch(), loop=loop) self.assertIs(t._loop, loop) + loop.run_until_complete(t) loop.close() def test_async_future(self): @@ -213,6 +215,7 @@ t.add_done_callback(Dummy()) self.assertEqual(repr(t), '()]>' % coro) + self.loop.run_until_complete(t) def test_task_basics(self): @asyncio.coroutine -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 00:00:41 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 26 Jun 2014 00:00:41 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxMTYz?= =?utf-8?q?=3A_Fix_one_more_=22Task_was_destroyed_but_it_is_pending!=22_lo?= =?utf-8?q?g_in_tests?= Message-ID: <3gzJFj12Kvz7LkK@mail.python.org> http://hg.python.org/cpython/rev/4e4c6e2ed0c5 changeset: 91416:4e4c6e2ed0c5 branch: 3.4 parent: 91414:e9150fdf068a user: Victor Stinner date: Wed Jun 25 23:57:50 2014 +0200 summary: Issue #21163: Fix one more "Task was destroyed but it is pending!" log in tests files: Lib/test/test_asyncio/test_tasks.py | 8 ++++++-- 1 files changed, 6 insertions(+), 2 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 @@ -411,8 +411,10 @@ loop.stop() t = asyncio.Task(task(), loop=loop) - self.assertRaises( - RuntimeError, loop.run_until_complete, t) + with self.assertRaises(RuntimeError) as cm: + loop.run_until_complete(t) + self.assertEqual(str(cm.exception), + 'Event loop stopped before Future completed.') self.assertFalse(t.done()) self.assertEqual(x, 2) self.assertAlmostEqual(0.3, loop.time()) @@ -420,6 +422,8 @@ # close generators for w in waiters: w.close() + t.cancel() + self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t) def test_wait_for(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 00:00:42 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 26 Jun 2014 00:00:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321163=3A_Fix_one_more_=22Task?= =?utf-8?q?_was_destroyed_but_it_is_pending!=22?= Message-ID: <3gzJFk3DNZz7Ljk@mail.python.org> http://hg.python.org/cpython/rev/24282c6f6019 changeset: 91417:24282c6f6019 parent: 91415:d92dc4462d26 parent: 91416:4e4c6e2ed0c5 user: Victor Stinner date: Wed Jun 25 23:59:31 2014 +0200 summary: (Merge 3.4) Issue #21163: Fix one more "Task was destroyed but it is pending!" log in tests files: Lib/test/test_asyncio/test_tasks.py | 8 ++++++-- 1 files changed, 6 insertions(+), 2 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 @@ -411,8 +411,10 @@ loop.stop() t = asyncio.Task(task(), loop=loop) - self.assertRaises( - RuntimeError, loop.run_until_complete, t) + with self.assertRaises(RuntimeError) as cm: + loop.run_until_complete(t) + self.assertEqual(str(cm.exception), + 'Event loop stopped before Future completed.') self.assertFalse(t.done()) self.assertEqual(x, 2) self.assertAlmostEqual(0.3, loop.time()) @@ -420,6 +422,8 @@ # close generators for w in waiters: w.close() + t.cancel() + self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t) def test_wait_for(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 01:37:29 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 26 Jun 2014 01:37:29 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogSGFu?= =?utf-8?q?dle_error_handler=3A_enhance_formatting_of_the_callback?= Message-ID: <3gzLPP6rv1z7LjS@mail.python.org> http://hg.python.org/cpython/rev/8cd3e6b0f5cc changeset: 91418:8cd3e6b0f5cc branch: 3.4 parent: 91416:4e4c6e2ed0c5 user: Victor Stinner date: Thu Jun 26 01:35:45 2014 +0200 summary: asyncio: Handle error handler: enhance formatting of the callback files: Lib/asyncio/events.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -89,8 +89,8 @@ try: self._callback(*self._args) except Exception as exc: - msg = 'Exception in callback {}{!r}'.format(self._callback, - self._args) + cb = _format_callback(self._callback, self._args) + msg = 'Exception in callback {}'.format(cb) self._loop.call_exception_handler({ 'message': msg, 'exception': exc, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 01:37:31 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 26 Jun 2014 01:37:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Handle_error_handler=3A_enh?= =?utf-8?q?ance_formatting_of_the_callback?= Message-ID: <3gzLPR1TvWz7LjS@mail.python.org> http://hg.python.org/cpython/rev/cb04c94014ea changeset: 91419:cb04c94014ea parent: 91417:24282c6f6019 parent: 91418:8cd3e6b0f5cc user: Victor Stinner date: Thu Jun 26 01:36:47 2014 +0200 summary: (Merge 3.4) asyncio: Handle error handler: enhance formatting of the callback files: Lib/asyncio/events.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -89,8 +89,8 @@ try: self._callback(*self._args) except Exception as exc: - msg = 'Exception in callback {}{!r}'.format(self._callback, - self._args) + cb = _format_callback(self._callback, self._args) + msg = 'Exception in callback {}'.format(cb) self._loop.call_exception_handler({ 'message': msg, 'exception': exc, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 07:41:30 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 26 Jun 2014 07:41:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE4NTky?= =?utf-8?q?=3A_For_idlelib=2ESearchDialogBase=2C_edit_and_add_docstrings?= =?utf-8?q?=2C?= Message-ID: <3gzVTQ3mx3z7Lk3@mail.python.org> http://hg.python.org/cpython/rev/752439a6bdd9 changeset: 91420:752439a6bdd9 branch: 2.7 parent: 91407:2672e30d9095 user: Terry Jan Reedy date: Thu Jun 26 01:40:46 2014 -0400 summary: Issue #18592: For idlelib.SearchDialogBase, edit and add docstrings, move two functions next to the functions that use them. files: Lib/idlelib/SearchDialogBase.py | 87 +++++++++++++------- 1 files changed, 57 insertions(+), 30 deletions(-) diff --git a/Lib/idlelib/SearchDialogBase.py b/Lib/idlelib/SearchDialogBase.py --- a/Lib/idlelib/SearchDialogBase.py +++ b/Lib/idlelib/SearchDialogBase.py @@ -1,16 +1,19 @@ '''Define SearchDialogBase used by Search, Replace, and Grep dialogs.''' -from Tkinter import * + +from Tkinter import (Toplevel, Frame, Entry, Label, Button, + Checkbutton, Radiobutton) class SearchDialogBase: - '''Create most of a modal search dialog (make_frame, create_widgets). + '''Create most of a 3 or 4 row, 3 column search dialog. - The wide left column contains: - 1 or 2 text entry lines (create_entries, make_entry); - a row of standard radiobuttons (create_option_buttons); - a row of dialog specific radiobuttons (create_other_buttons). + The left and wide middle column contain: + 1 or 2 labeled text entry lines (make_entry, create_entries); + a row of standard Checkbuttons (make_frame, create_option_buttons), + each of which corresponds to a search engine Variable; + a row of dialog-specific Check/Radiobuttons (create_other_buttons). The narrow right column contains command buttons - (create_command_buttons, make_button). + (make_button, create_command_buttons). These are bound to functions that execute the command. Except for command buttons, this base class is not limited to @@ -19,16 +22,27 @@ The other dialogs override methods to replace and add widgets. ''' - title = "Search Dialog" + title = "Search Dialog" # replace in subclasses icon = "Search" - needwrapbutton = 1 + needwrapbutton = 1 # not in Find in Files def __init__(self, root, engine): + '''Initialize root, engine, and top attributes. + + top (level widget): set in create_widgets() called from open(). + text (Text being searched): set in open(), only used in subclasses(). + ent (ry): created in make_entry() called from create_entry(). + row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). + + title (of dialog): class attribute, override in subclasses. + icon (of dialog): ditto, use unclear if cannot minimize dialog. + ''' self.root = root self.engine = engine self.top = None def open(self, text, searchphrase=None): + "Make dialog visible on top of others and ready to use." self.text = text if not self.top: self.create_widgets() @@ -44,11 +58,17 @@ self.top.grab_set() def close(self, event=None): + "Put dialog away for later use." if self.top: self.top.grab_release() self.top.withdraw() def create_widgets(self): + '''Create basic 3 row x 3 col search (find) dialog. + + Other dialogs override subsidiary create_x methods as needed. + Replace and Find-in-Files add another entry row. + ''' top = Toplevel(self.root) top.bind("", self.default_command) top.bind("", self.close) @@ -61,12 +81,13 @@ self.top.grid_columnconfigure(0, pad=2, weight=0) self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100) - self.create_entries() - self.create_option_buttons() - self.create_other_buttons() - return self.create_command_buttons() + self.create_entries() # row 0 (and maybe 1), cols 0, 1 + self.create_option_buttons() # next row, cols 0, 1 + self.create_other_buttons() # next row, cols 0, 1 + self.create_command_buttons() # col 2, all rows def make_entry(self, label, var): + "Return gridded labeled Entry." l = Label(self.top, text=label) l.grid(row=self.row, column=0, sticky="nw") e = Entry(self.top, textvariable=var, exportselection=0) @@ -74,7 +95,12 @@ self.row = self.row + 1 return e + def create_entries(self): + "Create one or more entry lines with make_entry." + self.ent = self.make_entry("Find:", self.engine.patvar) + def make_frame(self,labeltext=None): + "Return gridded labeled Frame for option or other buttons." if labeltext: l = Label(self.top, text=labeltext) l.grid(row=self.row, column=0, sticky="nw") @@ -83,19 +109,8 @@ self.row = self.row + 1 return f - def make_button(self, label, command, isdef=0): - b = Button(self.buttonframe, - text=label, command=command, - default=isdef and "active" or "normal") - cols,rows=self.buttonframe.grid_size() - b.grid(pady=1,row=rows,column=0,sticky="ew") - self.buttonframe.grid(rowspan=rows+1) - return b - - def create_entries(self): - self.ent = self.make_entry("Find:", self.engine.patvar) - def create_option_buttons(self): + "Fill frame with Checkbuttons bound to SearchEngine booleanvars." f = self.make_frame("Options") btn = Checkbutton(f, anchor="w", @@ -128,11 +143,9 @@ btn.select() def create_other_buttons(self): + "Fill frame with buttons tied to other options." f = self.make_frame("Direction") - #lbl = Label(f, text="Direction: ") - #lbl.pack(side="left") - btn = Radiobutton(f, anchor="w", variable=self.engine.backvar, value=1, text="Up") @@ -147,11 +160,25 @@ if not self.engine.isback(): btn.select() + def make_button(self, label, command, isdef=0): + "Return command button gridded in command frame." + b = Button(self.buttonframe, + text=label, command=command, + default=isdef and "active" or "normal") + cols,rows=self.buttonframe.grid_size() + b.grid(pady=1,row=rows,column=0,sticky="ew") + self.buttonframe.grid(rowspan=rows+1) + return b + def create_command_buttons(self): - # - # place button frame on the right + "Place buttons in vertical command frame gridded on right." f = self.buttonframe = Frame(self.top) f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) b = self.make_button("close", self.close) b.lower() + +if __name__ == '__main__': + import unittest + unittest.main( + 'idlelib.idle_test.test_searchdialogbase', verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 07:41:31 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 26 Jun 2014 07:41:31 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE4NTky?= =?utf-8?q?=3A_For_idlelib=2ESearchDialogBase=2C_edit_and_add_docstrings?= =?utf-8?q?=2C?= Message-ID: <3gzVTR6j5Vz7Lk8@mail.python.org> http://hg.python.org/cpython/rev/ed60a73e1c82 changeset: 91421:ed60a73e1c82 branch: 3.4 parent: 91418:8cd3e6b0f5cc user: Terry Jan Reedy date: Thu Jun 26 01:40:51 2014 -0400 summary: Issue #18592: For idlelib.SearchDialogBase, edit and add docstrings, move two functions next to the functions that use them. files: Lib/idlelib/SearchDialogBase.py | 87 +++++++++++++------- 1 files changed, 57 insertions(+), 30 deletions(-) diff --git a/Lib/idlelib/SearchDialogBase.py b/Lib/idlelib/SearchDialogBase.py --- a/Lib/idlelib/SearchDialogBase.py +++ b/Lib/idlelib/SearchDialogBase.py @@ -1,16 +1,19 @@ '''Define SearchDialogBase used by Search, Replace, and Grep dialogs.''' -from tkinter import * + +from tkinter import (Toplevel, Frame, Entry, Label, Button, + Checkbutton, Radiobutton) class SearchDialogBase: - '''Create most of a modal search dialog (make_frame, create_widgets). + '''Create most of a 3 or 4 row, 3 column search dialog. - The wide left column contains: - 1 or 2 text entry lines (create_entries, make_entry); - a row of standard radiobuttons (create_option_buttons); - a row of dialog specific radiobuttons (create_other_buttons). + The left and wide middle column contain: + 1 or 2 labeled text entry lines (make_entry, create_entries); + a row of standard Checkbuttons (make_frame, create_option_buttons), + each of which corresponds to a search engine Variable; + a row of dialog-specific Check/Radiobuttons (create_other_buttons). The narrow right column contains command buttons - (create_command_buttons, make_button). + (make_button, create_command_buttons). These are bound to functions that execute the command. Except for command buttons, this base class is not limited to @@ -19,16 +22,27 @@ The other dialogs override methods to replace and add widgets. ''' - title = "Search Dialog" + title = "Search Dialog" # replace in subclasses icon = "Search" - needwrapbutton = 1 + needwrapbutton = 1 # not in Find in Files def __init__(self, root, engine): + '''Initialize root, engine, and top attributes. + + top (level widget): set in create_widgets() called from open(). + text (Text being searched): set in open(), only used in subclasses(). + ent (ry): created in make_entry() called from create_entry(). + row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). + + title (of dialog): class attribute, override in subclasses. + icon (of dialog): ditto, use unclear if cannot minimize dialog. + ''' self.root = root self.engine = engine self.top = None def open(self, text, searchphrase=None): + "Make dialog visible on top of others and ready to use." self.text = text if not self.top: self.create_widgets() @@ -44,11 +58,17 @@ self.top.grab_set() def close(self, event=None): + "Put dialog away for later use." if self.top: self.top.grab_release() self.top.withdraw() def create_widgets(self): + '''Create basic 3 row x 3 col search (find) dialog. + + Other dialogs override subsidiary create_x methods as needed. + Replace and Find-in-Files add another entry row. + ''' top = Toplevel(self.root) top.bind("", self.default_command) top.bind("", self.close) @@ -61,12 +81,13 @@ self.top.grid_columnconfigure(0, pad=2, weight=0) self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100) - self.create_entries() - self.create_option_buttons() - self.create_other_buttons() - return self.create_command_buttons() + self.create_entries() # row 0 (and maybe 1), cols 0, 1 + self.create_option_buttons() # next row, cols 0, 1 + self.create_other_buttons() # next row, cols 0, 1 + self.create_command_buttons() # col 2, all rows def make_entry(self, label, var): + "Return gridded labeled Entry." l = Label(self.top, text=label) l.grid(row=self.row, column=0, sticky="nw") e = Entry(self.top, textvariable=var, exportselection=0) @@ -74,7 +95,12 @@ self.row = self.row + 1 return e + def create_entries(self): + "Create one or more entry lines with make_entry." + self.ent = self.make_entry("Find:", self.engine.patvar) + def make_frame(self,labeltext=None): + "Return gridded labeled Frame for option or other buttons." if labeltext: l = Label(self.top, text=labeltext) l.grid(row=self.row, column=0, sticky="nw") @@ -83,19 +109,8 @@ self.row = self.row + 1 return f - def make_button(self, label, command, isdef=0): - b = Button(self.buttonframe, - text=label, command=command, - default=isdef and "active" or "normal") - cols,rows=self.buttonframe.grid_size() - b.grid(pady=1,row=rows,column=0,sticky="ew") - self.buttonframe.grid(rowspan=rows+1) - return b - - def create_entries(self): - self.ent = self.make_entry("Find:", self.engine.patvar) - def create_option_buttons(self): + "Fill frame with Checkbuttons bound to SearchEngine booleanvars." f = self.make_frame("Options") btn = Checkbutton(f, anchor="w", @@ -128,11 +143,9 @@ btn.select() def create_other_buttons(self): + "Fill frame with buttons tied to other options." f = self.make_frame("Direction") - #lbl = Label(f, text="Direction: ") - #lbl.pack(side="left") - btn = Radiobutton(f, anchor="w", variable=self.engine.backvar, value=1, text="Up") @@ -147,11 +160,25 @@ if not self.engine.isback(): btn.select() + def make_button(self, label, command, isdef=0): + "Return command button gridded in command frame." + b = Button(self.buttonframe, + text=label, command=command, + default=isdef and "active" or "normal") + cols,rows=self.buttonframe.grid_size() + b.grid(pady=1,row=rows,column=0,sticky="ew") + self.buttonframe.grid(rowspan=rows+1) + return b + def create_command_buttons(self): - # - # place button frame on the right + "Place buttons in vertical command frame gridded on right." f = self.buttonframe = Frame(self.top) f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) b = self.make_button("close", self.close) b.lower() + +if __name__ == '__main__': + import unittest + unittest.main( + 'idlelib.idle_test.test_searchdialogbase', verbosity=2) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 07:41:33 2014 From: python-checkins at python.org (terry.reedy) Date: Thu, 26 Jun 2014 07:41:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3gzVTT2N3gz7Lkn@mail.python.org> http://hg.python.org/cpython/rev/3151f6f9df85 changeset: 91422:3151f6f9df85 parent: 91419:cb04c94014ea parent: 91421:ed60a73e1c82 user: Terry Jan Reedy date: Thu Jun 26 01:41:06 2014 -0400 summary: Merge with 3.4 files: Lib/idlelib/SearchDialogBase.py | 87 +++++++++++++------- 1 files changed, 57 insertions(+), 30 deletions(-) diff --git a/Lib/idlelib/SearchDialogBase.py b/Lib/idlelib/SearchDialogBase.py --- a/Lib/idlelib/SearchDialogBase.py +++ b/Lib/idlelib/SearchDialogBase.py @@ -1,16 +1,19 @@ '''Define SearchDialogBase used by Search, Replace, and Grep dialogs.''' -from tkinter import * + +from tkinter import (Toplevel, Frame, Entry, Label, Button, + Checkbutton, Radiobutton) class SearchDialogBase: - '''Create most of a modal search dialog (make_frame, create_widgets). + '''Create most of a 3 or 4 row, 3 column search dialog. - The wide left column contains: - 1 or 2 text entry lines (create_entries, make_entry); - a row of standard radiobuttons (create_option_buttons); - a row of dialog specific radiobuttons (create_other_buttons). + The left and wide middle column contain: + 1 or 2 labeled text entry lines (make_entry, create_entries); + a row of standard Checkbuttons (make_frame, create_option_buttons), + each of which corresponds to a search engine Variable; + a row of dialog-specific Check/Radiobuttons (create_other_buttons). The narrow right column contains command buttons - (create_command_buttons, make_button). + (make_button, create_command_buttons). These are bound to functions that execute the command. Except for command buttons, this base class is not limited to @@ -19,16 +22,27 @@ The other dialogs override methods to replace and add widgets. ''' - title = "Search Dialog" + title = "Search Dialog" # replace in subclasses icon = "Search" - needwrapbutton = 1 + needwrapbutton = 1 # not in Find in Files def __init__(self, root, engine): + '''Initialize root, engine, and top attributes. + + top (level widget): set in create_widgets() called from open(). + text (Text being searched): set in open(), only used in subclasses(). + ent (ry): created in make_entry() called from create_entry(). + row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). + + title (of dialog): class attribute, override in subclasses. + icon (of dialog): ditto, use unclear if cannot minimize dialog. + ''' self.root = root self.engine = engine self.top = None def open(self, text, searchphrase=None): + "Make dialog visible on top of others and ready to use." self.text = text if not self.top: self.create_widgets() @@ -44,11 +58,17 @@ self.top.grab_set() def close(self, event=None): + "Put dialog away for later use." if self.top: self.top.grab_release() self.top.withdraw() def create_widgets(self): + '''Create basic 3 row x 3 col search (find) dialog. + + Other dialogs override subsidiary create_x methods as needed. + Replace and Find-in-Files add another entry row. + ''' top = Toplevel(self.root) top.bind("", self.default_command) top.bind("", self.close) @@ -61,12 +81,13 @@ self.top.grid_columnconfigure(0, pad=2, weight=0) self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100) - self.create_entries() - self.create_option_buttons() - self.create_other_buttons() - return self.create_command_buttons() + self.create_entries() # row 0 (and maybe 1), cols 0, 1 + self.create_option_buttons() # next row, cols 0, 1 + self.create_other_buttons() # next row, cols 0, 1 + self.create_command_buttons() # col 2, all rows def make_entry(self, label, var): + "Return gridded labeled Entry." l = Label(self.top, text=label) l.grid(row=self.row, column=0, sticky="nw") e = Entry(self.top, textvariable=var, exportselection=0) @@ -74,7 +95,12 @@ self.row = self.row + 1 return e + def create_entries(self): + "Create one or more entry lines with make_entry." + self.ent = self.make_entry("Find:", self.engine.patvar) + def make_frame(self,labeltext=None): + "Return gridded labeled Frame for option or other buttons." if labeltext: l = Label(self.top, text=labeltext) l.grid(row=self.row, column=0, sticky="nw") @@ -83,19 +109,8 @@ self.row = self.row + 1 return f - def make_button(self, label, command, isdef=0): - b = Button(self.buttonframe, - text=label, command=command, - default=isdef and "active" or "normal") - cols,rows=self.buttonframe.grid_size() - b.grid(pady=1,row=rows,column=0,sticky="ew") - self.buttonframe.grid(rowspan=rows+1) - return b - - def create_entries(self): - self.ent = self.make_entry("Find:", self.engine.patvar) - def create_option_buttons(self): + "Fill frame with Checkbuttons bound to SearchEngine booleanvars." f = self.make_frame("Options") btn = Checkbutton(f, anchor="w", @@ -128,11 +143,9 @@ btn.select() def create_other_buttons(self): + "Fill frame with buttons tied to other options." f = self.make_frame("Direction") - #lbl = Label(f, text="Direction: ") - #lbl.pack(side="left") - btn = Radiobutton(f, anchor="w", variable=self.engine.backvar, value=1, text="Up") @@ -147,11 +160,25 @@ if not self.engine.isback(): btn.select() + def make_button(self, label, command, isdef=0): + "Return command button gridded in command frame." + b = Button(self.buttonframe, + text=label, command=command, + default=isdef and "active" or "normal") + cols,rows=self.buttonframe.grid_size() + b.grid(pady=1,row=rows,column=0,sticky="ew") + self.buttonframe.grid(rowspan=rows+1) + return b + def create_command_buttons(self): - # - # place button frame on the right + "Place buttons in vertical command frame gridded on right." f = self.buttonframe = Frame(self.top) f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) b = self.make_button("close", self.close) b.lower() + +if __name__ == '__main__': + import unittest + unittest.main( + 'idlelib.idle_test.test_searchdialogbase', verbosity=2) -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Thu Jun 26 10:56:42 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 26 Jun 2014 10:56:42 +0200 Subject: [Python-checkins] Daily reference leaks (cb04c94014ea): sum=11 Message-ID: results for cb04c94014ea on branch "default" -------------------------------------------- test_collections leaked [0, -2, 0] references, sum=-2 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [2, 0, 0] references, sum=2 test_site leaked [2, 0, 0] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogC_GJT1', '-x'] From python-checkins at python.org Thu Jun 26 18:25:52 2014 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 26 Jun 2014 18:25:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE5MTQ1?= =?utf-8?q?=3A__Remove_duplicate_ACKS_entry?= Message-ID: <3gzmmw1XW0z7Lvw@mail.python.org> http://hg.python.org/cpython/rev/463f499ef591 changeset: 91423:463f499ef591 branch: 3.4 parent: 91421:ed60a73e1c82 user: Raymond Hettinger date: Thu Jun 26 09:25:18 2014 -0700 summary: Issue #19145: Remove duplicate ACKS entry files: Misc/ACKS | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1388,7 +1388,6 @@ Pauli Virtanen Frank Visser Johannes Vogel -Vajrasky Kok Alex Volkov Martijn Vries Sjoerd de Vries -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 18:25:53 2014 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 26 Jun 2014 18:25:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_merge?= Message-ID: <3gzmmx37V9z7Lvw@mail.python.org> http://hg.python.org/cpython/rev/9f51e71bf761 changeset: 91424:9f51e71bf761 parent: 91422:3151f6f9df85 parent: 91423:463f499ef591 user: Raymond Hettinger date: Thu Jun 26 09:25:41 2014 -0700 summary: merge files: Misc/ACKS | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1399,7 +1399,6 @@ Pauli Virtanen Frank Visser Johannes Vogel -Vajrasky Kok Alex Volkov Martijn Vries Sjoerd de Vries -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 18:27:47 2014 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 26 Jun 2014 18:27:47 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5MTQ1?= =?utf-8?q?=3A__Remove_duplicate_ACKS_entry?= Message-ID: <3gzmq74Q8pz7P3P@mail.python.org> http://hg.python.org/cpython/rev/07eb04003839 changeset: 91425:07eb04003839 branch: 2.7 parent: 91420:752439a6bdd9 user: Raymond Hettinger date: Thu Jun 26 09:27:36 2014 -0700 summary: Issue #19145: Remove duplicate ACKS entry files: Misc/ACKS | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1384,7 +1384,6 @@ Pauli Virtanen Frank Visser Johannes Vogel -Vajrasky Kok Alex Volkov Guido Vranken Martijn Vries -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 18:28:38 2014 From: python-checkins at python.org (r.david.murray) Date: Thu, 26 Jun 2014 18:28:38 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=2320295=3A_Teach_imghdr_t?= =?utf-8?q?o_recognize_OpenEXR_format_images=2E?= Message-ID: <3gzmr66RZDz7M0n@mail.python.org> http://hg.python.org/cpython/rev/71b9a841119a changeset: 91426:71b9a841119a parent: 91424:9f51e71bf761 user: R David Murray date: Thu Jun 26 12:27:57 2014 -0400 summary: #20295: Teach imghdr to recognize OpenEXR format images. Patch by Martin Vignali, test by Claudiu Popa. files: Doc/library/imghdr.rst | 5 +++++ Doc/whatsnew/3.5.rst | 6 ++++++ Lib/imghdr.py | 6 ++++++ Lib/test/imghdrdata/python.exr | Bin Lib/test/test_imghdr.py | 1 + Misc/NEWS | 2 ++ 6 files changed, 20 insertions(+), 0 deletions(-) diff --git a/Doc/library/imghdr.rst b/Doc/library/imghdr.rst --- a/Doc/library/imghdr.rst +++ b/Doc/library/imghdr.rst @@ -50,6 +50,11 @@ +------------+-----------------------------------+ | ``'webp'`` | WebP files | +------------+-----------------------------------+ +| ``'exr'`` | OpenEXR Files | ++------------+-----------------------------------+ + +.. versionadded:: 3.5 + The *exr* format was added. .. versionchanged:: 3.5 The *webp* type was added. diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -141,6 +141,12 @@ *module* contains no docstrings instead of raising :exc:`ValueError` (contributed by Glenn Jones in :issue:`15916`). +imghdr +------ + +* :func:`~imghdr.what` now recognizes the `OpenEXR `_ + format (contributed by Martin vignali and Cladui Popa in :issue:`20295`). + importlib --------- diff --git a/Lib/imghdr.py b/Lib/imghdr.py --- a/Lib/imghdr.py +++ b/Lib/imghdr.py @@ -116,6 +116,12 @@ tests.append(test_webp) +def test_exr(h, f): + if h.startswith(b'\x76\x2f\x31\x01'): + return 'exr' + +tests.append(test_exr) + #--------------------# # Small test program # #--------------------# diff --git a/Lib/test/imghdrdata/python.exr b/Lib/test/imghdrdata/python.exr new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..773c81ee1fb850cdbb6cccd3ae5edabb80146481 GIT binary patch [stripped] diff --git a/Lib/test/test_imghdr.py b/Lib/test/test_imghdr.py --- a/Lib/test/test_imghdr.py +++ b/Lib/test/test_imghdr.py @@ -18,6 +18,7 @@ ('python.tiff', 'tiff'), ('python.xbm', 'xbm'), ('python.webp', 'webp'), + ('python.exr', 'exr'), ) class UnseekableIO(io.FileIO): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,8 @@ Library ------- +- Issue #20295: imghdr now recognizes OpenEXR format images. + - Issue #21729: Used the "with" statement in the dbm.dumb module to ensure files closing. Patch by Claudiu Popa. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 19:33:32 2014 From: python-checkins at python.org (r.david.murray) Date: Thu, 26 Jun 2014 19:33:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogIzIxNDc2OiBVbndy?= =?utf-8?q?ap_fp_in_BytesParser_so_the_file_isn=27t_unexpectedly_closed=2E?= Message-ID: <3gzpH05QVSz7LjP@mail.python.org> http://hg.python.org/cpython/rev/0a16756dfcc0 changeset: 91427:0a16756dfcc0 branch: 3.4 parent: 91423:463f499ef591 user: R David Murray date: Thu Jun 26 13:31:43 2014 -0400 summary: #21476: Unwrap fp in BytesParser so the file isn't unexpectedly closed. This makes the behavior match that of Parser. Patch by Vajrasky Kok. files: Lib/email/parser.py | 4 ++- Lib/test/test_email/test_email.py | 25 +++++++++++++++++++ Misc/NEWS | 3 ++ 3 files changed, 31 insertions(+), 1 deletions(-) diff --git a/Lib/email/parser.py b/Lib/email/parser.py --- a/Lib/email/parser.py +++ b/Lib/email/parser.py @@ -106,8 +106,10 @@ meaning it parses the entire contents of the file. """ fp = TextIOWrapper(fp, encoding='ascii', errors='surrogateescape') - with fp: + try: return self.parser.parse(fp, headersonly) + finally: + fp.detach() def parsebytes(self, text, headersonly=False): 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 @@ -3390,6 +3390,31 @@ self.assertIsInstance(msg.get_payload(), str) self.assertIsInstance(msg.get_payload(decode=True), bytes) + def test_bytes_parser_does_not_close_file(self): + with openfile('msg_02.txt', 'rb') as fp: + email.parser.BytesParser().parse(fp) + self.assertFalse(fp.closed) + + def test_bytes_parser_on_exception_does_not_close_file(self): + with openfile('msg_15.txt', 'rb') as fp: + bytesParser = email.parser.BytesParser + self.assertRaises(email.errors.StartBoundaryNotFoundDefect, + bytesParser(policy=email.policy.strict).parse, + fp) + self.assertFalse(fp.closed) + + def test_parser_does_not_close_file(self): + with openfile('msg_02.txt', 'r') as fp: + email.parser.Parser().parse(fp) + self.assertFalse(fp.closed) + + def test_parser_on_exception_does_not_close_file(self): + with openfile('msg_15.txt', 'r') as fp: + parser = email.parser.Parser + self.assertRaises(email.errors.StartBoundaryNotFoundDefect, + parser(policy=email.policy.strict).parse, fp) + self.assertFalse(fp.closed) + def test_whitespace_continuation(self): eq = self.assertEqual # This message contains a line after the Subject: header that has only diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,9 @@ Library ------- +- Issue #21476: Make sure the email.parser.BytesParser TextIOWrapper is + discarded after parsing, so the input file isn't unexpectedly closed. + - Issue #21729: Used the "with" statement in the dbm.dumb module to ensure files closing. Patch by Claudiu Popa. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 19:33:34 2014 From: python-checkins at python.org (r.david.murray) Date: Thu, 26 Jun 2014 19:33:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_=2321476=3A_Unwrap_fp_in_BytesParser_so_the_file_i?= =?utf-8?q?sn=27t_unexpectedly_closed=2E?= Message-ID: <3gzpH20BLnz7LjR@mail.python.org> http://hg.python.org/cpython/rev/a3ee325fd489 changeset: 91428:a3ee325fd489 parent: 91426:71b9a841119a parent: 91427:0a16756dfcc0 user: R David Murray date: Thu Jun 26 13:33:05 2014 -0400 summary: Merge #21476: Unwrap fp in BytesParser so the file isn't unexpectedly closed. files: Lib/email/parser.py | 4 ++- Lib/test/test_email/test_email.py | 25 +++++++++++++++++++ Misc/NEWS | 3 ++ 3 files changed, 31 insertions(+), 1 deletions(-) diff --git a/Lib/email/parser.py b/Lib/email/parser.py --- a/Lib/email/parser.py +++ b/Lib/email/parser.py @@ -106,8 +106,10 @@ meaning it parses the entire contents of the file. """ fp = TextIOWrapper(fp, encoding='ascii', errors='surrogateescape') - with fp: + try: return self.parser.parse(fp, headersonly) + finally: + fp.detach() def parsebytes(self, text, headersonly=False): 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 @@ -3390,6 +3390,31 @@ self.assertIsInstance(msg.get_payload(), str) self.assertIsInstance(msg.get_payload(decode=True), bytes) + def test_bytes_parser_does_not_close_file(self): + with openfile('msg_02.txt', 'rb') as fp: + email.parser.BytesParser().parse(fp) + self.assertFalse(fp.closed) + + def test_bytes_parser_on_exception_does_not_close_file(self): + with openfile('msg_15.txt', 'rb') as fp: + bytesParser = email.parser.BytesParser + self.assertRaises(email.errors.StartBoundaryNotFoundDefect, + bytesParser(policy=email.policy.strict).parse, + fp) + self.assertFalse(fp.closed) + + def test_parser_does_not_close_file(self): + with openfile('msg_02.txt', 'r') as fp: + email.parser.Parser().parse(fp) + self.assertFalse(fp.closed) + + def test_parser_on_exception_does_not_close_file(self): + with openfile('msg_15.txt', 'r') as fp: + parser = email.parser.Parser + self.assertRaises(email.errors.StartBoundaryNotFoundDefect, + parser(policy=email.policy.strict).parse, fp) + self.assertFalse(fp.closed) + def test_whitespace_continuation(self): eq = self.assertEqual # This message contains a line after the Subject: header that has only diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,9 @@ Library ------- +- Issue #21476: Make sure the email.parser.BytesParser TextIOWrapper is + discarded after parsing, so the input file isn't unexpectedly closed. + - Issue #20295: imghdr now recognizes OpenEXR format images. - Issue #21729: Used the "with" statement in the dbm.dumb module to ensure -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 19:57:57 2014 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 26 Jun 2014 19:57:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?devguide=3A_log_the_addition_of_Berke?= =?utf-8?q?r_Peksag?= Message-ID: <3gzpq92rb6z7LjM@mail.python.org> http://hg.python.org/devguide/rev/31dded3eeae0 changeset: 705:31dded3eeae0 user: Benjamin Peterson date: Thu Jun 26 10:57:49 2014 -0700 summary: log the addition of Berker Peksag files: developers.rst | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/developers.rst b/developers.rst --- a/developers.rst +++ b/developers.rst @@ -25,6 +25,9 @@ Permissions History ------------------- +- Berker Peksa? was given push privileges on June 26, 2014 by Benjamin Peterson, + on the recommendation of R. David Murray. + - Steve Dower was given push privileges on May 10, 2014 by Antoine Pitrou, on recommendation by Martin v. Loewis. -- Repository URL: http://hg.python.org/devguide From python-checkins at python.org Thu Jun 26 22:22:34 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 26 Jun 2014 22:22:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxODI5?= =?utf-8?q?=3A_Fix_running_test=5Fctypes_on_Windows_with_-O_or_-OO?= Message-ID: <3gzt226BLMz7LjM@mail.python.org> http://hg.python.org/cpython/rev/ab708e4131dd changeset: 91429:ab708e4131dd branch: 3.4 parent: 91427:0a16756dfcc0 user: Zachary Ware date: Thu Jun 26 15:20:44 2014 -0500 summary: Issue #21829: Fix running test_ctypes on Windows with -O or -OO files: Lib/ctypes/test/test_values.py | 15 +++------------ 1 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py --- a/Lib/ctypes/test/test_values.py +++ b/Lib/ctypes/test/test_values.py @@ -33,20 +33,11 @@ """This test only works when python itself is a dll/shared library""" def test_optimizeflag(self): - # This test accesses the Py_OptimizeFlag intger, which is - # exported by the Python dll. + # This test accesses the Py_OptimizeFlag integer, which is + # exported by the Python dll and should match the sys.flags value - # It's value is set depending on the -O and -OO flags: - # if not given, it is 0 and __debug__ is 1. - # If -O is given, the flag is 1, for -OO it is 2. - # docstrings are also removed in the latter case. opt = c_int.in_dll(pythonapi, "Py_OptimizeFlag").value - if __debug__: - self.assertEqual(opt, 0) - elif ValuesTestCase.__doc__ is not None: - self.assertEqual(opt, 1) - else: - self.assertEqual(opt, 2) + self.assertEqual(opt, sys.flags.optimize) def test_frozentable(self): # Python exports a PyImport_FrozenModules symbol. This is a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 22:22:36 2014 From: python-checkins at python.org (zach.ware) Date: Thu, 26 Jun 2014 22:22:36 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321829=3A_Merge_with_3=2E4?= Message-ID: <3gzt240S6Hz7Ljk@mail.python.org> http://hg.python.org/cpython/rev/bbb28082d7b4 changeset: 91430:bbb28082d7b4 parent: 91428:a3ee325fd489 parent: 91429:ab708e4131dd user: Zachary Ware date: Thu Jun 26 15:22:16 2014 -0500 summary: Issue #21829: Merge with 3.4 files: Lib/ctypes/test/test_values.py | 15 +++------------ 1 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py --- a/Lib/ctypes/test/test_values.py +++ b/Lib/ctypes/test/test_values.py @@ -33,20 +33,11 @@ """This test only works when python itself is a dll/shared library""" def test_optimizeflag(self): - # This test accesses the Py_OptimizeFlag intger, which is - # exported by the Python dll. + # This test accesses the Py_OptimizeFlag integer, which is + # exported by the Python dll and should match the sys.flags value - # It's value is set depending on the -O and -OO flags: - # if not given, it is 0 and __debug__ is 1. - # If -O is given, the flag is 1, for -OO it is 2. - # docstrings are also removed in the latter case. opt = c_int.in_dll(pythonapi, "Py_OptimizeFlag").value - if __debug__: - self.assertEqual(opt, 0) - elif ValuesTestCase.__doc__ is not None: - self.assertEqual(opt, 1) - else: - self.assertEqual(opt, 2) + self.assertEqual(opt, sys.flags.optimize) def test_frozentable(self): # Python exports a PyImport_FrozenModules symbol. This is a -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 22:30:05 2014 From: python-checkins at python.org (berker.peksag) Date: Thu, 26 Jun 2014 22:30:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzE5ODk3?= =?utf-8?q?=3A_Use_python_as_executable_instead_of_python3=2E?= Message-ID: <3gztBj1VDnz7Ljf@mail.python.org> http://hg.python.org/cpython/rev/eb0921b2100b changeset: 91431:eb0921b2100b branch: 2.7 parent: 91425:07eb04003839 user: Berker Peksag date: Thu Jun 26 23:27:26 2014 +0300 summary: Issue #19897: Use python as executable instead of python3. files: Doc/library/site.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/site.rst b/Doc/library/site.rst --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -185,8 +185,8 @@ .. code-block:: sh - $ python3 -m site --user-site - /home/user/.local/lib/python3.3/site-packages + $ python -m site --user-site + /home/user/.local/lib/python2.7/site-packages .. program:: site -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 23:14:09 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 26 Jun 2014 23:14:09 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?peps=3A_Add_PEP_471=3A_=22os=2Escandi?= =?utf-8?q?r=28=29_function_--_a_better_and_faster_directory_iterator=22?= Message-ID: <3gzv9Y1DSjz7LjR@mail.python.org> http://hg.python.org/peps/rev/2ff0e17443e4 changeset: 5490:2ff0e17443e4 user: Victor Stinner date: Thu Jun 26 23:14:01 2014 +0200 summary: Add PEP 471: "os.scandir() function -- a better and faster directory iterator" by Ben Hoyt files: pep-0471.txt | 376 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 376 insertions(+), 0 deletions(-) diff --git a/pep-0471.txt b/pep-0471.txt new file mode 100644 --- /dev/null +++ b/pep-0471.txt @@ -0,0 +1,376 @@ +PEP: 471 +Title: os.scandir() function -- a better and faster directory iterator +Version: $Revision$ +Last-Modified: $Date$ +Author: Ben Hoyt +Status: Draft +Type: Standards Track +Content-Type: text/x-rst +Created: 30-May-2014 +Python-Version: 3.5 + + +Abstract +======== + +This PEP proposes including a new directory iteration function, +``os.scandir()``, in the standard library. This new function adds +useful functionality and increases the speed of ``os.walk()`` by 2-10 +times (depending on the platform and file system) by significantly +reducing the number of times ``stat()`` needs to be called. + + +Rationale +========= + +Python's built-in ``os.walk()`` is significantly slower than it needs +to be, because -- in addition to calling ``os.listdir()`` on each +directory -- it executes the system call ``os.stat()`` or +``GetFileAttributes()`` on each file to determine whether the entry is +a directory or not. + +But the underlying system calls -- ``FindFirstFile`` / +``FindNextFile`` on Windows and ``readdir`` on Linux and OS X -- +already tell you whether the files returned are directories or not, so +no further system calls are needed. In short, you can reduce the +number of system calls from approximately 2N to N, where N is the +total number of files and directories in the tree. (And because +directory trees are usually much wider than they are deep, it's often +much better than this.) + +In practice, removing all those extra system calls makes ``os.walk()`` +about **8-9 times as fast on Windows**, and about **2-3 times as fast +on Linux and Mac OS X**. So we're not talking about micro- +optimizations. See more `benchmarks`_. + +.. _`benchmarks`: https://github.com/benhoyt/scandir#benchmarks + +Somewhat relatedly, many people (see Python `Issue 11406`_) are also +keen on a version of ``os.listdir()`` that yields filenames as it +iterates instead of returning them as one big list. This improves +memory efficiency for iterating very large directories. + +So as well as providing a ``scandir()`` iterator function for calling +directly, Python's existing ``os.walk()`` function could be sped up a +huge amount. + +.. _`Issue 11406`: http://bugs.python.org/issue11406 + + +Implementation +============== + +The implementation of this proposal was written by Ben Hoyt (initial +version) and Tim Golden (who helped a lot with the C extension +module). It lives on GitHub at `benhoyt/scandir`_. + +.. _`benhoyt/scandir`: https://github.com/benhoyt/scandir + +Note that this module has been used and tested (see "Use in the wild" +section in this PEP), so it's more than a proof-of-concept. However, +it is marked as beta software and is not extensively battle-tested. +It will need some cleanup and more thorough testing before going into +the standard library, as well as integration into `posixmodule.c`. + + + +Specifics of proposal +===================== + +Specifically, this PEP proposes adding a single function to the ``os`` +module in the standard library, ``scandir``, that takes a single, +optional string as its argument:: + + scandir(path='.') -> generator of DirEntry objects + +Like ``listdir``, ``scandir`` calls the operating system's directory +iteration system calls to get the names of the files in the ``path`` +directory, but it's different from ``listdir`` in two ways: + +* Instead of bare filename strings, it returns lightweight + ``DirEntry`` objects that hold the filename string and provide + simple methods that allow access to the stat-like data the operating + system returned. + +* It returns a generator instead of a list, so that ``scandir`` acts + as a true iterator instead of returning the full list immediately. + +``scandir()`` yields a ``DirEntry`` object for each file and directory +in ``path``. Just like ``listdir``, the ``'.'`` and ``'..'`` +pseudo-directories are skipped, and the entries are yielded in +system-dependent order. Each ``DirEntry`` object has the following +attributes and methods: + +* ``name``: the entry's filename, relative to ``path`` (corresponds to + the return values of ``os.listdir``) + +* ``is_dir()``: like ``os.path.isdir()``, but requires no system calls + on most systems (Linux, Windows, OS X) + +* ``is_file()``: like ``os.path.isfile()``, but requires no system + calls on most systems (Linux, Windows, OS X) + +* ``is_symlink()``: like ``os.path.islink()``, but requires no system + calls on most systems (Linux, Windows, OS X) + +* ``lstat()``: like ``os.lstat()``, but requires no system calls on + Windows + +The ``DirEntry`` attribute and method names were chosen to be the same +as those in the new ``pathlib`` module for consistency. + + +Notes on caching +---------------- + +The ``DirEntry`` objects are relatively dumb -- the ``name`` attribute +is obviously always cached, and the ``is_X`` and ``lstat`` methods +cache their values (immediately on Windows via ``FindNextFile``, and +on first use on Linux / OS X via a ``stat`` call) and never refetch +from the system. + +For this reason, ``DirEntry`` objects are intended to be used and +thrown away after iteration, not stored in long-lived data structured +and the methods called again and again. + +If a user wants to do that (for example, for watching a file's size +change), they'll need to call the regular ``os.lstat()`` or +``os.path.getsize()`` functions which force a new system call each +time. + + +Examples +======== + +Here's a good usage pattern for ``scandir``. This is in fact almost +exactly how the scandir module's faster ``os.walk()`` implementation +uses it:: + + dirs = [] + non_dirs = [] + for entry in scandir(path): + if entry.is_dir(): + dirs.append(entry) + else: + non_dirs.append(entry) + +The above ``os.walk()``-like code will be significantly using scandir +on both Windows and Linux or OS X. + +Or, for getting the total size of files in a directory tree -- showing +use of the ``DirEntry.lstat()`` method:: + + def get_tree_size(path): + """Return total size of files in path and subdirs.""" + size = 0 + for entry in scandir(path): + if entry.is_dir(): + sub_path = os.path.join(path, entry.name) + size += get_tree_size(sub_path) + else: + size += entry.lstat().st_size + return size + +Note that ``get_tree_size()`` will get a huge speed boost on Windows, +because no extra stat call are needed, but on Linux and OS X the size +information is not returned by the directory iteration functions, so +this function won't gain anything there. + + +Support +======= + +The scandir module on GitHub has been forked and used quite a bit (see +"Use in the wild" in this PEP), but there's also been a fair bit of +direct support for a scandir-like function from core developers and +others on the python-dev and python-ideas mailing lists. A sampling: + +* **Nick Coghlan**, a core Python developer: "I've had the local Red + Hat release engineering team express their displeasure at having to + stat every file in a network mounted directory tree for info that is + present in the dirent structure, so a definite +1 to os.scandir from + me, so long as it makes that info available." + [`source1 `_] + +* **Tim Golden**, a core Python developer, supports scandir enough to + have spent time refactoring and significantly improving scandir's C + extension module. + [`source2 `_] + +* **Christian Heimes**, a core Python developer: "+1 for something + like yielddir()" + [`source3 `_] + and "Indeed! I'd like to see the feature in 3.4 so I can remove my + own hack from our code base." + [`source4 `_] + +* **Gregory P. Smith**, a core Python developer: "As 3.4beta1 happens + tonight, this isn't going to make 3.4 so i'm bumping this to 3.5. + I really like the proposed design outlined above." + [`source5 `_] + +* **Guido van Rossum** on the possibility of adding scandir to Python + 3.5 (as it was too late for 3.4): "The ship has likewise sailed for + adding scandir() (whether to os or pathlib). By all means experiment + and get it ready for consideration for 3.5, but I don't want to add + it to 3.4." + [`source6 `_] + +Support for this PEP itself (meta-support?) was given by Nick Coghlan +on python-dev: "A PEP reviewing all this for 3.5 and proposing a +specific os.scandir API would be a good thing." +[`source7 `_] + + +Use in the wild +=============== + +To date, ``scandir`` is definitely useful, but has been clearly marked +"beta", so it's uncertain how much use of it there is in the wild. Ben +Hoyt has had several reports from people using it. For example: + +* Chris F: "I am processing some pretty large directories and was half + expecting to have to modify getdents. So thanks for saving me the + effort." [via personal email] + +* bschollnick: "I wanted to let you know about this, since I am using + Scandir as a building block for this code. Here's a good example of + scandir making a radical performance improvement over os.listdir." + [`source8 `_] + +* Avram L: "I'm testing our scandir for a project I'm working on. + Seems pretty solid, so first thing, just want to say nice work!" + [via personal email] + +Others have `requested a PyPI package`_ for it, which has been +created. See `PyPI package`_. + +.. _`requested a PyPI package`: https://github.com/benhoyt/scandir/issues/12 +.. _`PyPI package`: https://pypi.python.org/pypi/scandir + +GitHub stats don't mean too much, but scandir does have several +watchers, issues, forks, etc. Here's the run-down as of the stats as +of June 5, 2014: + +* Watchers: 17 +* Stars: 48 +* Forks: 15 +* Issues: 2 open, 19 closed + +**However, the much larger point is this:**, if this PEP is accepted, +``os.walk()`` can easily be reimplemented using ``scandir`` rather +than ``listdir`` and ``stat``, increasing the speed of ``os.walk()`` +very significantly. There are thousands of developers, scripts, and +production code that would benefit from this large speedup of +``os.walk()``. For example, on GitHub, there are almost as many uses +of ``os.walk`` (194,000) as there are of ``os.mkdir`` (230,000). + + +Open issues and optional things +=============================== + +There are a few open issues or optional additions: + + +Should scandir be in its own module? +------------------------------------ + +Should the function be included in the standard library in a new +module, ``scandir.scandir()``, or just as ``os.scandir()`` as +discussed? The preference of this PEP's author (Ben Hoyt) would be +``os.scandir()``, as it's just a single function. + + +Should there be a way to access the full path? +---------------------------------------------- + +Should ``DirEntry``'s have a way to get the full path without using +``os.path.join(path, entry.name)``? This is a pretty common pattern, +and it may be useful to add pathlib-like ``str(entry)`` functionality. +This functionality has also been requested in `issue 13`_ on GitHub. + +.. _`issue 13`: https://github.com/benhoyt/scandir/issues/13 + + +Should it expose Windows wildcard functionality? +------------------------------------------------ + +Should ``scandir()`` have a way of exposing the wildcard functionality +in the Windows ``FindFirstFile`` / ``FindNextFile`` functions? The +scandir module on GitHub exposes this as a ``windows_wildcard`` +keyword argument, allowing Windows power users the option to pass a +custom wildcard to ``FindFirstFile``, which may avoid the need to use +``fnmatch`` or similar on the resulting names. It is named the +unwieldly ``windows_wildcard`` to remind you you're writing power- +user, Windows-only code if you use it. + +This boils down to whether ``scandir`` should be about exposing all of +the system's directory iteration features, or simply providing a fast, +simple, cross-platform directory iteration API. + +This PEP's author votes for not including ``windows_wildcard`` in the +standard library version, because even though it could be useful in +rare cases (say the Windows Dropbox client?), it'd be too easy to use +it just because you're a Windows developer, and create code that is +not cross-platform. + + +Possible improvements +===================== + +There are many possible improvements one could make to scandir, but +here is a short list of some this PEP's author has in mind: + +* scandir could potentially be further sped up by calling ``readdir`` + / ``FindNextFile`` say 50 times per ``Py_BEGIN_ALLOW_THREADS`` block + so that it stays in the C extension module for longer, and may be + somewhat faster as a result. This approach hasn't been tested, but + was suggested by on Issue 11406 by Antoine Pitrou. + [`source9 `_] + + +Previous discussion +=================== + +* `Original thread Ben Hoyt started on python-ideas`_ about speeding + up ``os.walk()`` + +* Python `Issue 11406`_, which includes the original proposal for a + scandir-like function + +* `Further thread Ben Hoyt started on python-dev`_ that refined the + ``scandir()`` API, including Nick Coghlan's suggestion of scandir + yielding ``DirEntry``-like objects + +* `Final thread Ben Hoyt started on python-dev`_ to discuss the + interaction between scandir and the new ``pathlib`` module + +* `Question on StackOverflow`_ about why ``os.walk()`` is slow and + pointers on how to fix it (this inspired the author of this PEP + early on) + +* `BetterWalk`_, this PEP's author's previous attempt at this, on + which the scandir code is based + +.. _`Original thread Ben Hoyt started on python-ideas`: https://mail.python.org/pipermail/python-ideas/2012-November/017770.html +.. _`Further thread Ben Hoyt started on python-dev`: https://mail.python.org/pipermail/python-dev/2013-May/126119.html +.. _`Final thread Ben Hoyt started on python-dev`: https://mail.python.org/pipermail/python-dev/2013-November/130572.html +.. _`Question on StackOverflow`: http://stackoverflow.com/questions/2485719/very-quickly-getting-total-size-of-folder +.. _`BetterWalk`: https://github.com/benhoyt/betterwalk + + +Copyright +========= + +This document has been placed in the public domain. + + + +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: -- Repository URL: http://hg.python.org/peps From python-checkins at python.org Thu Jun 26 23:34:25 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 26 Jun 2014 23:34:25 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxODU4?= =?utf-8?q?=3A_Better_handling_of_Python_exceptions_in_the_sqlite3_module?= =?utf-8?q?=2E?= Message-ID: <3gzvcx69Cbz7LjM@mail.python.org> http://hg.python.org/cpython/rev/62612b195cb5 changeset: 91432:62612b195cb5 branch: 3.4 parent: 91429:ab708e4131dd user: Victor Stinner date: Thu Jun 26 23:32:00 2014 +0200 summary: Issue #21858: Better handling of Python exceptions in the sqlite3 module. files: Misc/NEWS | 2 + Modules/_sqlite/cursor.c | 42 +++++++++++++++++---------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,8 @@ Library ------- +- Issue #21858: Better handling of Python exceptions in the sqlite3 module. + - Issue #21476: Make sure the email.parser.BytesParser TextIOWrapper is discarded after parsing, so the input file isn't unexpectedly closed. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -289,9 +289,8 @@ Py_END_ALLOW_THREADS row = PyTuple_New(numcols); - if (!row) { + if (!row) return NULL; - } for (i = 0; i < numcols; i++) { if (self->connection->detect_types) { @@ -311,14 +310,12 @@ converted = Py_None; } else { item = PyBytes_FromStringAndSize(val_str, nbytes); - if (!item) { - return NULL; - } + if (!item) + goto error; converted = PyObject_CallFunction(converter, "O", item); Py_DECREF(item); - if (!converted) { + if (!converted) break; - } } } else { Py_BEGIN_ALLOW_THREADS @@ -374,9 +371,8 @@ nbytes = sqlite3_column_bytes(self->statement->st, i); buffer = PyBytes_FromStringAndSize( sqlite3_column_blob(self->statement->st, i), nbytes); - if (!buffer) { + if (!buffer) break; - } converted = buffer; } } @@ -389,12 +385,14 @@ } } - if (PyErr_Occurred()) { - Py_DECREF(row); - row = NULL; - } + if (PyErr_Occurred()) + goto error; return row; + +error: + Py_DECREF(row); + return NULL; } /* @@ -612,6 +610,10 @@ while (1) { /* Actually execute the SQL statement. */ rc = pysqlite_step(self->statement->st, self->connection); + if (PyErr_Occurred()) { + (void)pysqlite_statement_reset(self->statement); + goto error; + } if (rc == SQLITE_DONE || rc == SQLITE_ROW) { /* If it worked, let's get out of the loop */ break; @@ -685,6 +687,8 @@ } self->next_row = _pysqlite_fetch_one_row(self); + if (self->next_row == NULL) + goto error; } else if (rc == SQLITE_DONE && !multiple) { pysqlite_statement_reset(self->statement); Py_CLEAR(self->statement); @@ -807,7 +811,10 @@ rc = SQLITE_ROW; while (rc == SQLITE_ROW) { rc = pysqlite_step(statement, self->connection); - /* TODO: we probably need more error handling here */ + if (PyErr_Occurred()) { + (void)sqlite3_finalize(statement); + goto error; + } } if (rc != SQLITE_DONE) { @@ -884,6 +891,11 @@ if (self->statement) { rc = pysqlite_step(self->statement->st, self->connection); + if (PyErr_Occurred()) { + (void)pysqlite_statement_reset(self->statement); + Py_DECREF(next_row); + return NULL; + } if (rc != SQLITE_DONE && rc != SQLITE_ROW) { (void)pysqlite_statement_reset(self->statement); Py_DECREF(next_row); @@ -895,8 +907,6 @@ self->next_row = _pysqlite_fetch_one_row(self); if (self->next_row == NULL) { (void)pysqlite_statement_reset(self->statement); - Py_DECREF(next_row); - _pysqlite_seterror(self->connection->db, NULL); return NULL; } } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Thu Jun 26 23:34:27 2014 From: python-checkins at python.org (victor.stinner) Date: Thu, 26 Jun 2014 23:34:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321858=3A_Better_handling_of_P?= =?utf-8?q?ython_exceptions_in_the_sqlite3?= Message-ID: <3gzvcz1qy7z7LkK@mail.python.org> http://hg.python.org/cpython/rev/169171da66fa changeset: 91433:169171da66fa parent: 91430:bbb28082d7b4 parent: 91432:62612b195cb5 user: Victor Stinner date: Thu Jun 26 23:33:34 2014 +0200 summary: (Merge 3.4) Issue #21858: Better handling of Python exceptions in the sqlite3 module. files: Misc/NEWS | 2 + Modules/_sqlite/cursor.c | 42 +++++++++++++++++---------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,8 @@ Library ------- +- Issue #21858: Better handling of Python exceptions in the sqlite3 module. + - Issue #21476: Make sure the email.parser.BytesParser TextIOWrapper is discarded after parsing, so the input file isn't unexpectedly closed. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -289,9 +289,8 @@ Py_END_ALLOW_THREADS row = PyTuple_New(numcols); - if (!row) { + if (!row) return NULL; - } for (i = 0; i < numcols; i++) { if (self->connection->detect_types) { @@ -311,14 +310,12 @@ converted = Py_None; } else { item = PyBytes_FromStringAndSize(val_str, nbytes); - if (!item) { - return NULL; - } + if (!item) + goto error; converted = PyObject_CallFunction(converter, "O", item); Py_DECREF(item); - if (!converted) { + if (!converted) break; - } } } else { Py_BEGIN_ALLOW_THREADS @@ -374,9 +371,8 @@ nbytes = sqlite3_column_bytes(self->statement->st, i); buffer = PyBytes_FromStringAndSize( sqlite3_column_blob(self->statement->st, i), nbytes); - if (!buffer) { + if (!buffer) break; - } converted = buffer; } } @@ -389,12 +385,14 @@ } } - if (PyErr_Occurred()) { - Py_DECREF(row); - row = NULL; - } + if (PyErr_Occurred()) + goto error; return row; + +error: + Py_DECREF(row); + return NULL; } /* @@ -612,6 +610,10 @@ while (1) { /* Actually execute the SQL statement. */ rc = pysqlite_step(self->statement->st, self->connection); + if (PyErr_Occurred()) { + (void)pysqlite_statement_reset(self->statement); + goto error; + } if (rc == SQLITE_DONE || rc == SQLITE_ROW) { /* If it worked, let's get out of the loop */ break; @@ -685,6 +687,8 @@ } self->next_row = _pysqlite_fetch_one_row(self); + if (self->next_row == NULL) + goto error; } else if (rc == SQLITE_DONE && !multiple) { pysqlite_statement_reset(self->statement); Py_CLEAR(self->statement); @@ -807,7 +811,10 @@ rc = SQLITE_ROW; while (rc == SQLITE_ROW) { rc = pysqlite_step(statement, self->connection); - /* TODO: we probably need more error handling here */ + if (PyErr_Occurred()) { + (void)sqlite3_finalize(statement); + goto error; + } } if (rc != SQLITE_DONE) { @@ -884,6 +891,11 @@ if (self->statement) { rc = pysqlite_step(self->statement->st, self->connection); + if (PyErr_Occurred()) { + (void)pysqlite_statement_reset(self->statement); + Py_DECREF(next_row); + return NULL; + } if (rc != SQLITE_DONE && rc != SQLITE_ROW) { (void)pysqlite_statement_reset(self->statement); Py_DECREF(next_row); @@ -895,8 +907,6 @@ self->next_row = _pysqlite_fetch_one_row(self); if (self->next_row == NULL) { (void)pysqlite_statement_reset(self->statement); - Py_DECREF(next_row); - _pysqlite_seterror(self->connection->db, NULL); return NULL; } } -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 08:29:32 2014 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 27 Jun 2014 08:29:32 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogZG9uJ3Qgb3Zlcndy?= =?utf-8?q?ite_the_error_from_PyObject=5FGetAttrString_=28closes_=234346?= =?utf-8?q?=29?= Message-ID: <3h07VN4gcWz7Ljh@mail.python.org> http://hg.python.org/cpython/rev/aa4b4487c7ad changeset: 91434:aa4b4487c7ad branch: 2.7 parent: 91431:eb0921b2100b user: Benjamin Peterson date: Thu Jun 26 23:27:41 2014 -0700 summary: don't overwrite the error from PyObject_GetAttrString (closes #4346) files: Misc/NEWS | 3 +++ Objects/abstract.c | 12 ++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #4346: In PyObject_CallMethod and PyObject_CallMethodObjArgs, don't + overwrite the error set in PyObject_GetAttr. + - Issue #21831: Avoid integer overflow when large sizes and offsets are given to the buffer type. diff --git a/Objects/abstract.c b/Objects/abstract.c --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2617,10 +2617,8 @@ return null_error(); func = PyObject_GetAttrString(o, name); - if (func == NULL) { - PyErr_SetString(PyExc_AttributeError, name); - return 0; - } + if (func == NULL) + return NULL; if (!PyCallable_Check(func)) { type_error("attribute of type '%.200s' is not callable", func); @@ -2656,10 +2654,8 @@ return null_error(); func = PyObject_GetAttrString(o, name); - if (func == NULL) { - PyErr_SetString(PyExc_AttributeError, name); - return 0; - } + if (func == NULL) + return NULL; if (!PyCallable_Check(func)) { type_error("attribute of type '%.200s' is not callable", func); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 08:29:33 2014 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 27 Jun 2014 08:29:33 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_use_NULL_not_0?= Message-ID: <3h07VP6Pjtz7Ljr@mail.python.org> http://hg.python.org/cpython/rev/92521be371c5 changeset: 91435:92521be371c5 branch: 3.4 parent: 91432:62612b195cb5 user: Benjamin Peterson date: Thu Jun 26 23:29:13 2014 -0700 summary: use NULL not 0 files: Objects/abstract.c | 15 ++++++--------- 1 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2191,9 +2191,8 @@ return null_error(); func = PyObject_GetAttrString(o, name); - if (func == NULL) { - return 0; - } + if (func == NULL) + return NULL; va_start(va, format); retval = callmethod(func, format, va, 0); @@ -2213,9 +2212,8 @@ return null_error(); func = _PyObject_GetAttrId(o, name); - if (func == NULL) { - return 0; - } + if (func == NULL) + return NULL; va_start(va, format); retval = callmethod(func, format, va, 0); @@ -2235,9 +2233,8 @@ return null_error(); func = PyObject_GetAttrString(o, name); - if (func == NULL) { - return 0; - } + if (func == NULL) + return NULL; va_start(va, format); retval = callmethod(func, format, va, 1); va_end(va); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 08:29:35 2014 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 27 Jun 2014 08:29:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <3h07VR0pC0z7LkH@mail.python.org> http://hg.python.org/cpython/rev/738262a327af changeset: 91436:738262a327af parent: 91433:169171da66fa parent: 91435:92521be371c5 user: Benjamin Peterson date: Thu Jun 26 23:29:19 2014 -0700 summary: merge 3.4 files: Objects/abstract.c | 15 ++++++--------- 1 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2205,9 +2205,8 @@ return null_error(); func = PyObject_GetAttrString(o, name); - if (func == NULL) { - return 0; - } + if (func == NULL) + return NULL; va_start(va, format); retval = callmethod(func, format, va, 0); @@ -2227,9 +2226,8 @@ return null_error(); func = _PyObject_GetAttrId(o, name); - if (func == NULL) { - return 0; - } + if (func == NULL) + return NULL; va_start(va, format); retval = callmethod(func, format, va, 0); @@ -2249,9 +2247,8 @@ return null_error(); func = PyObject_GetAttrString(o, name); - if (func == NULL) { - return 0; - } + if (func == NULL) + return NULL; va_start(va, format); retval = callmethod(func, format, va, 1); va_end(va); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 08:43:13 2014 From: python-checkins at python.org (ned.deily) Date: Fri, 27 Jun 2014 08:43:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxODc1?= =?utf-8?q?=3A_Remove_vestigial_references_to_Classic_Mac_OS_in_os_module_?= =?utf-8?q?docs=2E?= Message-ID: <3h07p96K90z7LjM@mail.python.org> http://hg.python.org/cpython/rev/94f7cdab9f71 changeset: 91437:94f7cdab9f71 branch: 2.7 parent: 91434:aa4b4487c7ad user: Ned Deily date: Thu Jun 26 23:38:14 2014 -0700 summary: Issue #21875: Remove vestigial references to Classic Mac OS in os module docs. files: Doc/library/os.rst | 11 ++--------- Lib/os.py | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1382,9 +1382,8 @@ .. versionchanged:: 2.3 If :func:`stat_float_times` returns ``True``, the time values are floats, measuring - seconds. Fractions of a second may be reported if the system supports that. On - Mac OS, the times are always floats. See :func:`stat_float_times` for further - discussion. + seconds. Fractions of a second may be reported if the system supports that. + See :func:`stat_float_times` for further discussion. On some Unix systems (such as Linux), the following attributes may also be available: @@ -1400,12 +1399,6 @@ * :attr:`st_gen` - file generation number * :attr:`st_birthtime` - time of file creation - On Mac OS systems, the following attributes may also be available: - - * :attr:`st_rsize` - * :attr:`st_creator` - * :attr:`st_type` - On RISCOS systems, the following attributes are also available: * :attr:`st_ftype` (file type) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -1,4 +1,4 @@ -r"""OS routines for Mac, NT, or Posix depending on what system we're on. +r"""OS routines for NT or Posix depending on what system we're on. This exports: - all functions from posix, nt, os2, or ce, e.g. unlink, stat, etc. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 08:43:15 2014 From: python-checkins at python.org (ned.deily) Date: Fri, 27 Jun 2014 08:43:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxODc1?= =?utf-8?q?=3A_Remove_vestigial_references_to_Classic_Mac_OS_in_os_module_?= =?utf-8?q?docs=2E?= Message-ID: <3h07pC12Mqz7LkK@mail.python.org> http://hg.python.org/cpython/rev/d130a04fa6a1 changeset: 91438:d130a04fa6a1 branch: 3.4 parent: 91435:92521be371c5 user: Ned Deily date: Thu Jun 26 23:40:06 2014 -0700 summary: Issue #21875: Remove vestigial references to Classic Mac OS in os module docs. files: Doc/library/os.rst | 8 +------- Lib/os.py | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -53,7 +53,7 @@ .. data:: name The name of the operating system dependent module imported. The following - names have currently been registered: ``'posix'``, ``'nt'``, ``'mac'``, + names have currently been registered: ``'posix'``, ``'nt'``, ``'ce'``, ``'java'``. .. seealso:: @@ -1891,12 +1891,6 @@ * :attr:`st_gen` - file generation number * :attr:`st_birthtime` - time of file creation - On Mac OS systems, the following attributes may also be available: - - * :attr:`st_rsize` - * :attr:`st_creator` - * :attr:`st_type` - .. note:: The exact meaning and resolution of the :attr:`st_atime`, diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -1,4 +1,4 @@ -r"""OS routines for Mac, NT, or Posix depending on what system we're on. +r"""OS routines for NT or Posix depending on what system we're on. This exports: - all functions from posix, nt or ce, e.g. unlink, stat, etc. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 08:43:16 2014 From: python-checkins at python.org (ned.deily) Date: Fri, 27 Jun 2014 08:43:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321875=3A_Remove_vestigial_references_to_Classic?= =?utf-8?q?_Mac_OS_in_os_module_docs=2E?= Message-ID: <3h07pD3X8kz7LjM@mail.python.org> http://hg.python.org/cpython/rev/3124790c07b4 changeset: 91439:3124790c07b4 parent: 91436:738262a327af parent: 91438:d130a04fa6a1 user: Ned Deily date: Thu Jun 26 23:42:38 2014 -0700 summary: Issue #21875: Remove vestigial references to Classic Mac OS in os module docs. files: Doc/library/os.rst | 8 +------- Lib/os.py | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -53,7 +53,7 @@ .. data:: name The name of the operating system dependent module imported. The following - names have currently been registered: ``'posix'``, ``'nt'``, ``'mac'``, + names have currently been registered: ``'posix'``, ``'nt'``, ``'ce'``, ``'java'``. .. seealso:: @@ -1899,12 +1899,6 @@ * :attr:`st_gen` - file generation number * :attr:`st_birthtime` - time of file creation - On Mac OS systems, the following attributes may also be available: - - * :attr:`st_rsize` - * :attr:`st_creator` - * :attr:`st_type` - On Windows systems, the following attribute is also available: * :attr:`st_file_attributes` - Windows file attribute bits (see the diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -1,4 +1,4 @@ -r"""OS routines for Mac, NT, or Posix depending on what system we're on. +r"""OS routines for NT or Posix depending on what system we're on. This exports: - all functions from posix, nt or ce, e.g. unlink, stat, etc. -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Fri Jun 27 10:10:32 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 27 Jun 2014 10:10:32 +0200 Subject: [Python-checkins] Daily reference leaks (169171da66fa): sum=23 Message-ID: results for 169171da66fa on branch "default" -------------------------------------------- test_asyncio leaked [0, 4, 0] memory blocks, sum=4 test_collections leaked [0, 4, 0] references, sum=4 test_collections leaked [0, 2, 0] memory blocks, sum=2 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [2, 0, 0] references, sum=2 test_site leaked [2, 0, 0] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog71yKqN', '-x'] From python-checkins at python.org Fri Jun 27 12:25:27 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 12:25:27 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogT29w?= =?utf-8?q?s=2C_restore_a_removed_test?= Message-ID: <3h0Dkb4mK0z7Ljq@mail.python.org> http://hg.python.org/cpython/rev/1cd4210c050b changeset: 91440:1cd4210c050b branch: 3.4 parent: 91438:d130a04fa6a1 user: Victor Stinner date: Fri Jun 27 12:23:41 2014 +0200 summary: asyncio: Oops, restore a removed test files: Lib/test/test_asyncio/test_tasks.py | 3 +++ 1 files changed, 3 insertions(+), 0 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 @@ -1529,6 +1529,9 @@ def test_corowrapper_weakref(self): wd = weakref.WeakValueDictionary() def foo(): yield from [] + cw = asyncio.tasks.CoroWrapper(foo(), foo) + wd['cw'] = cw # Would fail without __weakref__ slot. + cw.gen = None # Suppress warning from __del__. @unittest.skipUnless(PY34, 'need python 3.4 or later') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 12:25:28 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 12:25:28 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Oops=2C_restore_a_removed_t?= =?utf-8?q?est?= Message-ID: <3h0Dkc6Sy7z7LkS@mail.python.org> http://hg.python.org/cpython/rev/4be3ee53c62c changeset: 91441:4be3ee53c62c parent: 91439:3124790c07b4 parent: 91440:1cd4210c050b user: Victor Stinner date: Fri Jun 27 12:24:14 2014 +0200 summary: (Merge 3.4) asyncio: Oops, restore a removed test files: Lib/test/test_asyncio/test_tasks.py | 3 +++ 1 files changed, 3 insertions(+), 0 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 @@ -1529,6 +1529,9 @@ def test_corowrapper_weakref(self): wd = weakref.WeakValueDictionary() def foo(): yield from [] + cw = asyncio.tasks.CoroWrapper(foo(), foo) + wd['cw'] = cw # Would fail without __weakref__ slot. + cw.gen = None # Suppress warning from __del__. @unittest.skipUnless(PY34, 'need python 3.4 or later') -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 12:30:47 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 12:30:47 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbywgVHVs?= =?utf-8?q?ip_issue_137=3A_In_debug_mode=2C_add_the_traceback_where_the_co?= =?utf-8?q?routine?= Message-ID: <3h0Drl2cHyz7LjM@mail.python.org> http://hg.python.org/cpython/rev/84816348e809 changeset: 91442:84816348e809 branch: 3.4 parent: 91440:1cd4210c050b user: Victor Stinner date: Fri Jun 27 12:28:41 2014 +0200 summary: asyncio, Tulip issue 137: In debug mode, add the traceback where the coroutine object was created to the "coroutine ... was never yield from" log files: Lib/asyncio/tasks.py | 17 +++++--- Lib/test/test_asyncio/test_tasks.py | 32 +++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -43,6 +43,7 @@ assert inspect.isgenerator(gen), gen self.gen = gen self.func = func + self._source_traceback = traceback.extract_stack(sys._getframe(1)) def __iter__(self): return self @@ -81,13 +82,13 @@ gen = getattr(self, 'gen', None) frame = getattr(gen, 'gi_frame', None) if frame is not None and frame.f_lasti == -1: - func = self.func - code = func.__code__ - filename = code.co_filename - lineno = code.co_firstlineno - logger.error( - 'Coroutine %r defined at %s:%s was never yielded from', - func.__name__, filename, lineno) + func = events._format_callback(self.func, ()) + tb = ''.join(traceback.format_list(self._source_traceback)) + message = ('Coroutine %s was never yielded from\n' + 'Coroutine object created at (most recent call last):\n' + '%s' + % (func, tb.rstrip())) + logger.error(message) def coroutine(func): @@ -112,6 +113,8 @@ @functools.wraps(func) def wrapper(*args, **kwds): w = CoroWrapper(coro(*args, **kwds), func) + if w._source_traceback: + del w._source_traceback[-1] w.__name__ = func.__name__ if _PY35: w.__qualname__ = func.__qualname__ 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 @@ -1,6 +1,7 @@ """Tests for tasks.py.""" import os.path +import re import sys import types import unittest @@ -1572,6 +1573,37 @@ }) mock_handler.reset_mock() + @mock.patch('asyncio.tasks.logger') + def test_coroutine_never_yielded(self, m_log): + debug = asyncio.tasks._DEBUG + try: + asyncio.tasks._DEBUG = True + @asyncio.coroutine + def coro_noop(): + pass + finally: + asyncio.tasks._DEBUG = debug + + tb_filename = __file__ + tb_lineno = sys._getframe().f_lineno + 1 + coro = coro_noop() + coro = None + support.gc_collect() + + self.assertTrue(m_log.error.called) + message = m_log.error.call_args[0][0] + func_filename, func_lineno = test_utils.get_function_source(coro_noop) + regex = (r'^Coroutine %s\(\) at %s:%s was never yielded from\n' + r'Coroutine object created at \(most recent call last\):\n' + r'.*\n' + r' File "%s", line %s, in test_coroutine_never_yielded\n' + r' coro = coro_noop\(\)$' + % (re.escape(coro_noop.__qualname__), + func_filename, func_lineno, + tb_filename, tb_lineno)) + + self.assertRegex(message, re.compile(regex, re.DOTALL)) + class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 12:30:48 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 12:30:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_=28Merge_3=2E4=29_asyncio?= =?utf-8?q?=2C_Tulip_issue_137=3A_In_debug_mode=2C_add_the_traceback_where?= Message-ID: <3h0Drm43Xcz7LjM@mail.python.org> http://hg.python.org/cpython/rev/d8c24c25c42d changeset: 91443:d8c24c25c42d parent: 91441:4be3ee53c62c user: Victor Stinner date: Fri Jun 27 12:29:30 2014 +0200 summary: (Merge 3.4) asyncio, Tulip issue 137: In debug mode, add the traceback where the coroutine object was created to the "coroutine ... was never yield from" log files: Lib/asyncio/tasks.py | 17 +++++--- Lib/test/test_asyncio/test_tasks.py | 32 +++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -43,6 +43,7 @@ assert inspect.isgenerator(gen), gen self.gen = gen self.func = func + self._source_traceback = traceback.extract_stack(sys._getframe(1)) def __iter__(self): return self @@ -81,13 +82,13 @@ gen = getattr(self, 'gen', None) frame = getattr(gen, 'gi_frame', None) if frame is not None and frame.f_lasti == -1: - func = self.func - code = func.__code__ - filename = code.co_filename - lineno = code.co_firstlineno - logger.error( - 'Coroutine %r defined at %s:%s was never yielded from', - func.__name__, filename, lineno) + func = events._format_callback(self.func, ()) + tb = ''.join(traceback.format_list(self._source_traceback)) + message = ('Coroutine %s was never yielded from\n' + 'Coroutine object created at (most recent call last):\n' + '%s' + % (func, tb.rstrip())) + logger.error(message) def coroutine(func): @@ -112,6 +113,8 @@ @functools.wraps(func) def wrapper(*args, **kwds): w = CoroWrapper(coro(*args, **kwds), func) + if w._source_traceback: + del w._source_traceback[-1] w.__name__ = func.__name__ if _PY35: w.__qualname__ = func.__qualname__ 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 @@ -1,6 +1,7 @@ """Tests for tasks.py.""" import os.path +import re import sys import types import unittest @@ -1572,6 +1573,37 @@ }) mock_handler.reset_mock() + @mock.patch('asyncio.tasks.logger') + def test_coroutine_never_yielded(self, m_log): + debug = asyncio.tasks._DEBUG + try: + asyncio.tasks._DEBUG = True + @asyncio.coroutine + def coro_noop(): + pass + finally: + asyncio.tasks._DEBUG = debug + + tb_filename = __file__ + tb_lineno = sys._getframe().f_lineno + 1 + coro = coro_noop() + coro = None + support.gc_collect() + + self.assertTrue(m_log.error.called) + message = m_log.error.call_args[0][0] + func_filename, func_lineno = test_utils.get_function_source(coro_noop) + regex = (r'^Coroutine %s\(\) at %s:%s was never yielded from\n' + r'Coroutine object created at \(most recent call last\):\n' + r'.*\n' + r' File "%s", line %s, in test_coroutine_never_yielded\n' + r' coro = coro_noop\(\)$' + % (re.escape(coro_noop.__qualname__), + func_filename, func_lineno, + tb_filename, tb_lineno)) + + self.assertRegex(message, re.compile(regex, re.DOTALL)) + class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 14:00:17 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 14:00:17 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbywgVHVs?= =?utf-8?q?ip_issue_137=3A_In_debug_mode=2C_save_traceback_where_Future=2C?= =?utf-8?q?_Task_and?= Message-ID: <3h0Gr112kkz7Lks@mail.python.org> http://hg.python.org/cpython/rev/8fb489a8eb28 changeset: 91444:8fb489a8eb28 branch: 3.4 parent: 91442:84816348e809 user: Victor Stinner date: Fri Jun 27 13:52:20 2014 +0200 summary: asyncio, Tulip issue 137: In debug mode, save traceback where Future, Task and Handle objects are created. Pass the traceback to call_exception_handler() in the 'source_traceback' key. The traceback is truncated to hide internal calls in asyncio, show only the traceback from user code. Add tests for the new source_traceback, and a test for the 'Future/Task exception was never retrieved' log. files: Lib/asyncio/base_events.py | 26 ++++- Lib/asyncio/events.py | 18 ++- Lib/asyncio/futures.py | 29 +++- Lib/asyncio/tasks.py | 14 ++- Lib/test/test_asyncio/test_base_events.py | 9 +- Lib/test/test_asyncio/test_events.py | 37 ++++++- Lib/test/test_asyncio/test_futures.py | 59 +++++++++++ Lib/test/test_asyncio/test_tasks.py | 14 ++ 8 files changed, 180 insertions(+), 26 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 @@ -21,6 +21,7 @@ import logging import socket import subprocess +import traceback import time import os import sys @@ -290,7 +291,10 @@ Any positional arguments after the callback will be passed to the callback when it is called. """ - return self.call_at(self.time() + delay, callback, *args) + timer = self.call_at(self.time() + delay, callback, *args) + if timer._source_traceback: + del timer._source_traceback[-1] + return timer def call_at(self, when, callback, *args): """Like call_later(), but uses an absolute time.""" @@ -299,6 +303,8 @@ if self._debug: self._assert_is_current_event_loop() timer = events.TimerHandle(when, callback, args, self) + if timer._source_traceback: + del timer._source_traceback[-1] heapq.heappush(self._scheduled, timer) return timer @@ -312,7 +318,10 @@ Any positional arguments after the callback will be passed to the callback when it is called. """ - return self._call_soon(callback, args, check_loop=True) + handle = self._call_soon(callback, args, check_loop=True) + if handle._source_traceback: + del handle._source_traceback[-1] + return handle def _call_soon(self, callback, args, check_loop): if tasks.iscoroutinefunction(callback): @@ -320,6 +329,8 @@ if self._debug and check_loop: self._assert_is_current_event_loop() handle = events.Handle(callback, args, self) + if handle._source_traceback: + del handle._source_traceback[-1] self._ready.append(handle) return handle @@ -344,6 +355,8 @@ def call_soon_threadsafe(self, callback, *args): """Like call_soon(), but thread safe.""" handle = self._call_soon(callback, args, check_loop=False) + if handle._source_traceback: + del handle._source_traceback[-1] self._write_to_self() return handle @@ -757,7 +770,14 @@ for key in sorted(context): if key in {'message', 'exception'}: continue - log_lines.append('{}: {!r}'.format(key, context[key])) + value = context[key] + if key == 'source_traceback': + tb = ''.join(traceback.format_list(value)) + value = 'Object created at (most recent call last):\n' + value += tb.rstrip() + else: + value = repr(value) + log_lines.append('{}: {}'.format(key, value)) logger.error('\n'.join(log_lines), exc_info=exc_info) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -11,6 +11,7 @@ import functools import inspect import subprocess +import traceback import threading import socket import sys @@ -66,7 +67,8 @@ class Handle: """Object returned by callback registration methods.""" - __slots__ = ['_callback', '_args', '_cancelled', '_loop', '__weakref__'] + __slots__ = ('_callback', '_args', '_cancelled', '_loop', + '_source_traceback', '__weakref__') def __init__(self, callback, args, loop): assert not isinstance(callback, Handle), 'A Handle is not a callback' @@ -74,6 +76,10 @@ self._callback = callback self._args = args self._cancelled = False + if self._loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + else: + self._source_traceback = None def __repr__(self): info = [] @@ -91,11 +97,14 @@ except Exception as exc: cb = _format_callback(self._callback, self._args) msg = 'Exception in callback {}'.format(cb) - self._loop.call_exception_handler({ + context = { 'message': msg, 'exception': exc, 'handle': self, - }) + } + if self._source_traceback: + context['source_traceback'] = self._source_traceback + self._loop.call_exception_handler(context) self = None # Needed to break cycles when an exception occurs. @@ -107,7 +116,8 @@ def __init__(self, when, callback, args, loop): assert when is not None super().__init__(callback, args, loop) - + if self._source_traceback: + del self._source_traceback[-1] self._when = when def __repr__(self): diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -82,10 +82,11 @@ in a discussion about closing files when they are collected. """ - __slots__ = ['exc', 'tb', 'loop'] + __slots__ = ('loop', 'source_traceback', 'exc', 'tb') - def __init__(self, exc, loop): - self.loop = loop + def __init__(self, future, exc): + self.loop = future._loop + self.source_traceback = future._source_traceback self.exc = exc self.tb = None @@ -102,11 +103,12 @@ def __del__(self): if self.tb: - msg = 'Future/Task exception was never retrieved:\n{tb}' - context = { - 'message': msg.format(tb=''.join(self.tb)), - } - self.loop.call_exception_handler(context) + msg = 'Future/Task exception was never retrieved' + if self.source_traceback: + msg += '\nFuture/Task created at (most recent call last):\n' + msg += ''.join(traceback.format_list(self.source_traceback)) + msg += ''.join(self.tb).rstrip() + self.loop.call_exception_handler({'message': msg}) class Future: @@ -149,6 +151,10 @@ else: self._loop = loop self._callbacks = [] + if self._loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + else: + self._source_traceback = None def _format_callbacks(self): cb = self._callbacks @@ -196,10 +202,13 @@ return exc = self._exception context = { - 'message': 'Future/Task exception was never retrieved', + 'message': ('%s exception was never retrieved' + % self.__class__.__name__), 'exception': exc, 'future': self, } + if self._source_traceback: + context['source_traceback'] = self._source_traceback self._loop.call_exception_handler(context) def cancel(self): @@ -335,7 +344,7 @@ if _PY34: self._log_traceback = True else: - self._tb_logger = _TracebackLogger(exception, self._loop) + self._tb_logger = _TracebackLogger(self, exception) # Arrange for the logger to be activated after all callbacks # have had a chance to call result() or exception(). self._loop.call_soon(self._tb_logger.activate) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -195,6 +195,8 @@ def __init__(self, coro, *, loop=None): assert iscoroutine(coro), repr(coro) # Not a coroutine function! super().__init__(loop=loop) + if self._source_traceback: + del self._source_traceback[-1] self._coro = iter(coro) # Use the iterator just in case. self._fut_waiter = None self._must_cancel = False @@ -207,10 +209,13 @@ if _PY34: def __del__(self): if self._state == futures._PENDING: - self._loop.call_exception_handler({ + context = { 'task': self, 'message': 'Task was destroyed but it is pending!', - }) + } + if self._source_traceback: + context['source_traceback'] = self._source_traceback + self._loop.call_exception_handler(context) futures.Future.__del__(self) def __repr__(self): @@ -620,7 +625,10 @@ raise ValueError('loop argument must agree with Future') return coro_or_future elif iscoroutine(coro_or_future): - return Task(coro_or_future, loop=loop) + task = Task(coro_or_future, loop=loop) + if task._source_traceback: + del task._source_traceback[-1] + return task else: raise TypeError('A Future or coroutine is required') 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 @@ -406,19 +406,22 @@ 1/0 def run_loop(): - self.loop.call_soon(zero_error) + handle = self.loop.call_soon(zero_error) self.loop._run_once() + return handle + self.loop.set_debug(True) self.loop._process_events = mock.Mock() mock_handler = mock.Mock() self.loop.set_exception_handler(mock_handler) - run_loop() + handle = run_loop() mock_handler.assert_called_with(self.loop, { 'exception': MOCK_ANY, 'message': test_utils.MockPattern( 'Exception in callback.*zero_error'), - 'handle': MOCK_ANY, + 'handle': handle, + 'source_traceback': handle._source_traceback, }) mock_handler.reset_mock() 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 @@ -1751,10 +1751,11 @@ pass -class HandleTests(unittest.TestCase): +class HandleTests(test_utils.TestCase): def setUp(self): - self.loop = None + self.loop = mock.Mock() + self.loop.get_debug.return_value = True def test_handle(self): def callback(*args): @@ -1789,7 +1790,8 @@ self.loop.call_exception_handler.assert_called_with({ 'message': test_utils.MockPattern('Exception in callback.*'), 'exception': mock.ANY, - 'handle': h + 'handle': h, + 'source_traceback': h._source_traceback, }) def test_handle_weakref(self): @@ -1837,6 +1839,35 @@ % (cb_regex, re.escape(filename), lineno)) self.assertRegex(repr(h), regex) + def test_handle_source_traceback(self): + loop = asyncio.get_event_loop_policy().new_event_loop() + loop.set_debug(True) + self.set_event_loop(loop) + + def check_source_traceback(h): + lineno = sys._getframe(1).f_lineno - 1 + self.assertIsInstance(h._source_traceback, list) + self.assertEqual(h._source_traceback[-1][:3], + (__file__, + lineno, + 'test_handle_source_traceback')) + + # call_soon + h = loop.call_soon(noop) + check_source_traceback(h) + + # call_soon_threadsafe + h = loop.call_soon_threadsafe(noop) + check_source_traceback(h) + + # call_later + h = loop.call_later(0, noop) + check_source_traceback(h) + + # call_at + h = loop.call_later(0, noop) + check_source_traceback(h) + class TimerTests(unittest.TestCase): diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -2,8 +2,10 @@ import concurrent.futures import re +import sys import threading import unittest +from test import support from unittest import mock import asyncio @@ -284,6 +286,63 @@ self.assertEqual(f1.result(), 42) self.assertTrue(f2.cancelled()) + def test_future_source_traceback(self): + self.loop.set_debug(True) + + future = asyncio.Future(loop=self.loop) + lineno = sys._getframe().f_lineno - 1 + self.assertIsInstance(future._source_traceback, list) + self.assertEqual(future._source_traceback[-1][:3], + (__file__, + lineno, + 'test_future_source_traceback')) + + @mock.patch('asyncio.base_events.logger') + def test_future_exception_never_retrieved(self, m_log): + self.loop.set_debug(True) + + def memroy_error(): + try: + raise MemoryError() + except BaseException as exc: + return exc + exc = memroy_error() + + future = asyncio.Future(loop=self.loop) + source_traceback = future._source_traceback + future.set_exception(exc) + future = None + test_utils.run_briefly(self.loop) + support.gc_collect() + + if sys.version_info >= (3, 4): + frame = source_traceback[-1] + regex = (r'^Future exception was never retrieved\n' + r'future: \n' + r'source_traceback: Object created at \(most recent call last\):\n' + r' File' + r'.*\n' + r' File "%s", line %s, in test_future_exception_never_retrieved\n' + r' future = asyncio\.Future\(loop=self\.loop\)$' + % (frame[0], frame[1])) + exc_info = (type(exc), exc, exc.__traceback__) + m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info) + else: + frame = source_traceback[-1] + regex = (r'^Future/Task exception was never retrieved\n' + r'Future/Task created at \(most recent call last\):\n' + r' File' + r'.*\n' + r' File "%s", line %s, in test_future_exception_never_retrieved\n' + r' future = asyncio\.Future\(loop=self\.loop\)\n' + r'Traceback \(most recent call last\):\n' + r'.*\n' + r'MemoryError$' + % (frame[0], frame[1])) + m_log.error.assert_called_once_with(mock.ANY, exc_info=False) + message = m_log.error.call_args[0][0] + self.assertRegex(message, re.compile(regex, re.DOTALL)) + class FutureDoneCallbackTests(test_utils.TestCase): 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 @@ -1546,6 +1546,7 @@ raise Exception("code never reached") mock_handler = mock.Mock() + self.loop.set_debug(True) self.loop.set_exception_handler(mock_handler) # schedule the task @@ -1560,6 +1561,7 @@ # remove the future used in kill_me(), and references to the task del coro.gi_frame.f_locals['future'] coro = None + source_traceback = task._source_traceback task = None # no more reference to kill_me() task: the task is destroyed by the GC @@ -1570,6 +1572,7 @@ mock_handler.assert_called_with(self.loop, { 'message': 'Task was destroyed but it is pending!', 'task': mock.ANY, + 'source_traceback': source_traceback, }) mock_handler.reset_mock() @@ -1604,6 +1607,17 @@ self.assertRegex(message, re.compile(regex, re.DOTALL)) + def test_task_source_traceback(self): + self.loop.set_debug(True) + + task = asyncio.Task(coroutine_function(), loop=self.loop) + lineno = sys._getframe().f_lineno - 1 + self.assertIsInstance(task._source_traceback, list) + self.assertEqual(task._source_traceback[-1][:3], + (__file__, + lineno, + 'test_task_source_traceback')) + class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 14:00:18 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 14:00:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=2C_Tulip_issue_137=3A_In_debug?= =?utf-8?q?_mode=2C_save_traceback_where?= Message-ID: <3h0Gr24jMTz7Lkr@mail.python.org> http://hg.python.org/cpython/rev/cadef389fdbc changeset: 91445:cadef389fdbc parent: 91443:d8c24c25c42d parent: 91444:8fb489a8eb28 user: Victor Stinner date: Fri Jun 27 13:55:28 2014 +0200 summary: (Merge 3.4) asyncio, Tulip issue 137: In debug mode, save traceback where Future, Task and Handle objects are created. Pass the traceback to call_exception_handler() in the 'source_traceback' key. The traceback is truncated to hide internal calls in asyncio, show only the traceback from user code. Add tests for the new source_traceback, and a test for the 'Future/Task exception was never retrieved' log. files: Lib/asyncio/base_events.py | 26 ++++- Lib/asyncio/events.py | 18 ++- Lib/asyncio/futures.py | 29 +++- Lib/asyncio/tasks.py | 14 ++- Lib/test/test_asyncio/test_base_events.py | 9 +- Lib/test/test_asyncio/test_events.py | 37 ++++++- Lib/test/test_asyncio/test_futures.py | 59 +++++++++++ Lib/test/test_asyncio/test_tasks.py | 14 ++ 8 files changed, 180 insertions(+), 26 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 @@ -21,6 +21,7 @@ import logging import socket import subprocess +import traceback import time import os import sys @@ -290,7 +291,10 @@ Any positional arguments after the callback will be passed to the callback when it is called. """ - return self.call_at(self.time() + delay, callback, *args) + timer = self.call_at(self.time() + delay, callback, *args) + if timer._source_traceback: + del timer._source_traceback[-1] + return timer def call_at(self, when, callback, *args): """Like call_later(), but uses an absolute time.""" @@ -299,6 +303,8 @@ if self._debug: self._assert_is_current_event_loop() timer = events.TimerHandle(when, callback, args, self) + if timer._source_traceback: + del timer._source_traceback[-1] heapq.heappush(self._scheduled, timer) return timer @@ -312,7 +318,10 @@ Any positional arguments after the callback will be passed to the callback when it is called. """ - return self._call_soon(callback, args, check_loop=True) + handle = self._call_soon(callback, args, check_loop=True) + if handle._source_traceback: + del handle._source_traceback[-1] + return handle def _call_soon(self, callback, args, check_loop): if tasks.iscoroutinefunction(callback): @@ -320,6 +329,8 @@ if self._debug and check_loop: self._assert_is_current_event_loop() handle = events.Handle(callback, args, self) + if handle._source_traceback: + del handle._source_traceback[-1] self._ready.append(handle) return handle @@ -344,6 +355,8 @@ def call_soon_threadsafe(self, callback, *args): """Like call_soon(), but thread safe.""" handle = self._call_soon(callback, args, check_loop=False) + if handle._source_traceback: + del handle._source_traceback[-1] self._write_to_self() return handle @@ -757,7 +770,14 @@ for key in sorted(context): if key in {'message', 'exception'}: continue - log_lines.append('{}: {!r}'.format(key, context[key])) + value = context[key] + if key == 'source_traceback': + tb = ''.join(traceback.format_list(value)) + value = 'Object created at (most recent call last):\n' + value += tb.rstrip() + else: + value = repr(value) + log_lines.append('{}: {}'.format(key, value)) logger.error('\n'.join(log_lines), exc_info=exc_info) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -11,6 +11,7 @@ import functools import inspect import subprocess +import traceback import threading import socket import sys @@ -66,7 +67,8 @@ class Handle: """Object returned by callback registration methods.""" - __slots__ = ['_callback', '_args', '_cancelled', '_loop', '__weakref__'] + __slots__ = ('_callback', '_args', '_cancelled', '_loop', + '_source_traceback', '__weakref__') def __init__(self, callback, args, loop): assert not isinstance(callback, Handle), 'A Handle is not a callback' @@ -74,6 +76,10 @@ self._callback = callback self._args = args self._cancelled = False + if self._loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + else: + self._source_traceback = None def __repr__(self): info = [] @@ -91,11 +97,14 @@ except Exception as exc: cb = _format_callback(self._callback, self._args) msg = 'Exception in callback {}'.format(cb) - self._loop.call_exception_handler({ + context = { 'message': msg, 'exception': exc, 'handle': self, - }) + } + if self._source_traceback: + context['source_traceback'] = self._source_traceback + self._loop.call_exception_handler(context) self = None # Needed to break cycles when an exception occurs. @@ -107,7 +116,8 @@ def __init__(self, when, callback, args, loop): assert when is not None super().__init__(callback, args, loop) - + if self._source_traceback: + del self._source_traceback[-1] self._when = when def __repr__(self): diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -82,10 +82,11 @@ in a discussion about closing files when they are collected. """ - __slots__ = ['exc', 'tb', 'loop'] + __slots__ = ('loop', 'source_traceback', 'exc', 'tb') - def __init__(self, exc, loop): - self.loop = loop + def __init__(self, future, exc): + self.loop = future._loop + self.source_traceback = future._source_traceback self.exc = exc self.tb = None @@ -102,11 +103,12 @@ def __del__(self): if self.tb: - msg = 'Future/Task exception was never retrieved:\n{tb}' - context = { - 'message': msg.format(tb=''.join(self.tb)), - } - self.loop.call_exception_handler(context) + msg = 'Future/Task exception was never retrieved' + if self.source_traceback: + msg += '\nFuture/Task created at (most recent call last):\n' + msg += ''.join(traceback.format_list(self.source_traceback)) + msg += ''.join(self.tb).rstrip() + self.loop.call_exception_handler({'message': msg}) class Future: @@ -149,6 +151,10 @@ else: self._loop = loop self._callbacks = [] + if self._loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + else: + self._source_traceback = None def _format_callbacks(self): cb = self._callbacks @@ -196,10 +202,13 @@ return exc = self._exception context = { - 'message': 'Future/Task exception was never retrieved', + 'message': ('%s exception was never retrieved' + % self.__class__.__name__), 'exception': exc, 'future': self, } + if self._source_traceback: + context['source_traceback'] = self._source_traceback self._loop.call_exception_handler(context) def cancel(self): @@ -335,7 +344,7 @@ if _PY34: self._log_traceback = True else: - self._tb_logger = _TracebackLogger(exception, self._loop) + self._tb_logger = _TracebackLogger(self, exception) # Arrange for the logger to be activated after all callbacks # have had a chance to call result() or exception(). self._loop.call_soon(self._tb_logger.activate) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -195,6 +195,8 @@ def __init__(self, coro, *, loop=None): assert iscoroutine(coro), repr(coro) # Not a coroutine function! super().__init__(loop=loop) + if self._source_traceback: + del self._source_traceback[-1] self._coro = iter(coro) # Use the iterator just in case. self._fut_waiter = None self._must_cancel = False @@ -207,10 +209,13 @@ if _PY34: def __del__(self): if self._state == futures._PENDING: - self._loop.call_exception_handler({ + context = { 'task': self, 'message': 'Task was destroyed but it is pending!', - }) + } + if self._source_traceback: + context['source_traceback'] = self._source_traceback + self._loop.call_exception_handler(context) futures.Future.__del__(self) def __repr__(self): @@ -620,7 +625,10 @@ raise ValueError('loop argument must agree with Future') return coro_or_future elif iscoroutine(coro_or_future): - return Task(coro_or_future, loop=loop) + task = Task(coro_or_future, loop=loop) + if task._source_traceback: + del task._source_traceback[-1] + return task else: raise TypeError('A Future or coroutine is required') 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 @@ -406,19 +406,22 @@ 1/0 def run_loop(): - self.loop.call_soon(zero_error) + handle = self.loop.call_soon(zero_error) self.loop._run_once() + return handle + self.loop.set_debug(True) self.loop._process_events = mock.Mock() mock_handler = mock.Mock() self.loop.set_exception_handler(mock_handler) - run_loop() + handle = run_loop() mock_handler.assert_called_with(self.loop, { 'exception': MOCK_ANY, 'message': test_utils.MockPattern( 'Exception in callback.*zero_error'), - 'handle': MOCK_ANY, + 'handle': handle, + 'source_traceback': handle._source_traceback, }) mock_handler.reset_mock() 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 @@ -1751,10 +1751,11 @@ pass -class HandleTests(unittest.TestCase): +class HandleTests(test_utils.TestCase): def setUp(self): - self.loop = None + self.loop = mock.Mock() + self.loop.get_debug.return_value = True def test_handle(self): def callback(*args): @@ -1789,7 +1790,8 @@ self.loop.call_exception_handler.assert_called_with({ 'message': test_utils.MockPattern('Exception in callback.*'), 'exception': mock.ANY, - 'handle': h + 'handle': h, + 'source_traceback': h._source_traceback, }) def test_handle_weakref(self): @@ -1837,6 +1839,35 @@ % (cb_regex, re.escape(filename), lineno)) self.assertRegex(repr(h), regex) + def test_handle_source_traceback(self): + loop = asyncio.get_event_loop_policy().new_event_loop() + loop.set_debug(True) + self.set_event_loop(loop) + + def check_source_traceback(h): + lineno = sys._getframe(1).f_lineno - 1 + self.assertIsInstance(h._source_traceback, list) + self.assertEqual(h._source_traceback[-1][:3], + (__file__, + lineno, + 'test_handle_source_traceback')) + + # call_soon + h = loop.call_soon(noop) + check_source_traceback(h) + + # call_soon_threadsafe + h = loop.call_soon_threadsafe(noop) + check_source_traceback(h) + + # call_later + h = loop.call_later(0, noop) + check_source_traceback(h) + + # call_at + h = loop.call_later(0, noop) + check_source_traceback(h) + class TimerTests(unittest.TestCase): diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -2,8 +2,10 @@ import concurrent.futures import re +import sys import threading import unittest +from test import support from unittest import mock import asyncio @@ -284,6 +286,63 @@ self.assertEqual(f1.result(), 42) self.assertTrue(f2.cancelled()) + def test_future_source_traceback(self): + self.loop.set_debug(True) + + future = asyncio.Future(loop=self.loop) + lineno = sys._getframe().f_lineno - 1 + self.assertIsInstance(future._source_traceback, list) + self.assertEqual(future._source_traceback[-1][:3], + (__file__, + lineno, + 'test_future_source_traceback')) + + @mock.patch('asyncio.base_events.logger') + def test_future_exception_never_retrieved(self, m_log): + self.loop.set_debug(True) + + def memroy_error(): + try: + raise MemoryError() + except BaseException as exc: + return exc + exc = memroy_error() + + future = asyncio.Future(loop=self.loop) + source_traceback = future._source_traceback + future.set_exception(exc) + future = None + test_utils.run_briefly(self.loop) + support.gc_collect() + + if sys.version_info >= (3, 4): + frame = source_traceback[-1] + regex = (r'^Future exception was never retrieved\n' + r'future: \n' + r'source_traceback: Object created at \(most recent call last\):\n' + r' File' + r'.*\n' + r' File "%s", line %s, in test_future_exception_never_retrieved\n' + r' future = asyncio\.Future\(loop=self\.loop\)$' + % (frame[0], frame[1])) + exc_info = (type(exc), exc, exc.__traceback__) + m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info) + else: + frame = source_traceback[-1] + regex = (r'^Future/Task exception was never retrieved\n' + r'Future/Task created at \(most recent call last\):\n' + r' File' + r'.*\n' + r' File "%s", line %s, in test_future_exception_never_retrieved\n' + r' future = asyncio\.Future\(loop=self\.loop\)\n' + r'Traceback \(most recent call last\):\n' + r'.*\n' + r'MemoryError$' + % (frame[0], frame[1])) + m_log.error.assert_called_once_with(mock.ANY, exc_info=False) + message = m_log.error.call_args[0][0] + self.assertRegex(message, re.compile(regex, re.DOTALL)) + class FutureDoneCallbackTests(test_utils.TestCase): 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 @@ -1546,6 +1546,7 @@ raise Exception("code never reached") mock_handler = mock.Mock() + self.loop.set_debug(True) self.loop.set_exception_handler(mock_handler) # schedule the task @@ -1560,6 +1561,7 @@ # remove the future used in kill_me(), and references to the task del coro.gi_frame.f_locals['future'] coro = None + source_traceback = task._source_traceback task = None # no more reference to kill_me() task: the task is destroyed by the GC @@ -1570,6 +1572,7 @@ mock_handler.assert_called_with(self.loop, { 'message': 'Task was destroyed but it is pending!', 'task': mock.ANY, + 'source_traceback': source_traceback, }) mock_handler.reset_mock() @@ -1604,6 +1607,17 @@ self.assertRegex(message, re.compile(regex, re.DOTALL)) + def test_task_source_traceback(self): + self.loop.set_debug(True) + + task = asyncio.Task(coroutine_function(), loop=self.loop) + lineno = sys._getframe().f_lineno - 1 + self.assertIsInstance(task._source_traceback, list) + self.assertEqual(task._source_traceback[-1][:3], + (__file__, + lineno, + 'test_task_source_traceback')) + class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 15:35:20 2014 From: python-checkins at python.org (ezio.melotti) Date: Fri, 27 Jun 2014 15:35:20 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fix_indentatio?= =?utf-8?q?n_and_class_name_in_socket_howto_example=2E?= Message-ID: <3h0Jxh5hk9z7Ljw@mail.python.org> http://hg.python.org/cpython/rev/e3f0347e682e changeset: 91446:e3f0347e682e branch: 3.4 parent: 91444:8fb489a8eb28 user: Ezio Melotti date: Fri Jun 27 16:34:14 2014 +0300 summary: Fix indentation and class name in socket howto example. files: Doc/howto/sockets.rst | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/howto/sockets.rst b/Doc/howto/sockets.rst --- a/Doc/howto/sockets.rst +++ b/Doc/howto/sockets.rst @@ -180,7 +180,7 @@ Assuming you don't want to end the connection, the simplest solution is a fixed length message:: - class mysocket: + class MySocket: """demonstration class only - coded for clarity, not efficiency """ @@ -189,8 +189,8 @@ if sock is None: self.sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM) - else: - self.sock = sock + else: + self.sock = sock def connect(self, host, port): self.sock.connect((host, port)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 15:35:21 2014 From: python-checkins at python.org (ezio.melotti) Date: Fri, 27 Jun 2014 15:35:21 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_socket_howto_fixes_from_3=2E4=2E?= Message-ID: <3h0Jxj721tz7Lkb@mail.python.org> http://hg.python.org/cpython/rev/61c41cf0b488 changeset: 91447:61c41cf0b488 parent: 91445:cadef389fdbc parent: 91446:e3f0347e682e user: Ezio Melotti date: Fri Jun 27 16:34:57 2014 +0300 summary: Merge socket howto fixes from 3.4. files: Doc/howto/sockets.rst | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/howto/sockets.rst b/Doc/howto/sockets.rst --- a/Doc/howto/sockets.rst +++ b/Doc/howto/sockets.rst @@ -180,7 +180,7 @@ Assuming you don't want to end the connection, the simplest solution is a fixed length message:: - class mysocket: + class MySocket: """demonstration class only - coded for clarity, not efficiency """ @@ -189,8 +189,8 @@ if sock is None: self.sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM) - else: - self.sock = sock + else: + self.sock = sock def connect(self, host, port): self.sock.connect((host, port)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 22:48:54 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 22:48:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogQ2xvc2VzICMyMTU4?= =?utf-8?q?2=3A_Cleanup_test=5Fasyncore=2E_Patch_written_by_diana=2E?= Message-ID: <3h0VYy1K9pz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/c2dba8ee4e96 changeset: 91448:c2dba8ee4e96 branch: 3.4 parent: 91446:e3f0347e682e user: Victor Stinner date: Fri Jun 27 22:44:40 2014 +0200 summary: Closes #21582: Cleanup test_asyncore. Patch written by diana. - Use support.captured_stderr() where appropriate - Removes some "from test.support import xxx" import and uses support.xxx instead. files: Lib/test/test_asyncore.py | 54 +++++++++------------------ 1 files changed, 18 insertions(+), 36 deletions(-) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -5,14 +5,12 @@ import socket import sys import time -import warnings import errno import struct +import warnings from test import support -from test.support import TESTFN, run_unittest, unlink, HOST, HOSTv6 from io import BytesIO -from io import StringIO try: import threading @@ -94,7 +92,7 @@ """Helper function to bind a socket according to its family.""" if HAS_UNIX_SOCKETS and sock.family == socket.AF_UNIX: # Make sure the path doesn't exist. - unlink(addr) + support.unlink(addr) sock.bind(addr) @@ -257,40 +255,29 @@ d = asyncore.dispatcher() # capture output of dispatcher.log() (to stderr) - fp = StringIO() - stderr = sys.stderr l1 = "Lovely spam! Wonderful spam!" l2 = "I don't like spam!" - try: - sys.stderr = fp + with support.captured_stderr() as stderr: d.log(l1) d.log(l2) - finally: - sys.stderr = stderr - lines = fp.getvalue().splitlines() + lines = stderr.getvalue().splitlines() self.assertEqual(lines, ['log: %s' % l1, 'log: %s' % l2]) def test_log_info(self): d = asyncore.dispatcher() # capture output of dispatcher.log_info() (to stdout via print) - fp = StringIO() - stdout = sys.stdout l1 = "Have you got anything without spam?" l2 = "Why can't she have egg bacon spam and sausage?" l3 = "THAT'S got spam in it!" - try: - sys.stdout = fp + with support.captured_stdout() as stdout: d.log_info(l1, 'EGGS') d.log_info(l2) d.log_info(l3, 'SPAM') - finally: - sys.stdout = stdout - lines = fp.getvalue().splitlines() + lines = stdout.getvalue().splitlines() expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3] - self.assertEqual(lines, expected) def test_unhandled(self): @@ -298,18 +285,13 @@ d.ignore_log_types = () # capture output of dispatcher.log_info() (to stdout via print) - fp = StringIO() - stdout = sys.stdout - try: - sys.stdout = fp + with support.captured_stdout() as stdout: d.handle_expt() d.handle_read() d.handle_write() d.handle_connect() - finally: - sys.stdout = stdout - lines = fp.getvalue().splitlines() + lines = stdout.getvalue().splitlines() expected = ['warning: unhandled incoming priority event', 'warning: unhandled read event', 'warning: unhandled write event', @@ -378,7 +360,7 @@ data = b"Suppose there isn't a 16-ton weight?" d = dispatcherwithsend_noread() d.create_socket() - d.connect((HOST, port)) + d.connect((support.HOST, port)) # give time for socket to connect time.sleep(0.1) @@ -410,14 +392,14 @@ class FileWrapperTest(unittest.TestCase): def setUp(self): self.d = b"It's not dead, it's sleeping!" - with open(TESTFN, 'wb') as file: + with open(support.TESTFN, 'wb') as file: file.write(self.d) def tearDown(self): - unlink(TESTFN) + support.unlink(support.TESTFN) def test_recv(self): - fd = os.open(TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) w = asyncore.file_wrapper(fd) os.close(fd) @@ -431,20 +413,20 @@ def test_send(self): d1 = b"Come again?" d2 = b"I want to buy some cheese." - fd = os.open(TESTFN, os.O_WRONLY | os.O_APPEND) + fd = os.open(support.TESTFN, os.O_WRONLY | os.O_APPEND) w = asyncore.file_wrapper(fd) os.close(fd) w.write(d1) w.send(d2) w.close() - with open(TESTFN, 'rb') as file: + with open(support.TESTFN, 'rb') as file: self.assertEqual(file.read(), self.d + d1 + d2) @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'), 'asyncore.file_dispatcher required') def test_dispatcher(self): - fd = os.open(TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) data = [] class FileDispatcher(asyncore.file_dispatcher): def handle_read(self): @@ -815,12 +797,12 @@ class TestAPI_UseIPv4Sockets(BaseTestAPI): family = socket.AF_INET - addr = (HOST, 0) + addr = (support.HOST, 0) @unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 support required') class TestAPI_UseIPv6Sockets(BaseTestAPI): family = socket.AF_INET6 - addr = (HOSTv6, 0) + addr = (support.HOSTv6, 0) @unittest.skipUnless(HAS_UNIX_SOCKETS, 'Unix sockets required') class TestAPI_UseUnixSockets(BaseTestAPI): @@ -829,7 +811,7 @@ addr = support.TESTFN def tearDown(self): - unlink(self.addr) + support.unlink(self.addr) BaseTestAPI.tearDown(self) class TestAPI_UseIPv4Select(TestAPI_UseIPv4Sockets, unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 22:48:55 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 22:48:55 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Closes_=2321582=3A_Cleanup_test=5Fasyn?= =?utf-8?q?core=2E_Patch_written_by_diana=2E?= Message-ID: <3h0VYz44d9z7Lk8@mail.python.org> http://hg.python.org/cpython/rev/f1cd0aa1561a changeset: 91449:f1cd0aa1561a parent: 91447:61c41cf0b488 parent: 91448:c2dba8ee4e96 user: Victor Stinner date: Fri Jun 27 22:47:41 2014 +0200 summary: (Merge 3.4) Closes #21582: Cleanup test_asyncore. Patch written by diana. - Use support.captured_stderr() where appropriate - Removes some "from test.support import xxx" import and uses support.xxx instead. files: Lib/test/test_asyncore.py | 53 ++++++++------------------ 1 files changed, 17 insertions(+), 36 deletions(-) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -5,14 +5,11 @@ import socket import sys import time -import warnings import errno import struct from test import support -from test.support import TESTFN, run_unittest, unlink, HOST, HOSTv6 from io import BytesIO -from io import StringIO try: import threading @@ -94,7 +91,7 @@ """Helper function to bind a socket according to its family.""" if HAS_UNIX_SOCKETS and sock.family == socket.AF_UNIX: # Make sure the path doesn't exist. - unlink(addr) + support.unlink(addr) sock.bind(addr) @@ -257,40 +254,29 @@ d = asyncore.dispatcher() # capture output of dispatcher.log() (to stderr) - fp = StringIO() - stderr = sys.stderr l1 = "Lovely spam! Wonderful spam!" l2 = "I don't like spam!" - try: - sys.stderr = fp + with support.captured_stderr() as stderr: d.log(l1) d.log(l2) - finally: - sys.stderr = stderr - lines = fp.getvalue().splitlines() + lines = stderr.getvalue().splitlines() self.assertEqual(lines, ['log: %s' % l1, 'log: %s' % l2]) def test_log_info(self): d = asyncore.dispatcher() # capture output of dispatcher.log_info() (to stdout via print) - fp = StringIO() - stdout = sys.stdout l1 = "Have you got anything without spam?" l2 = "Why can't she have egg bacon spam and sausage?" l3 = "THAT'S got spam in it!" - try: - sys.stdout = fp + with support.captured_stdout() as stdout: d.log_info(l1, 'EGGS') d.log_info(l2) d.log_info(l3, 'SPAM') - finally: - sys.stdout = stdout - lines = fp.getvalue().splitlines() + lines = stdout.getvalue().splitlines() expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3] - self.assertEqual(lines, expected) def test_unhandled(self): @@ -298,18 +284,13 @@ d.ignore_log_types = () # capture output of dispatcher.log_info() (to stdout via print) - fp = StringIO() - stdout = sys.stdout - try: - sys.stdout = fp + with support.captured_stdout() as stdout: d.handle_expt() d.handle_read() d.handle_write() d.handle_connect() - finally: - sys.stdout = stdout - lines = fp.getvalue().splitlines() + lines = stdout.getvalue().splitlines() expected = ['warning: unhandled incoming priority event', 'warning: unhandled read event', 'warning: unhandled write event', @@ -360,7 +341,7 @@ data = b"Suppose there isn't a 16-ton weight?" d = dispatcherwithsend_noread() d.create_socket() - d.connect((HOST, port)) + d.connect((support.HOST, port)) # give time for socket to connect time.sleep(0.1) @@ -388,14 +369,14 @@ class FileWrapperTest(unittest.TestCase): def setUp(self): self.d = b"It's not dead, it's sleeping!" - with open(TESTFN, 'wb') as file: + with open(support.TESTFN, 'wb') as file: file.write(self.d) def tearDown(self): - unlink(TESTFN) + support.unlink(support.TESTFN) def test_recv(self): - fd = os.open(TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) w = asyncore.file_wrapper(fd) os.close(fd) @@ -409,20 +390,20 @@ def test_send(self): d1 = b"Come again?" d2 = b"I want to buy some cheese." - fd = os.open(TESTFN, os.O_WRONLY | os.O_APPEND) + fd = os.open(support.TESTFN, os.O_WRONLY | os.O_APPEND) w = asyncore.file_wrapper(fd) os.close(fd) w.write(d1) w.send(d2) w.close() - with open(TESTFN, 'rb') as file: + with open(support.TESTFN, 'rb') as file: self.assertEqual(file.read(), self.d + d1 + d2) @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'), 'asyncore.file_dispatcher required') def test_dispatcher(self): - fd = os.open(TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) data = [] class FileDispatcher(asyncore.file_dispatcher): def handle_read(self): @@ -793,12 +774,12 @@ class TestAPI_UseIPv4Sockets(BaseTestAPI): family = socket.AF_INET - addr = (HOST, 0) + addr = (support.HOST, 0) @unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 support required') class TestAPI_UseIPv6Sockets(BaseTestAPI): family = socket.AF_INET6 - addr = (HOSTv6, 0) + addr = (support.HOSTv6, 0) @unittest.skipUnless(HAS_UNIX_SOCKETS, 'Unix sockets required') class TestAPI_UseUnixSockets(BaseTestAPI): @@ -807,7 +788,7 @@ addr = support.TESTFN def tearDown(self): - unlink(self.addr) + support.unlink(self.addr) BaseTestAPI.tearDown(self) class TestAPI_UseIPv4Select(TestAPI_UseIPv4Sockets, unittest.TestCase): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 23:52:51 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 23:52:51 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzExNDUz?= =?utf-8?q?=3A_asyncore=3A_emit_a_ResourceWarning_when_an_unclosed_file=5F?= =?utf-8?q?wrapper?= Message-ID: <3h0Wzl2xk6zPYS@mail.python.org> http://hg.python.org/cpython/rev/ae12a926e680 changeset: 91450:ae12a926e680 branch: 3.4 parent: 91448:c2dba8ee4e96 user: Victor Stinner date: Fri Jun 27 23:52:03 2014 +0200 summary: Issue #11453: asyncore: emit a ResourceWarning when an unclosed file_wrapper object is destroyed. The destructor now closes the file if needed. The close() method can now be called twice: the second call does nothing. files: Lib/asyncore.py | 8 ++++++++ Lib/test/test_asyncore.py | 16 ++++++++++++++++ Misc/NEWS | 4 ++++ 3 files changed, 28 insertions(+), 0 deletions(-) diff --git a/Lib/asyncore.py b/Lib/asyncore.py --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -614,6 +614,11 @@ def __init__(self, fd): self.fd = os.dup(fd) + def __del__(self): + if self.fd >= 0: + warnings.warn("unclosed file %r" % self, ResourceWarning) + self.close() + def recv(self, *args): return os.read(self.fd, *args) @@ -632,7 +637,10 @@ write = send def close(self): + if self.fd < 0: + return os.close(self.fd) + self.fd = -1 def fileno(self): return self.fd diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -436,6 +436,22 @@ asyncore.loop(timeout=0.01, use_poll=True, count=2) self.assertEqual(b"".join(data), self.d) + def test_resource_warning(self): + # Issue #11453 + fd = os.open(support.TESTFN, os.O_RDONLY) + f = asyncore.file_wrapper(fd) + with support.check_warnings(('', ResourceWarning)): + f = None + support.gc_collect() + + def test_close_twice(self): + fd = os.open(support.TESTFN, os.O_RDONLY) + f = asyncore.file_wrapper(fd) + f.close() + self.assertEqual(f.fd, -1) + # calling close twice should not fail + f.close() + class BaseTestHandler(asyncore.dispatcher): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,10 @@ Library ------- +- Issue #11453: asyncore: emit a ResourceWarning when an unclosed file_wrapper + object is destroyed. The destructor now closes the file if needed. The + close() method can now be called twice: the second call does nothing. + - Issue #21858: Better handling of Python exceptions in the sqlite3 module. - Issue #21476: Make sure the email.parser.BytesParser TextIOWrapper is -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Fri Jun 27 23:57:30 2014 From: python-checkins at python.org (victor.stinner) Date: Fri, 27 Jun 2014 23:57:30 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2311453=3A_asyncore=3A_emit_a_R?= =?utf-8?q?esourceWarning_when_an_unclosed?= Message-ID: <3h0X562QTvz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/7c9335d97628 changeset: 91451:7c9335d97628 parent: 91449:f1cd0aa1561a parent: 91450:ae12a926e680 user: Victor Stinner date: Fri Jun 27 23:57:19 2014 +0200 summary: (Merge 3.4) Issue #11453: asyncore: emit a ResourceWarning when an unclosed file_wrapper object is destroyed. The destructor now closes the file if needed. The close() method can now be called twice: the second call does nothing. files: Lib/asyncore.py | 8 ++++++++ Lib/test/test_asyncore.py | 16 ++++++++++++++++ Misc/NEWS | 4 ++++ 3 files changed, 28 insertions(+), 0 deletions(-) diff --git a/Lib/asyncore.py b/Lib/asyncore.py --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -600,6 +600,11 @@ def __init__(self, fd): self.fd = os.dup(fd) + def __del__(self): + if self.fd >= 0: + warnings.warn("unclosed file %r" % self, ResourceWarning) + self.close() + def recv(self, *args): return os.read(self.fd, *args) @@ -618,7 +623,10 @@ write = send def close(self): + if self.fd < 0: + return os.close(self.fd) + self.fd = -1 def fileno(self): return self.fd diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -413,6 +413,22 @@ asyncore.loop(timeout=0.01, use_poll=True, count=2) self.assertEqual(b"".join(data), self.d) + def test_resource_warning(self): + # Issue #11453 + fd = os.open(support.TESTFN, os.O_RDONLY) + f = asyncore.file_wrapper(fd) + with support.check_warnings(('', ResourceWarning)): + f = None + support.gc_collect() + + def test_close_twice(self): + fd = os.open(support.TESTFN, os.O_RDONLY) + f = asyncore.file_wrapper(fd) + f.close() + self.assertEqual(f.fd, -1) + # calling close twice should not fail + f.close() + class BaseTestHandler(asyncore.dispatcher): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,10 @@ Library ------- +- Issue #11453: asyncore: emit a ResourceWarning when an unclosed file_wrapper + object is destroyed. The destructor now closes the file if needed. The + close() method can now be called twice: the second call does nothing. + - Issue #21858: Better handling of Python exceptions in the sqlite3 module. - Issue #21476: Make sure the email.parser.BytesParser TextIOWrapper is -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 28 00:12:11 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 28 Jun 2014 00:12:11 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogRml4?= =?utf-8?q?_unit_tests_on_Windows=2C_escape_filenames_in_regex?= Message-ID: <3h0XQ31Sngz7Ll6@mail.python.org> http://hg.python.org/cpython/rev/26287c059304 changeset: 91452:26287c059304 branch: 3.4 parent: 91450:ae12a926e680 user: Victor Stinner date: Sat Jun 28 00:12:02 2014 +0200 summary: asyncio: Fix unit tests on Windows, escape filenames in regex files: Lib/test/test_asyncio/test_futures.py | 8 ++++---- Lib/test/test_asyncio/test_tasks.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -322,9 +322,9 @@ r'source_traceback: Object created at \(most recent call last\):\n' r' File' r'.*\n' - r' File "%s", line %s, in test_future_exception_never_retrieved\n' + r' File "{filename}", line {lineno}, in test_future_exception_never_retrieved\n' r' future = asyncio\.Future\(loop=self\.loop\)$' - % (frame[0], frame[1])) + ).format(filename=re.escape(frame[0]), lineno=frame[1]) exc_info = (type(exc), exc, exc.__traceback__) m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info) else: @@ -333,12 +333,12 @@ r'Future/Task created at \(most recent call last\):\n' r' File' r'.*\n' - r' File "%s", line %s, in test_future_exception_never_retrieved\n' + r' File "{filename}", line {lineno}, in test_future_exception_never_retrieved\n' r' future = asyncio\.Future\(loop=self\.loop\)\n' r'Traceback \(most recent call last\):\n' r'.*\n' r'MemoryError$' - % (frame[0], frame[1])) + ).format(filename=re.escape(frame[0]), lineno=frame[1]) m_log.error.assert_called_once_with(mock.ANY, exc_info=False) message = m_log.error.call_args[0][0] self.assertRegex(message, re.compile(regex, re.DOTALL)) 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 @@ -1602,8 +1602,8 @@ r' File "%s", line %s, in test_coroutine_never_yielded\n' r' coro = coro_noop\(\)$' % (re.escape(coro_noop.__qualname__), - func_filename, func_lineno, - tb_filename, tb_lineno)) + re.escape(func_filename), func_lineno, + re.escape(tb_filename), tb_lineno)) self.assertRegex(message, re.compile(regex, re.DOTALL)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 28 00:14:37 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 28 Jun 2014 00:14:37 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Fix_unit_tests_on_Windows?= =?utf-8?q?=2C_escape_filenames_in_regex?= Message-ID: <3h0XSs57t9z7Ljk@mail.python.org> http://hg.python.org/cpython/rev/d7f108ebc2dd changeset: 91453:d7f108ebc2dd parent: 91451:7c9335d97628 parent: 91452:26287c059304 user: Victor Stinner date: Sat Jun 28 00:14:28 2014 +0200 summary: (Merge 3.4) asyncio: Fix unit tests on Windows, escape filenames in regex files: Lib/test/test_asyncio/test_futures.py | 8 ++++---- Lib/test/test_asyncio/test_tasks.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -322,9 +322,9 @@ r'source_traceback: Object created at \(most recent call last\):\n' r' File' r'.*\n' - r' File "%s", line %s, in test_future_exception_never_retrieved\n' + r' File "{filename}", line {lineno}, in test_future_exception_never_retrieved\n' r' future = asyncio\.Future\(loop=self\.loop\)$' - % (frame[0], frame[1])) + ).format(filename=re.escape(frame[0]), lineno=frame[1]) exc_info = (type(exc), exc, exc.__traceback__) m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info) else: @@ -333,12 +333,12 @@ r'Future/Task created at \(most recent call last\):\n' r' File' r'.*\n' - r' File "%s", line %s, in test_future_exception_never_retrieved\n' + r' File "{filename}", line {lineno}, in test_future_exception_never_retrieved\n' r' future = asyncio\.Future\(loop=self\.loop\)\n' r'Traceback \(most recent call last\):\n' r'.*\n' r'MemoryError$' - % (frame[0], frame[1])) + ).format(filename=re.escape(frame[0]), lineno=frame[1]) m_log.error.assert_called_once_with(mock.ANY, exc_info=False) message = m_log.error.call_args[0][0] self.assertRegex(message, re.compile(regex, re.DOTALL)) 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 @@ -1602,8 +1602,8 @@ r' File "%s", line %s, in test_coroutine_never_yielded\n' r' coro = coro_noop\(\)$' % (re.escape(coro_noop.__qualname__), - func_filename, func_lineno, - tb_filename, tb_lineno)) + re.escape(func_filename), func_lineno, + re.escape(tb_filename), tb_lineno)) self.assertRegex(message, re.compile(regex, re.DOTALL)) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 28 01:20:34 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 28 Jun 2014 01:20:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogRml4?= =?utf-8?q?_two_=22Coroutine_xxx_was_never_yielded_from=22_messages_in_tes?= =?utf-8?q?ts?= Message-ID: <3h0Ywy03V6z7Ljk@mail.python.org> http://hg.python.org/cpython/rev/39019673aa8d changeset: 91454:39019673aa8d branch: 3.4 parent: 91452:26287c059304 user: Victor Stinner date: Sat Jun 28 01:19:11 2014 +0200 summary: asyncio: Fix two "Coroutine xxx was never yielded from" messages in tests files: Lib/test/test_asyncio/test_tasks.py | 8 ++++++-- 1 files changed, 6 insertions(+), 2 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 @@ -1423,8 +1423,10 @@ # as_completed() expects a list of futures, not a future instance self.assertRaises(TypeError, self.loop.run_until_complete, asyncio.as_completed(fut, loop=self.loop)) + coro = coroutine_function() self.assertRaises(TypeError, self.loop.run_until_complete, - asyncio.as_completed(coroutine_function(), loop=self.loop)) + asyncio.as_completed(coro, loop=self.loop)) + coro.close() def test_wait_invalid_args(self): fut = asyncio.Future(loop=self.loop) @@ -1432,8 +1434,10 @@ # wait() expects a list of futures, not a future instance self.assertRaises(TypeError, self.loop.run_until_complete, asyncio.wait(fut, loop=self.loop)) + coro = coroutine_function() self.assertRaises(TypeError, self.loop.run_until_complete, - asyncio.wait(coroutine_function(), loop=self.loop)) + asyncio.wait(coro, loop=self.loop)) + coro.close() # wait() expects at least a future self.assertRaises(ValueError, self.loop.run_until_complete, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 28 01:20:35 2014 From: python-checkins at python.org (victor.stinner) Date: Sat, 28 Jun 2014 01:20:35 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_Fix_two_=22Coroutine_xxx_wa?= =?utf-8?q?s_never_yielded_from=22_messages_in?= Message-ID: <3h0Ywz1Mlbz7Ljm@mail.python.org> http://hg.python.org/cpython/rev/ac270acff94f changeset: 91455:ac270acff94f parent: 91453:d7f108ebc2dd parent: 91454:39019673aa8d user: Victor Stinner date: Sat Jun 28 01:19:28 2014 +0200 summary: (Merge 3.4) asyncio: Fix two "Coroutine xxx was never yielded from" messages in tests files: Lib/test/test_asyncio/test_tasks.py | 8 ++++++-- 1 files changed, 6 insertions(+), 2 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 @@ -1423,8 +1423,10 @@ # as_completed() expects a list of futures, not a future instance self.assertRaises(TypeError, self.loop.run_until_complete, asyncio.as_completed(fut, loop=self.loop)) + coro = coroutine_function() self.assertRaises(TypeError, self.loop.run_until_complete, - asyncio.as_completed(coroutine_function(), loop=self.loop)) + asyncio.as_completed(coro, loop=self.loop)) + coro.close() def test_wait_invalid_args(self): fut = asyncio.Future(loop=self.loop) @@ -1432,8 +1434,10 @@ # wait() expects a list of futures, not a future instance self.assertRaises(TypeError, self.loop.run_until_complete, asyncio.wait(fut, loop=self.loop)) + coro = coroutine_function() self.assertRaises(TypeError, self.loop.run_until_complete, - asyncio.wait(coroutine_function(), loop=self.loop)) + asyncio.wait(coro, loop=self.loop)) + coro.close() # wait() expects at least a future self.assertRaises(ValueError, self.loop.run_until_complete, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 28 02:12:42 2014 From: python-checkins at python.org (berker.peksag) Date: Sat, 28 Jun 2014 02:12:42 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Fix_typo_point?= =?utf-8?q?ed_out_by_Dmitry_Chaplinsky_on_docs=40=2E?= Message-ID: <3h0b565Wxjz7LjR@mail.python.org> http://hg.python.org/cpython/rev/8552f3031753 changeset: 91456:8552f3031753 branch: 2.7 parent: 91437:94f7cdab9f71 user: Berker Peksag date: Sat Jun 28 03:12:37 2014 +0300 summary: Fix typo pointed out by Dmitry Chaplinsky on docs at . files: Doc/library/urllib2.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/urllib2.rst b/Doc/library/urllib2.rst --- a/Doc/library/urllib2.rst +++ b/Doc/library/urllib2.rst @@ -43,7 +43,7 @@ timeout setting will be used). This actually only works for HTTP, HTTPS and FTP connections. - This function returns a file-like object with two additional methods: + This function returns a file-like object with three additional methods: * :meth:`geturl` --- return the URL of the resource retrieved, commonly used to determine if a redirect was followed -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 28 05:55:57 2014 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 28 Jun 2014 05:55:57 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321863=3A_cProfile?= =?utf-8?q?_now_displays_the_module_name_of_C_extension_functions=2C?= Message-ID: <3h0h2j6J4cz7LjY@mail.python.org> http://hg.python.org/cpython/rev/6dd4c2d30b0e changeset: 91457:6dd4c2d30b0e parent: 91455:ac270acff94f user: Antoine Pitrou date: Fri Jun 27 23:49:29 2014 -0400 summary: Issue #21863: cProfile now displays the module name of C extension functions, in addition to their own name. files: Lib/test/test_cprofile.py | 10 +++++----- Misc/NEWS | 3 +++ Modules/_lsprof.c | 11 +++++++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py --- a/Lib/test/test_cprofile.py +++ b/Lib/test/test_cprofile.py @@ -11,7 +11,7 @@ class CProfileTest(ProfileTest): profilerclass = cProfile.Profile profilermodule = cProfile - expected_max_output = "{built-in method max}" + expected_max_output = "{built-in method builtins.max}" def get_expected_output(self): return _ProfileOutput @@ -72,9 +72,9 @@ profilee.py:88(helper2) <- 6 0.234 0.300 profilee.py:55(helper) 2 0.078 0.100 profilee.py:84(helper2_indirect) profilee.py:98(subhelper) <- 8 0.064 0.080 profilee.py:88(helper2) -{built-in method exc_info} <- 4 0.000 0.000 profilee.py:73(helper1) -{built-in method hasattr} <- 4 0.000 0.004 profilee.py:73(helper1) +{built-in method builtins.hasattr} <- 4 0.000 0.004 profilee.py:73(helper1) 8 0.000 0.008 profilee.py:88(helper2) +{built-in method sys.exc_info} <- 4 0.000 0.000 profilee.py:73(helper1) {method 'append' of 'list' objects} <- 4 0.000 0.000 profilee.py:73(helper1)""" _ProfileOutput['print_callees'] = """\ :1() -> 1 0.270 1.000 profilee.py:25(testfunc) @@ -87,12 +87,12 @@ profilee.py:55(helper) -> 4 0.116 0.120 profilee.py:73(helper1) 2 0.000 0.140 profilee.py:84(helper2_indirect) 6 0.234 0.300 profilee.py:88(helper2) -profilee.py:73(helper1) -> 4 0.000 0.000 {built-in method exc_info} +profilee.py:73(helper1) -> 4 0.000 0.004 {built-in method builtins.hasattr} profilee.py:84(helper2_indirect) -> 2 0.006 0.040 profilee.py:35(factorial) 2 0.078 0.100 profilee.py:88(helper2) profilee.py:88(helper2) -> 8 0.064 0.080 profilee.py:98(subhelper) profilee.py:98(subhelper) -> 16 0.016 0.016 profilee.py:110(__getattr__) -{built-in method hasattr} -> 12 0.012 0.012 profilee.py:110(__getattr__)""" +{built-in method builtins.hasattr} -> 12 0.012 0.012 profilee.py:110(__getattr__)""" if __name__ == "__main__": main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,9 @@ Library ------- +- Issue #21863: cProfile now displays the module name of C extension functions, + in addition to their own name. + - Issue #11453: asyncore: emit a ResourceWarning when an unclosed file_wrapper object is destroyed. The destructor now closes the file if needed. The close() method can now be called twice: the second call does nothing. diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -202,6 +202,8 @@ */ PyObject *self = fn->m_self; PyObject *name = PyUnicode_FromString(fn->m_ml->ml_name); + PyObject *modname = fn->m_module; + if (name != NULL) { PyObject *mo = _PyType_Lookup(Py_TYPE(self), name); Py_XINCREF(mo); @@ -213,9 +215,14 @@ return res; } } + /* Otherwise, use __module__ */ PyErr_Clear(); - return PyUnicode_FromFormat("", - fn->m_ml->ml_name); + if (modname != NULL && PyUnicode_Check(modname)) + return PyUnicode_FromFormat("", + modname, fn->m_ml->ml_name); + else + return PyUnicode_FromFormat("", + fn->m_ml->ml_name); } } -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sat Jun 28 10:19:59 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 28 Jun 2014 10:19:59 +0200 Subject: [Python-checkins] Daily reference leaks (ac270acff94f): sum=7 Message-ID: results for ac270acff94f on branch "default" -------------------------------------------- test_collections leaked [0, 2, 0] references, sum=2 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [0, -2, 0] references, sum=-2 test_site leaked [0, -2, 0] memory blocks, sum=-2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogObHZ2q', '-x'] From python-checkins at python.org Sat Jun 28 18:40:12 2014 From: python-checkins at python.org (jesus.cea) Date: Sat, 28 Jun 2014 18:40:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogQ2xvc2VzICMxMTI3?= =?utf-8?q?9=3A_test=5Fposix_and_lack_of_=22id_-G=22_support_-_less_noise_?= =?utf-8?q?required=3F?= Message-ID: <3h110X3snQz7LjV@mail.python.org> http://hg.python.org/cpython/rev/4ef517041573 changeset: 91458:4ef517041573 branch: 2.7 parent: 91456:8552f3031753 user: Jesus Cea date: Sat Jun 28 18:39:01 2014 +0200 summary: Closes #11279: test_posix and lack of "id -G" support - less noise required? (Solaris) files: Lib/test/test_posix.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 @@ -509,7 +509,7 @@ @unittest.skipUnless(hasattr(os, 'getegid'), "test needs os.getegid()") def test_getgroups(self): - with os.popen('id -G') as idg: + with os.popen('id -G 2>/dev/null') as idg: groups = idg.read().strip() ret = idg.close() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 28 18:40:13 2014 From: python-checkins at python.org (jesus.cea) Date: Sat, 28 Jun 2014 18:40:13 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogQ2xvc2VzICMxMTI3?= =?utf-8?q?9=3A_test=5Fposix_and_lack_of_=22id_-G=22_support_-_less_noise_?= =?utf-8?q?required=3F?= Message-ID: <3h110Y5Tw7z7Lk3@mail.python.org> http://hg.python.org/cpython/rev/6889fb276d87 changeset: 91459:6889fb276d87 branch: 3.4 parent: 91454:39019673aa8d user: Jesus Cea date: Sat Jun 28 18:39:35 2014 +0200 summary: Closes #11279: test_posix and lack of "id -G" support - less noise required? (Solaris) files: Lib/test/test_posix.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 @@ -757,7 +757,7 @@ @unittest.skipUnless(hasattr(os, 'getegid'), "test needs os.getegid()") def test_getgroups(self): - with os.popen('id -G') as idg: + with os.popen('id -G 2>/dev/null') as idg: groups = idg.read().strip() ret = idg.close() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sat Jun 28 18:40:15 2014 From: python-checkins at python.org (jesus.cea) Date: Sat, 28 Jun 2014 18:40:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_MERGE=3A_Closes_=2311279=3A_test=5Fposix_and_lack_of_=22?= =?utf-8?q?id_-G=22_support_-_less_noise?= Message-ID: <3h110b049Nz7Lk6@mail.python.org> http://hg.python.org/cpython/rev/54f94e753269 changeset: 91460:54f94e753269 parent: 91457:6dd4c2d30b0e parent: 91459:6889fb276d87 user: Jesus Cea date: Sat Jun 28 18:39:51 2014 +0200 summary: MERGE: Closes #11279: test_posix and lack of "id -G" support - less noise required? (Solaris) files: Lib/test/test_posix.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 @@ -757,7 +757,7 @@ @unittest.skipUnless(hasattr(os, 'getegid'), "test needs os.getegid()") def test_getgroups(self): - with os.popen('id -G') as idg: + with os.popen('id -G 2>/dev/null') as idg: groups = idg.read().strip() ret = idg.close() -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 29 00:53:10 2014 From: python-checkins at python.org (victor.stinner) Date: Sun, 29 Jun 2014 00:53:10 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogc3lu?= =?utf-8?q?c_with_Tulip=2C_add_a_new_asyncio=2Ecoroutines_module?= Message-ID: <3h19Gt73G3z7LjM@mail.python.org> http://hg.python.org/cpython/rev/8734e881c400 changeset: 91461:8734e881c400 branch: 3.4 parent: 91459:6889fb276d87 user: Victor Stinner date: Sun Jun 29 00:46:45 2014 +0200 summary: asyncio: sync with Tulip, add a new asyncio.coroutines module files: Lib/asyncio/__init__.py | 4 +- Lib/asyncio/base_events.py | 28 +- Lib/asyncio/base_subprocess.py | 4 +- Lib/asyncio/tasks.py | 633 +--------------- Lib/asyncio/locks.py | 12 +- Lib/asyncio/streams.py | 18 +- Lib/asyncio/subprocess.py | 15 +- Lib/asyncio/tasks.py | 143 +--- Lib/asyncio/test_utils.py | 3 +- Lib/asyncio/unix_events.py | 8 +- Lib/asyncio/windows_events.py | 11 +- Lib/test/test_asyncio/test_tasks.py | 34 +- 12 files changed, 83 insertions(+), 830 deletions(-) diff --git a/Lib/asyncio/__init__.py b/Lib/asyncio/__init__.py --- a/Lib/asyncio/__init__.py +++ b/Lib/asyncio/__init__.py @@ -18,6 +18,7 @@ import _overlapped # Will also be exported. # This relies on each of the submodules having an __all__ variable. +from .coroutines import * from .events import * from .futures import * from .locks import * @@ -34,7 +35,8 @@ from .unix_events import * # pragma: no cover -__all__ = (events.__all__ + +__all__ = (coroutines.__all__ + + events.__all__ + futures.__all__ + locks.__all__ + protocols.__all__ + 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 @@ -26,9 +26,11 @@ import os import sys +from . import coroutines from . import events from . import futures from . import tasks +from .coroutines import coroutine from .log import logger @@ -118,7 +120,7 @@ if not waiter.done(): waiter.set_result(waiter) - @tasks.coroutine + @coroutine def wait_closed(self): if self.sockets is None or self.waiters is None: return @@ -175,7 +177,7 @@ """Create write pipe transport.""" raise NotImplementedError - @tasks.coroutine + @coroutine def _make_subprocess_transport(self, protocol, args, shell, stdin, stdout, stderr, bufsize, extra=None, **kwargs): @@ -298,7 +300,7 @@ def call_at(self, when, callback, *args): """Like call_later(), but uses an absolute time.""" - if tasks.iscoroutinefunction(callback): + if coroutines.iscoroutinefunction(callback): raise TypeError("coroutines cannot be used with call_at()") if self._debug: self._assert_is_current_event_loop() @@ -324,7 +326,7 @@ return handle def _call_soon(self, callback, args, check_loop): - if tasks.iscoroutinefunction(callback): + if coroutines.iscoroutinefunction(callback): raise TypeError("coroutines cannot be used with call_soon()") if self._debug and check_loop: self._assert_is_current_event_loop() @@ -361,7 +363,7 @@ return handle def run_in_executor(self, executor, callback, *args): - if tasks.iscoroutinefunction(callback): + if coroutines.iscoroutinefunction(callback): raise TypeError("coroutines cannot be used with run_in_executor()") if isinstance(callback, events.Handle): assert not args @@ -389,7 +391,7 @@ def getnameinfo(self, sockaddr, flags=0): return self.run_in_executor(None, socket.getnameinfo, sockaddr, flags) - @tasks.coroutine + @coroutine def create_connection(self, protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None): @@ -505,7 +507,7 @@ sock, protocol_factory, ssl, server_hostname) return transport, protocol - @tasks.coroutine + @coroutine def _create_connection_transport(self, sock, protocol_factory, ssl, server_hostname): protocol = protocol_factory() @@ -521,7 +523,7 @@ yield from waiter return transport, protocol - @tasks.coroutine + @coroutine def create_datagram_endpoint(self, protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0): @@ -593,7 +595,7 @@ transport = self._make_datagram_transport(sock, protocol, r_addr) return transport, protocol - @tasks.coroutine + @coroutine def create_server(self, protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, @@ -672,7 +674,7 @@ self._start_serving(protocol_factory, sock, ssl, server) return server - @tasks.coroutine + @coroutine def connect_read_pipe(self, protocol_factory, pipe): protocol = protocol_factory() waiter = futures.Future(loop=self) @@ -680,7 +682,7 @@ yield from waiter return transport, protocol - @tasks.coroutine + @coroutine def connect_write_pipe(self, protocol_factory, pipe): protocol = protocol_factory() waiter = futures.Future(loop=self) @@ -688,7 +690,7 @@ yield from waiter return transport, protocol - @tasks.coroutine + @coroutine def subprocess_shell(self, protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False, shell=True, bufsize=0, @@ -706,7 +708,7 @@ protocol, cmd, True, stdin, stdout, stderr, bufsize, **kwargs) return transport, protocol - @tasks.coroutine + @coroutine def subprocess_exec(self, protocol_factory, program, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False, 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 @@ -2,8 +2,8 @@ import subprocess from . import protocols -from . import tasks from . import transports +from .coroutines import coroutine class BaseSubprocessTransport(transports.SubprocessTransport): @@ -65,7 +65,7 @@ def kill(self): self._proc.kill() - @tasks.coroutine + @coroutine def _post_init(self): proc = self._proc loop = self._loop diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/coroutines.py copy from Lib/asyncio/tasks.py copy to Lib/asyncio/coroutines.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/coroutines.py @@ -1,20 +1,11 @@ -"""Support for tasks, coroutines and the scheduler.""" +__all__ = ['coroutine', + 'iscoroutinefunction', 'iscoroutine'] -__all__ = ['coroutine', 'Task', - 'iscoroutinefunction', 'iscoroutine', - 'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED', - 'wait', 'wait_for', 'as_completed', 'sleep', 'async', - 'gather', 'shield', - ] - -import concurrent.futures import functools import inspect -import linecache import os import sys import traceback -import weakref from . import events from . import futures @@ -32,10 +23,8 @@ _DEBUG = (not sys.flags.ignore_environment and bool(os.environ.get('PYTHONASYNCIODEBUG'))) -_PY34 = (sys.version_info >= (3, 4)) _PY35 = (sys.version_info >= (3, 5)) - class CoroWrapper: # Wrapper for coroutine in _DEBUG mode. @@ -149,621 +138,3 @@ else: lineno = coro.gi_code.co_firstlineno return '%s() done at %s:%s' % (coro_name, filename, lineno) - - -class Task(futures.Future): - """A coroutine wrapped in a Future.""" - - # An important invariant maintained while a Task not done: - # - # - Either _fut_waiter is None, and _step() is scheduled; - # - or _fut_waiter is some Future, and _step() is *not* scheduled. - # - # The only transition from the latter to the former is through - # _wakeup(). When _fut_waiter is not None, one of its callbacks - # must be _wakeup(). - - # Weak set containing all tasks alive. - _all_tasks = weakref.WeakSet() - - # Dictionary containing tasks that are currently active in - # all running event loops. {EventLoop: Task} - _current_tasks = {} - - @classmethod - def current_task(cls, loop=None): - """Return the currently running task in an event loop or None. - - By default the current task for the current event loop is returned. - - None is returned when called not in the context of a Task. - """ - if loop is None: - loop = events.get_event_loop() - return cls._current_tasks.get(loop) - - @classmethod - def all_tasks(cls, loop=None): - """Return a set of all tasks for an event loop. - - By default all tasks for the current event loop are returned. - """ - if loop is None: - loop = events.get_event_loop() - return {t for t in cls._all_tasks if t._loop is loop} - - def __init__(self, coro, *, loop=None): - assert iscoroutine(coro), repr(coro) # Not a coroutine function! - super().__init__(loop=loop) - if self._source_traceback: - del self._source_traceback[-1] - self._coro = iter(coro) # Use the iterator just in case. - self._fut_waiter = None - self._must_cancel = False - self._loop.call_soon(self._step) - self.__class__._all_tasks.add(self) - - # On Python 3.3 or older, objects with a destructor part of a reference - # cycle are never destroyed. It's not more the case on Python 3.4 thanks to - # the PEP 442. - if _PY34: - def __del__(self): - if self._state == futures._PENDING: - context = { - 'task': self, - 'message': 'Task was destroyed but it is pending!', - } - if self._source_traceback: - context['source_traceback'] = self._source_traceback - self._loop.call_exception_handler(context) - futures.Future.__del__(self) - - def __repr__(self): - info = [] - if self._must_cancel: - info.append('cancelling') - else: - info.append(self._state.lower()) - - info.append(_format_coroutine(self._coro)) - - if self._state == futures._FINISHED: - info.append(self._format_result()) - - if self._callbacks: - info.append(self._format_callbacks()) - - return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) - - def get_stack(self, *, limit=None): - """Return the list of stack frames for this task's coroutine. - - If the coroutine is active, this returns the stack where it is - suspended. If the coroutine has completed successfully or was - cancelled, this returns an empty list. If the coroutine was - terminated by an exception, this returns the list of traceback - frames. - - The frames are always ordered from oldest to newest. - - 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. (This matches the - behavior of the traceback module.) - - For reasons beyond our control, only one stack frame is - returned for a suspended coroutine. - """ - frames = [] - f = self._coro.gi_frame - if f is not None: - while f is not None: - if limit is not None: - if limit <= 0: - break - limit -= 1 - frames.append(f) - f = f.f_back - frames.reverse() - elif self._exception is not None: - tb = self._exception.__traceback__ - while tb is not None: - if limit is not None: - if limit <= 0: - break - limit -= 1 - frames.append(tb.tb_frame) - tb = tb.tb_next - return frames - - def print_stack(self, *, limit=None, file=None): - """Print the stack or traceback for this task's coroutine. - - This produces output similar to that of the traceback module, - for the frames retrieved by get_stack(). The limit argument - is passed to get_stack(). The file argument is an I/O stream - to which the output goes; by default it goes to sys.stderr. - """ - extracted_list = [] - checked = set() - for f in self.get_stack(limit=limit): - lineno = f.f_lineno - co = f.f_code - filename = co.co_filename - name = co.co_name - if filename not in checked: - checked.add(filename) - linecache.checkcache(filename) - line = linecache.getline(filename, lineno, f.f_globals) - extracted_list.append((filename, lineno, name, line)) - exc = self._exception - if not extracted_list: - print('No stack for %r' % self, file=file) - elif exc is not None: - print('Traceback for %r (most recent call last):' % self, - file=file) - else: - print('Stack for %r (most recent call last):' % self, - file=file) - traceback.print_list(extracted_list, file=file) - if exc is not None: - for line in traceback.format_exception_only(exc.__class__, exc): - print(line, file=file, end='') - - def cancel(self): - """Request this task to cancel itself. - - This arranges for a CancelledError to be thrown into the - wrapped coroutine on the next cycle through the event loop. - The coroutine then has a chance to clean up or even deny - the request using try/except/finally. - - Contrary to Future.cancel(), this does not guarantee that the - task will be cancelled: the exception might be caught and - acted upon, delaying cancellation of the task or preventing it - completely. The task may also return a value or raise a - different exception. - - Immediately after this method is called, Task.cancelled() will - not return True (unless the task was already cancelled). A - task will be marked as cancelled when the wrapped coroutine - terminates with a CancelledError exception (even if cancel() - was not called). - """ - if self.done(): - return False - if self._fut_waiter is not None: - if self._fut_waiter.cancel(): - # Leave self._fut_waiter; it may be a Task that - # catches and ignores the cancellation so we may have - # to cancel it again later. - return True - # It must be the case that self._step is already scheduled. - self._must_cancel = True - return True - - def _step(self, value=None, exc=None): - assert not self.done(), \ - '_step(): already done: {!r}, {!r}, {!r}'.format(self, value, exc) - if self._must_cancel: - if not isinstance(exc, futures.CancelledError): - exc = futures.CancelledError() - self._must_cancel = False - coro = self._coro - self._fut_waiter = None - - self.__class__._current_tasks[self._loop] = self - # Call either coro.throw(exc) or coro.send(value). - try: - if exc is not None: - result = coro.throw(exc) - elif value is not None: - result = coro.send(value) - else: - result = next(coro) - except StopIteration as exc: - self.set_result(exc.value) - except futures.CancelledError as exc: - super().cancel() # I.e., Future.cancel(self). - except Exception as exc: - self.set_exception(exc) - except BaseException as exc: - self.set_exception(exc) - raise - else: - if isinstance(result, futures.Future): - # Yielded Future must come from Future.__iter__(). - if result._blocking: - result._blocking = False - result.add_done_callback(self._wakeup) - self._fut_waiter = result - if self._must_cancel: - if self._fut_waiter.cancel(): - self._must_cancel = False - else: - self._loop.call_soon( - self._step, None, - RuntimeError( - 'yield was used instead of yield from ' - 'in task {!r} with {!r}'.format(self, result))) - elif result is None: - # Bare yield relinquishes control for one event loop iteration. - self._loop.call_soon(self._step) - elif inspect.isgenerator(result): - # Yielding a generator is just wrong. - self._loop.call_soon( - self._step, None, - RuntimeError( - 'yield was used instead of yield from for ' - 'generator in task {!r} with {}'.format( - self, result))) - else: - # Yielding something else is an error. - self._loop.call_soon( - self._step, None, - RuntimeError( - 'Task got bad yield: {!r}'.format(result))) - finally: - self.__class__._current_tasks.pop(self._loop) - self = None # Needed to break cycles when an exception occurs. - - def _wakeup(self, future): - try: - value = future.result() - except Exception as exc: - # This may also be a cancellation. - self._step(None, exc) - else: - self._step(value, None) - self = None # Needed to break cycles when an exception occurs. - - -# wait() and as_completed() similar to those in PEP 3148. - -FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED -FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION -ALL_COMPLETED = concurrent.futures.ALL_COMPLETED - - - at coroutine -def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED): - """Wait for the Futures and coroutines given by fs to complete. - - The sequence futures must not be empty. - - Coroutines will be wrapped in Tasks. - - Returns two sets of Future: (done, pending). - - Usage: - - done, pending = yield from asyncio.wait(fs) - - Note: This does not raise TimeoutError! Futures that aren't done - when the timeout occurs are returned in the second set. - """ - if isinstance(fs, futures.Future) or iscoroutine(fs): - raise TypeError("expect a list of futures, not %s" % type(fs).__name__) - if not fs: - raise ValueError('Set of coroutines/Futures is empty.') - - if loop is None: - loop = events.get_event_loop() - - 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)) - return (yield from _wait(fs, timeout, return_when, loop)) - - -def _release_waiter(waiter, value=True, *args): - if not waiter.done(): - waiter.set_result(value) - - - at coroutine -def wait_for(fut, timeout, *, loop=None): - """Wait for the single Future or coroutine to complete, with timeout. - - Coroutine will be wrapped in Task. - - Returns result of the Future or coroutine. When a timeout occurs, - it cancels the task and raises TimeoutError. To avoid the task - cancellation, wrap it in shield(). - - Usage: - - result = yield from asyncio.wait_for(fut, 10.0) - - """ - if loop is None: - loop = events.get_event_loop() - - if timeout is None: - return (yield from fut) - - waiter = futures.Future(loop=loop) - timeout_handle = loop.call_later(timeout, _release_waiter, waiter, False) - cb = functools.partial(_release_waiter, waiter, True) - - fut = async(fut, loop=loop) - fut.add_done_callback(cb) - - try: - if (yield from waiter): - return fut.result() - else: - fut.remove_done_callback(cb) - fut.cancel() - raise futures.TimeoutError() - finally: - timeout_handle.cancel() - - - at coroutine -def _wait(fs, timeout, return_when, loop): - """Internal helper for wait() and _wait_for(). - - The fs argument must be a collection of Futures. - """ - assert fs, 'Set of Futures is empty.' - waiter = futures.Future(loop=loop) - timeout_handle = None - if timeout is not None: - timeout_handle = loop.call_later(timeout, _release_waiter, waiter) - counter = len(fs) - - def _on_completion(f): - nonlocal counter - counter -= 1 - if (counter <= 0 or - return_when == FIRST_COMPLETED or - return_when == FIRST_EXCEPTION and (not f.cancelled() and - f.exception() is not None)): - if timeout_handle is not None: - timeout_handle.cancel() - if not waiter.done(): - waiter.set_result(False) - - for f in fs: - f.add_done_callback(_on_completion) - - try: - yield from waiter - finally: - if timeout_handle is not None: - timeout_handle.cancel() - - done, pending = set(), set() - for f in fs: - f.remove_done_callback(_on_completion) - if f.done(): - done.add(f) - else: - pending.add(f) - return done, pending - - -# This is *not* a @coroutine! It is just an iterator (yielding Futures). -def as_completed(fs, *, loop=None, timeout=None): - """Return an iterator whose values are coroutines. - - When waiting for the yielded coroutines you'll get the results (or - exceptions!) of the original Futures (or coroutines), in the order - in which and as soon as they complete. - - This differs from PEP 3148; the proper way to use this is: - - for f in as_completed(fs): - result = yield from f # The 'yield from' may raise. - # Use result. - - If a timeout is specified, the 'yield from' will raise - TimeoutError when the timeout occurs before all Futures are done. - - Note: The futures 'f' are not necessarily members of fs. - """ - if isinstance(fs, futures.Future) or iscoroutine(fs): - raise TypeError("expect a list of futures, not %s" % type(fs).__name__) - loop = loop if loop is not None else events.get_event_loop() - todo = {async(f, loop=loop) for f in set(fs)} - from .queues import Queue # Import here to avoid circular import problem. - done = Queue(loop=loop) - timeout_handle = None - - def _on_timeout(): - for f in todo: - f.remove_done_callback(_on_completion) - done.put_nowait(None) # Queue a dummy value for _wait_for_one(). - todo.clear() # Can't do todo.remove(f) in the loop. - - def _on_completion(f): - if not todo: - return # _on_timeout() was here first. - todo.remove(f) - done.put_nowait(f) - if not todo and timeout_handle is not None: - timeout_handle.cancel() - - @coroutine - def _wait_for_one(): - f = yield from done.get() - if f is None: - # Dummy value from _on_timeout(). - raise futures.TimeoutError - return f.result() # May raise f.exception(). - - for f in todo: - f.add_done_callback(_on_completion) - if todo and timeout is not None: - timeout_handle = loop.call_later(timeout, _on_timeout) - for _ in range(len(todo)): - yield _wait_for_one() - - - at coroutine -def sleep(delay, result=None, *, loop=None): - """Coroutine that completes after a given time (in seconds).""" - future = futures.Future(loop=loop) - h = future._loop.call_later(delay, future.set_result, result) - try: - return (yield from future) - finally: - h.cancel() - - -def async(coro_or_future, *, loop=None): - """Wrap a coroutine in a future. - - If the argument is a Future, it is returned directly. - """ - if isinstance(coro_or_future, futures.Future): - if loop is not None and loop is not coro_or_future._loop: - raise ValueError('loop argument must agree with Future') - return coro_or_future - elif iscoroutine(coro_or_future): - task = Task(coro_or_future, loop=loop) - if task._source_traceback: - del task._source_traceback[-1] - return task - else: - raise TypeError('A Future or coroutine is required') - - -class _GatheringFuture(futures.Future): - """Helper for gather(). - - This overrides cancel() to cancel all the children and act more - like Task.cancel(), which doesn't immediately mark itself as - cancelled. - """ - - def __init__(self, children, *, loop=None): - super().__init__(loop=loop) - self._children = children - - def cancel(self): - if self.done(): - return False - for child in self._children: - child.cancel() - return True - - -def gather(*coros_or_futures, loop=None, return_exceptions=False): - """Return a future aggregating results from the given coroutines - or futures. - - 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 *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. - - Cancellation: if the outer Future is cancelled, all children (that - have not completed yet) are also cancelled. If any child is - cancelled, this is treated as if it raised CancelledError -- - the outer Future is *not* cancelled in this case. (This is to - prevent the cancellation of one child to cause other children to - be cancelled.) - """ - 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) - outer.set_result([]) - return outer - if loop is None: - loop = children[0]._loop - for fut in children: - if fut._loop is not loop: - raise ValueError("futures are tied to different event loops") - outer = _GatheringFuture(children, loop=loop) - nfinished = 0 - results = [None] * n - - def _done_callback(i, fut): - nonlocal nfinished - if outer._state != futures._PENDING: - if fut._exception is not None: - # Mark exception retrieved. - fut.exception() - return - if fut._state == futures._CANCELLED: - res = futures.CancelledError() - if not return_exceptions: - outer.set_exception(res) - return - elif fut._exception is not None: - res = fut.exception() # Mark exception retrieved. - if not return_exceptions: - outer.set_exception(res) - return - else: - res = fut._result - results[i] = res - nfinished += 1 - if nfinished == n: - outer.set_result(results) - - for i, fut in enumerate(children): - fut.add_done_callback(functools.partial(_done_callback, i)) - return outer - - -def shield(arg, *, loop=None): - """Wait for a future, shielding it from cancellation. - - The statement - - res = yield from shield(something()) - - is exactly equivalent to the statement - - res = yield from something() - - *except* that if the coroutine containing it is cancelled, the - task running in something() is not cancelled. From the POV of - something(), the cancellation did not happen. But its caller is - still cancelled, so the yield-from expression still raises - CancelledError. Note: If something() is cancelled by other means - this will still cancel shield(). - - If you want to completely ignore cancellation (not recommended) - you can combine shield() with a try/except clause, as follows: - - try: - res = yield from shield(something()) - except CancelledError: - res = None - """ - inner = async(arg, loop=loop) - if inner.done(): - # Shortcut. - return inner - loop = inner._loop - outer = futures.Future(loop=loop) - - def _done_callback(inner): - if outer.cancelled(): - # Mark inner's result as retrieved. - inner.cancelled() or inner.exception() - return - if inner.cancelled(): - outer.cancel() - else: - exc = inner.exception() - if exc is not None: - outer.set_exception(exc) - else: - outer.set_result(inner.result()) - - inner.add_done_callback(_done_callback) - return outer diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py --- a/Lib/asyncio/locks.py +++ b/Lib/asyncio/locks.py @@ -6,7 +6,7 @@ from . import events from . import futures -from . import tasks +from .coroutines import coroutine class _ContextManager: @@ -112,7 +112,7 @@ """Return True if lock is acquired.""" return self._locked - @tasks.coroutine + @coroutine def acquire(self): """Acquire a lock. @@ -225,7 +225,7 @@ to true again.""" self._value = False - @tasks.coroutine + @coroutine def wait(self): """Block until the internal flag is true. @@ -278,7 +278,7 @@ extra = '{},waiters:{}'.format(extra, len(self._waiters)) return '<{} [{}]>'.format(res[1:-1], extra) - @tasks.coroutine + @coroutine def wait(self): """Wait until notified. @@ -306,7 +306,7 @@ finally: yield from self.acquire() - @tasks.coroutine + @coroutine def wait_for(self, predicate): """Wait until a predicate becomes true. @@ -402,7 +402,7 @@ """Returns True if semaphore can not be acquired immediately.""" return self._value == 0 - @tasks.coroutine + @coroutine def acquire(self): """Acquire a semaphore. diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -10,10 +10,12 @@ if hasattr(socket, 'AF_UNIX'): __all__.extend(['open_unix_connection', 'start_unix_server']) +from . import coroutines from . import events from . import futures from . import protocols from . import tasks +from .coroutines import coroutine _DEFAULT_LIMIT = 2**16 @@ -33,7 +35,7 @@ self.expected = expected - at tasks.coroutine + at coroutine def open_connection(host=None, port=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """A wrapper for create_connection() returning a (reader, writer) pair. @@ -63,7 +65,7 @@ return reader, writer - at tasks.coroutine + at coroutine def start_server(client_connected_cb, host=None, port=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """Start a socket server, call back for each client connected. @@ -102,7 +104,7 @@ if hasattr(socket, 'AF_UNIX'): # UNIX Domain Sockets are supported on this platform - @tasks.coroutine + @coroutine def open_unix_connection(path=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """Similar to `open_connection` but works with UNIX Domain Sockets.""" @@ -116,7 +118,7 @@ return reader, writer - @tasks.coroutine + @coroutine def start_unix_server(client_connected_cb, path=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """Similar to `start_server` but works with UNIX Domain Sockets.""" @@ -210,7 +212,7 @@ self._loop) res = self._client_connected_cb(self._stream_reader, self._stream_writer) - if tasks.iscoroutine(res): + if coroutines.iscoroutine(res): tasks.Task(res, loop=self._loop) def connection_lost(self, exc): @@ -373,7 +375,7 @@ 'already waiting for incoming data' % func_name) return futures.Future(loop=self._loop) - @tasks.coroutine + @coroutine def readline(self): if self._exception is not None: raise self._exception @@ -410,7 +412,7 @@ self._maybe_resume_transport() return bytes(line) - @tasks.coroutine + @coroutine def read(self, n=-1): if self._exception is not None: raise self._exception @@ -449,7 +451,7 @@ self._maybe_resume_transport() return data - @tasks.coroutine + @coroutine def readexactly(self, n): if self._exception is not None: raise self._exception diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py --- a/Lib/asyncio/subprocess.py +++ b/Lib/asyncio/subprocess.py @@ -8,6 +8,7 @@ from . import protocols from . import streams from . import tasks +from .coroutines import coroutine PIPE = subprocess.PIPE @@ -94,7 +95,7 @@ def returncode(self): return self._transport.get_returncode() - @tasks.coroutine + @coroutine def wait(self): """Wait until the process exit and return the process return code.""" returncode = self._transport.get_returncode() @@ -122,17 +123,17 @@ self._check_alive() self._transport.kill() - @tasks.coroutine + @coroutine def _feed_stdin(self, input): self.stdin.write(input) yield from self.stdin.drain() self.stdin.close() - @tasks.coroutine + @coroutine def _noop(self): return None - @tasks.coroutine + @coroutine def _read_stream(self, fd): transport = self._transport.get_pipe_transport(fd) if fd == 2: @@ -144,7 +145,7 @@ transport.close() return output - @tasks.coroutine + @coroutine def communicate(self, input=None): if input: stdin = self._feed_stdin(input) @@ -164,7 +165,7 @@ return (stdout, stderr) - at tasks.coroutine + at coroutine def create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=streams._DEFAULT_LIMIT, **kwds): if loop is None: @@ -178,7 +179,7 @@ yield from protocol.waiter return Process(transport, protocol, loop) - at tasks.coroutine + at coroutine def create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, loop=None, limit=streams._DEFAULT_LIMIT, **kwds): diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -1,7 +1,6 @@ """Support for tasks, coroutines and the scheduler.""" -__all__ = ['coroutine', 'Task', - 'iscoroutinefunction', 'iscoroutine', +__all__ = ['Task', 'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED', 'wait', 'wait_for', 'as_completed', 'sleep', 'async', 'gather', 'shield', @@ -11,146 +10,20 @@ import functools import inspect import linecache -import os import sys import traceback import weakref +from . import coroutines from . import events from . import futures +from .coroutines import coroutine from .log import logger -# If you set _DEBUG to true, @coroutine will wrap the resulting -# generator objects in a CoroWrapper instance (defined below). That -# instance will log a message when the generator is never iterated -# over, which may happen when you forget to use "yield from" with a -# coroutine call. Note that the value of the _DEBUG flag is taken -# when the decorator is used, so to be of any use it must be set -# before you define your coroutines. A downside of using this feature -# is that tracebacks show entries for the CoroWrapper.__next__ method -# when _DEBUG is true. -_DEBUG = (not sys.flags.ignore_environment - and bool(os.environ.get('PYTHONASYNCIODEBUG'))) - _PY34 = (sys.version_info >= (3, 4)) _PY35 = (sys.version_info >= (3, 5)) -class CoroWrapper: - # Wrapper for coroutine in _DEBUG mode. - - def __init__(self, gen, func): - assert inspect.isgenerator(gen), gen - self.gen = gen - self.func = func - self._source_traceback = traceback.extract_stack(sys._getframe(1)) - - def __iter__(self): - return self - - def __next__(self): - return next(self.gen) - - def send(self, *value): - # We use `*value` because of a bug in CPythons prior - # to 3.4.1. See issue #21209 and test_yield_from_corowrapper - # for details. This workaround should be removed in 3.5.0. - if len(value) == 1: - value = value[0] - return self.gen.send(value) - - def throw(self, exc): - return self.gen.throw(exc) - - def close(self): - return self.gen.close() - - @property - def gi_frame(self): - return self.gen.gi_frame - - @property - def gi_running(self): - return self.gen.gi_running - - @property - def gi_code(self): - return self.gen.gi_code - - def __del__(self): - # Be careful accessing self.gen.frame -- self.gen might not exist. - gen = getattr(self, 'gen', None) - frame = getattr(gen, 'gi_frame', None) - if frame is not None and frame.f_lasti == -1: - func = events._format_callback(self.func, ()) - tb = ''.join(traceback.format_list(self._source_traceback)) - message = ('Coroutine %s was never yielded from\n' - 'Coroutine object created at (most recent call last):\n' - '%s' - % (func, tb.rstrip())) - logger.error(message) - - -def coroutine(func): - """Decorator to mark coroutines. - - If the coroutine is not yielded from before it is destroyed, - an error message is logged. - """ - if inspect.isgeneratorfunction(func): - coro = func - else: - @functools.wraps(func) - def coro(*args, **kw): - res = func(*args, **kw) - if isinstance(res, futures.Future) or inspect.isgenerator(res): - res = yield from res - return res - - if not _DEBUG: - wrapper = coro - else: - @functools.wraps(func) - def wrapper(*args, **kwds): - w = CoroWrapper(coro(*args, **kwds), func) - if w._source_traceback: - del w._source_traceback[-1] - w.__name__ = func.__name__ - if _PY35: - w.__qualname__ = func.__qualname__ - w.__doc__ = func.__doc__ - return w - - wrapper._is_coroutine = True # For iscoroutinefunction(). - return wrapper - - -def iscoroutinefunction(func): - """Return True if func is a decorated coroutine function.""" - return getattr(func, '_is_coroutine', False) - - -def iscoroutine(obj): - """Return True if obj is a coroutine object.""" - return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj) - - -def _format_coroutine(coro): - assert iscoroutine(coro) - if _PY35: - coro_name = coro.__qualname__ - else: - coro_name = coro.__name__ - - filename = coro.gi_code.co_filename - if coro.gi_frame is not None: - lineno = coro.gi_frame.f_lineno - return '%s() at %s:%s' % (coro_name, filename, lineno) - else: - lineno = coro.gi_code.co_firstlineno - return '%s() done at %s:%s' % (coro_name, filename, lineno) - - class Task(futures.Future): """A coroutine wrapped in a Future.""" @@ -193,7 +66,7 @@ return {t for t in cls._all_tasks if t._loop is loop} def __init__(self, coro, *, loop=None): - assert iscoroutine(coro), repr(coro) # Not a coroutine function! + assert coroutines.iscoroutine(coro), repr(coro) # Not a coroutine function! super().__init__(loop=loop) if self._source_traceback: del self._source_traceback[-1] @@ -225,7 +98,7 @@ else: info.append(self._state.lower()) - info.append(_format_coroutine(self._coro)) + info.append(coroutines._format_coroutine(self._coro)) if self._state == futures._FINISHED: info.append(self._format_result()) @@ -444,7 +317,7 @@ Note: This does not raise TimeoutError! Futures that aren't done when the timeout occurs are returned in the second set. """ - if isinstance(fs, futures.Future) or iscoroutine(fs): + if isinstance(fs, futures.Future) or coroutines.iscoroutine(fs): raise TypeError("expect a list of futures, not %s" % type(fs).__name__) if not fs: raise ValueError('Set of coroutines/Futures is empty.') @@ -566,7 +439,7 @@ Note: The futures 'f' are not necessarily members of fs. """ - if isinstance(fs, futures.Future) or iscoroutine(fs): + if isinstance(fs, futures.Future) or coroutines.iscoroutine(fs): raise TypeError("expect a list of futures, not %s" % type(fs).__name__) loop = loop if loop is not None else events.get_event_loop() todo = {async(f, loop=loop) for f in set(fs)} @@ -624,7 +497,7 @@ if loop is not None and loop is not coro_or_future._loop: raise ValueError('loop argument must agree with Future') return coro_or_future - elif iscoroutine(coro_or_future): + elif coroutines.iscoroutine(coro_or_future): task = Task(coro_or_future, loop=loop) if task._source_traceback: del task._source_traceback[-1] 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 @@ -27,6 +27,7 @@ from . import futures from . import selectors from . import tasks +from .coroutines import coroutine if sys.platform == 'win32': # pragma: no cover @@ -43,7 +44,7 @@ def run_briefly(loop): - @tasks.coroutine + @coroutine def once(): pass gen = once() 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 @@ -16,8 +16,8 @@ from . import constants from . import events from . import selector_events -from . import tasks from . import transports +from .coroutines import coroutine from .log import logger @@ -147,7 +147,7 @@ extra=None): return _UnixWritePipeTransport(self, pipe, protocol, waiter, extra) - @tasks.coroutine + @coroutine def _make_subprocess_transport(self, protocol, args, shell, stdin, stdout, stderr, bufsize, extra=None, **kwargs): @@ -164,7 +164,7 @@ def _child_watcher_callback(self, pid, returncode, transp): self.call_soon_threadsafe(transp._process_exited, returncode) - @tasks.coroutine + @coroutine def create_unix_connection(self, protocol_factory, path, *, ssl=None, sock=None, server_hostname=None): @@ -199,7 +199,7 @@ sock, protocol_factory, ssl, server_hostname) return transport, protocol - @tasks.coroutine + @coroutine def create_unix_server(self, protocol_factory, path=None, *, sock=None, backlog=100, ssl=None): if isinstance(ssl, bool): 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 @@ -14,8 +14,9 @@ from . import selector_events from . import tasks from . import windows_utils +from . import _overlapped +from .coroutines import coroutine from .log import logger -from . import _overlapped __all__ = ['SelectorEventLoop', 'ProactorEventLoop', 'IocpProactor', @@ -129,7 +130,7 @@ def _socketpair(self): return windows_utils.socketpair() - @tasks.coroutine + @coroutine def create_pipe_connection(self, protocol_factory, address): f = self._proactor.connect_pipe(address) pipe = yield from f @@ -138,7 +139,7 @@ extra={'addr': address}) return trans, protocol - @tasks.coroutine + @coroutine def start_serving_pipe(self, protocol_factory, address): server = PipeServer(address) @@ -172,7 +173,7 @@ self.call_soon(loop) return [server] - @tasks.coroutine + @coroutine def _make_subprocess_transport(self, protocol, args, shell, stdin, stdout, stderr, bufsize, extra=None, **kwargs): @@ -258,7 +259,7 @@ conn.settimeout(listener.gettimeout()) return conn, conn.getpeername() - @tasks.coroutine + @coroutine def accept_coro(future, conn): # Coroutine closing the accept socket if the future is cancelled try: 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 @@ -11,7 +11,7 @@ from unittest import mock import asyncio -from asyncio import tasks +from asyncio import coroutines from asyncio import test_utils @@ -193,7 +193,7 @@ # attribute). coro_name = 'notmuch' coro_qualname = 'TaskTests.test_task_repr_coro_decorator..notmuch' - elif tasks._DEBUG: + elif coroutines._DEBUG: # In debug mode, @coroutine decorator uses CoroWrapper which gets # its name (__name__ attribute) from the wrapped coroutine # function. @@ -1475,23 +1475,23 @@ self.assertIsNone(gen.gi_frame) # Save debug flag. - old_debug = asyncio.tasks._DEBUG + old_debug = asyncio.coroutines._DEBUG try: # Test with debug flag cleared. - asyncio.tasks._DEBUG = False + asyncio.coroutines._DEBUG = False check() # Test with debug flag set. - asyncio.tasks._DEBUG = True + asyncio.coroutines._DEBUG = True check() finally: # Restore original debug flag. - asyncio.tasks._DEBUG = old_debug + asyncio.coroutines._DEBUG = old_debug def test_yield_from_corowrapper(self): - old_debug = asyncio.tasks._DEBUG - asyncio.tasks._DEBUG = True + old_debug = asyncio.coroutines._DEBUG + asyncio.coroutines._DEBUG = True try: @asyncio.coroutine def t1(): @@ -1511,7 +1511,7 @@ val = self.loop.run_until_complete(task) self.assertEqual(val, (1, 2, 3)) finally: - asyncio.tasks._DEBUG = old_debug + asyncio.coroutines._DEBUG = old_debug def test_yield_from_corowrapper_send(self): def foo(): @@ -1519,7 +1519,7 @@ return a def call(arg): - cw = asyncio.tasks.CoroWrapper(foo(), foo) + cw = asyncio.coroutines.CoroWrapper(foo(), foo) cw.send(None) try: cw.send(arg) @@ -1534,7 +1534,7 @@ def test_corowrapper_weakref(self): wd = weakref.WeakValueDictionary() def foo(): yield from [] - cw = asyncio.tasks.CoroWrapper(foo(), foo) + cw = asyncio.coroutines.CoroWrapper(foo(), foo) wd['cw'] = cw # Would fail without __weakref__ slot. cw.gen = None # Suppress warning from __del__. @@ -1580,16 +1580,16 @@ }) mock_handler.reset_mock() - @mock.patch('asyncio.tasks.logger') + @mock.patch('asyncio.coroutines.logger') def test_coroutine_never_yielded(self, m_log): - debug = asyncio.tasks._DEBUG + debug = asyncio.coroutines._DEBUG try: - asyncio.tasks._DEBUG = True + asyncio.coroutines._DEBUG = True @asyncio.coroutine def coro_noop(): pass finally: - asyncio.tasks._DEBUG = debug + asyncio.coroutines._DEBUG = debug tb_filename = __file__ tb_lineno = sys._getframe().f_lineno + 1 @@ -1695,8 +1695,8 @@ def test_env_var_debug(self): code = '\n'.join(( - 'import asyncio.tasks', - 'print(asyncio.tasks._DEBUG)')) + 'import asyncio.coroutines', + 'print(asyncio.coroutines._DEBUG)')) # Test with -E to not fail if the unit test was run with # PYTHONASYNCIODEBUG set to a non-empty string -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 29 00:53:12 2014 From: python-checkins at python.org (victor.stinner) Date: Sun, 29 Jun 2014 00:53:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_asyncio=3A_sync_with_Tulip=2C_add_a_ne?= =?utf-8?q?w_asyncio=2Ecoroutines_module?= Message-ID: <3h19Gw6Fdqz7Ljc@mail.python.org> http://hg.python.org/cpython/rev/4f3a8829c069 changeset: 91462:4f3a8829c069 parent: 91460:54f94e753269 parent: 91461:8734e881c400 user: Victor Stinner date: Sun Jun 29 00:47:28 2014 +0200 summary: (Merge 3.4) asyncio: sync with Tulip, add a new asyncio.coroutines module files: Lib/asyncio/__init__.py | 4 +- Lib/asyncio/base_events.py | 28 +- Lib/asyncio/base_subprocess.py | 4 +- Lib/asyncio/tasks.py | 633 +--------------- Lib/asyncio/locks.py | 12 +- Lib/asyncio/streams.py | 18 +- Lib/asyncio/subprocess.py | 15 +- Lib/asyncio/tasks.py | 143 +--- Lib/asyncio/test_utils.py | 3 +- Lib/asyncio/unix_events.py | 8 +- Lib/asyncio/windows_events.py | 11 +- Lib/test/test_asyncio/test_tasks.py | 34 +- 12 files changed, 83 insertions(+), 830 deletions(-) diff --git a/Lib/asyncio/__init__.py b/Lib/asyncio/__init__.py --- a/Lib/asyncio/__init__.py +++ b/Lib/asyncio/__init__.py @@ -18,6 +18,7 @@ import _overlapped # Will also be exported. # This relies on each of the submodules having an __all__ variable. +from .coroutines import * from .events import * from .futures import * from .locks import * @@ -34,7 +35,8 @@ from .unix_events import * # pragma: no cover -__all__ = (events.__all__ + +__all__ = (coroutines.__all__ + + events.__all__ + futures.__all__ + locks.__all__ + protocols.__all__ + 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 @@ -26,9 +26,11 @@ import os import sys +from . import coroutines from . import events from . import futures from . import tasks +from .coroutines import coroutine from .log import logger @@ -118,7 +120,7 @@ if not waiter.done(): waiter.set_result(waiter) - @tasks.coroutine + @coroutine def wait_closed(self): if self.sockets is None or self.waiters is None: return @@ -175,7 +177,7 @@ """Create write pipe transport.""" raise NotImplementedError - @tasks.coroutine + @coroutine def _make_subprocess_transport(self, protocol, args, shell, stdin, stdout, stderr, bufsize, extra=None, **kwargs): @@ -298,7 +300,7 @@ def call_at(self, when, callback, *args): """Like call_later(), but uses an absolute time.""" - if tasks.iscoroutinefunction(callback): + if coroutines.iscoroutinefunction(callback): raise TypeError("coroutines cannot be used with call_at()") if self._debug: self._assert_is_current_event_loop() @@ -324,7 +326,7 @@ return handle def _call_soon(self, callback, args, check_loop): - if tasks.iscoroutinefunction(callback): + if coroutines.iscoroutinefunction(callback): raise TypeError("coroutines cannot be used with call_soon()") if self._debug and check_loop: self._assert_is_current_event_loop() @@ -361,7 +363,7 @@ return handle def run_in_executor(self, executor, callback, *args): - if tasks.iscoroutinefunction(callback): + if coroutines.iscoroutinefunction(callback): raise TypeError("coroutines cannot be used with run_in_executor()") if isinstance(callback, events.Handle): assert not args @@ -389,7 +391,7 @@ def getnameinfo(self, sockaddr, flags=0): return self.run_in_executor(None, socket.getnameinfo, sockaddr, flags) - @tasks.coroutine + @coroutine def create_connection(self, protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None): @@ -505,7 +507,7 @@ sock, protocol_factory, ssl, server_hostname) return transport, protocol - @tasks.coroutine + @coroutine def _create_connection_transport(self, sock, protocol_factory, ssl, server_hostname): protocol = protocol_factory() @@ -521,7 +523,7 @@ yield from waiter return transport, protocol - @tasks.coroutine + @coroutine def create_datagram_endpoint(self, protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0): @@ -593,7 +595,7 @@ transport = self._make_datagram_transport(sock, protocol, r_addr) return transport, protocol - @tasks.coroutine + @coroutine def create_server(self, protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, @@ -672,7 +674,7 @@ self._start_serving(protocol_factory, sock, ssl, server) return server - @tasks.coroutine + @coroutine def connect_read_pipe(self, protocol_factory, pipe): protocol = protocol_factory() waiter = futures.Future(loop=self) @@ -680,7 +682,7 @@ yield from waiter return transport, protocol - @tasks.coroutine + @coroutine def connect_write_pipe(self, protocol_factory, pipe): protocol = protocol_factory() waiter = futures.Future(loop=self) @@ -688,7 +690,7 @@ yield from waiter return transport, protocol - @tasks.coroutine + @coroutine def subprocess_shell(self, protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False, shell=True, bufsize=0, @@ -706,7 +708,7 @@ protocol, cmd, True, stdin, stdout, stderr, bufsize, **kwargs) return transport, protocol - @tasks.coroutine + @coroutine def subprocess_exec(self, protocol_factory, program, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False, 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 @@ -2,8 +2,8 @@ import subprocess from . import protocols -from . import tasks from . import transports +from .coroutines import coroutine class BaseSubprocessTransport(transports.SubprocessTransport): @@ -65,7 +65,7 @@ def kill(self): self._proc.kill() - @tasks.coroutine + @coroutine def _post_init(self): proc = self._proc loop = self._loop diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/coroutines.py copy from Lib/asyncio/tasks.py copy to Lib/asyncio/coroutines.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/coroutines.py @@ -1,20 +1,11 @@ -"""Support for tasks, coroutines and the scheduler.""" +__all__ = ['coroutine', + 'iscoroutinefunction', 'iscoroutine'] -__all__ = ['coroutine', 'Task', - 'iscoroutinefunction', 'iscoroutine', - 'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED', - 'wait', 'wait_for', 'as_completed', 'sleep', 'async', - 'gather', 'shield', - ] - -import concurrent.futures import functools import inspect -import linecache import os import sys import traceback -import weakref from . import events from . import futures @@ -32,10 +23,8 @@ _DEBUG = (not sys.flags.ignore_environment and bool(os.environ.get('PYTHONASYNCIODEBUG'))) -_PY34 = (sys.version_info >= (3, 4)) _PY35 = (sys.version_info >= (3, 5)) - class CoroWrapper: # Wrapper for coroutine in _DEBUG mode. @@ -149,621 +138,3 @@ else: lineno = coro.gi_code.co_firstlineno return '%s() done at %s:%s' % (coro_name, filename, lineno) - - -class Task(futures.Future): - """A coroutine wrapped in a Future.""" - - # An important invariant maintained while a Task not done: - # - # - Either _fut_waiter is None, and _step() is scheduled; - # - or _fut_waiter is some Future, and _step() is *not* scheduled. - # - # The only transition from the latter to the former is through - # _wakeup(). When _fut_waiter is not None, one of its callbacks - # must be _wakeup(). - - # Weak set containing all tasks alive. - _all_tasks = weakref.WeakSet() - - # Dictionary containing tasks that are currently active in - # all running event loops. {EventLoop: Task} - _current_tasks = {} - - @classmethod - def current_task(cls, loop=None): - """Return the currently running task in an event loop or None. - - By default the current task for the current event loop is returned. - - None is returned when called not in the context of a Task. - """ - if loop is None: - loop = events.get_event_loop() - return cls._current_tasks.get(loop) - - @classmethod - def all_tasks(cls, loop=None): - """Return a set of all tasks for an event loop. - - By default all tasks for the current event loop are returned. - """ - if loop is None: - loop = events.get_event_loop() - return {t for t in cls._all_tasks if t._loop is loop} - - def __init__(self, coro, *, loop=None): - assert iscoroutine(coro), repr(coro) # Not a coroutine function! - super().__init__(loop=loop) - if self._source_traceback: - del self._source_traceback[-1] - self._coro = iter(coro) # Use the iterator just in case. - self._fut_waiter = None - self._must_cancel = False - self._loop.call_soon(self._step) - self.__class__._all_tasks.add(self) - - # On Python 3.3 or older, objects with a destructor part of a reference - # cycle are never destroyed. It's not more the case on Python 3.4 thanks to - # the PEP 442. - if _PY34: - def __del__(self): - if self._state == futures._PENDING: - context = { - 'task': self, - 'message': 'Task was destroyed but it is pending!', - } - if self._source_traceback: - context['source_traceback'] = self._source_traceback - self._loop.call_exception_handler(context) - futures.Future.__del__(self) - - def __repr__(self): - info = [] - if self._must_cancel: - info.append('cancelling') - else: - info.append(self._state.lower()) - - info.append(_format_coroutine(self._coro)) - - if self._state == futures._FINISHED: - info.append(self._format_result()) - - if self._callbacks: - info.append(self._format_callbacks()) - - return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) - - def get_stack(self, *, limit=None): - """Return the list of stack frames for this task's coroutine. - - If the coroutine is active, this returns the stack where it is - suspended. If the coroutine has completed successfully or was - cancelled, this returns an empty list. If the coroutine was - terminated by an exception, this returns the list of traceback - frames. - - The frames are always ordered from oldest to newest. - - 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. (This matches the - behavior of the traceback module.) - - For reasons beyond our control, only one stack frame is - returned for a suspended coroutine. - """ - frames = [] - f = self._coro.gi_frame - if f is not None: - while f is not None: - if limit is not None: - if limit <= 0: - break - limit -= 1 - frames.append(f) - f = f.f_back - frames.reverse() - elif self._exception is not None: - tb = self._exception.__traceback__ - while tb is not None: - if limit is not None: - if limit <= 0: - break - limit -= 1 - frames.append(tb.tb_frame) - tb = tb.tb_next - return frames - - def print_stack(self, *, limit=None, file=None): - """Print the stack or traceback for this task's coroutine. - - This produces output similar to that of the traceback module, - for the frames retrieved by get_stack(). The limit argument - is passed to get_stack(). The file argument is an I/O stream - to which the output goes; by default it goes to sys.stderr. - """ - extracted_list = [] - checked = set() - for f in self.get_stack(limit=limit): - lineno = f.f_lineno - co = f.f_code - filename = co.co_filename - name = co.co_name - if filename not in checked: - checked.add(filename) - linecache.checkcache(filename) - line = linecache.getline(filename, lineno, f.f_globals) - extracted_list.append((filename, lineno, name, line)) - exc = self._exception - if not extracted_list: - print('No stack for %r' % self, file=file) - elif exc is not None: - print('Traceback for %r (most recent call last):' % self, - file=file) - else: - print('Stack for %r (most recent call last):' % self, - file=file) - traceback.print_list(extracted_list, file=file) - if exc is not None: - for line in traceback.format_exception_only(exc.__class__, exc): - print(line, file=file, end='') - - def cancel(self): - """Request this task to cancel itself. - - This arranges for a CancelledError to be thrown into the - wrapped coroutine on the next cycle through the event loop. - The coroutine then has a chance to clean up or even deny - the request using try/except/finally. - - Contrary to Future.cancel(), this does not guarantee that the - task will be cancelled: the exception might be caught and - acted upon, delaying cancellation of the task or preventing it - completely. The task may also return a value or raise a - different exception. - - Immediately after this method is called, Task.cancelled() will - not return True (unless the task was already cancelled). A - task will be marked as cancelled when the wrapped coroutine - terminates with a CancelledError exception (even if cancel() - was not called). - """ - if self.done(): - return False - if self._fut_waiter is not None: - if self._fut_waiter.cancel(): - # Leave self._fut_waiter; it may be a Task that - # catches and ignores the cancellation so we may have - # to cancel it again later. - return True - # It must be the case that self._step is already scheduled. - self._must_cancel = True - return True - - def _step(self, value=None, exc=None): - assert not self.done(), \ - '_step(): already done: {!r}, {!r}, {!r}'.format(self, value, exc) - if self._must_cancel: - if not isinstance(exc, futures.CancelledError): - exc = futures.CancelledError() - self._must_cancel = False - coro = self._coro - self._fut_waiter = None - - self.__class__._current_tasks[self._loop] = self - # Call either coro.throw(exc) or coro.send(value). - try: - if exc is not None: - result = coro.throw(exc) - elif value is not None: - result = coro.send(value) - else: - result = next(coro) - except StopIteration as exc: - self.set_result(exc.value) - except futures.CancelledError as exc: - super().cancel() # I.e., Future.cancel(self). - except Exception as exc: - self.set_exception(exc) - except BaseException as exc: - self.set_exception(exc) - raise - else: - if isinstance(result, futures.Future): - # Yielded Future must come from Future.__iter__(). - if result._blocking: - result._blocking = False - result.add_done_callback(self._wakeup) - self._fut_waiter = result - if self._must_cancel: - if self._fut_waiter.cancel(): - self._must_cancel = False - else: - self._loop.call_soon( - self._step, None, - RuntimeError( - 'yield was used instead of yield from ' - 'in task {!r} with {!r}'.format(self, result))) - elif result is None: - # Bare yield relinquishes control for one event loop iteration. - self._loop.call_soon(self._step) - elif inspect.isgenerator(result): - # Yielding a generator is just wrong. - self._loop.call_soon( - self._step, None, - RuntimeError( - 'yield was used instead of yield from for ' - 'generator in task {!r} with {}'.format( - self, result))) - else: - # Yielding something else is an error. - self._loop.call_soon( - self._step, None, - RuntimeError( - 'Task got bad yield: {!r}'.format(result))) - finally: - self.__class__._current_tasks.pop(self._loop) - self = None # Needed to break cycles when an exception occurs. - - def _wakeup(self, future): - try: - value = future.result() - except Exception as exc: - # This may also be a cancellation. - self._step(None, exc) - else: - self._step(value, None) - self = None # Needed to break cycles when an exception occurs. - - -# wait() and as_completed() similar to those in PEP 3148. - -FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED -FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION -ALL_COMPLETED = concurrent.futures.ALL_COMPLETED - - - at coroutine -def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED): - """Wait for the Futures and coroutines given by fs to complete. - - The sequence futures must not be empty. - - Coroutines will be wrapped in Tasks. - - Returns two sets of Future: (done, pending). - - Usage: - - done, pending = yield from asyncio.wait(fs) - - Note: This does not raise TimeoutError! Futures that aren't done - when the timeout occurs are returned in the second set. - """ - if isinstance(fs, futures.Future) or iscoroutine(fs): - raise TypeError("expect a list of futures, not %s" % type(fs).__name__) - if not fs: - raise ValueError('Set of coroutines/Futures is empty.') - - if loop is None: - loop = events.get_event_loop() - - 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)) - return (yield from _wait(fs, timeout, return_when, loop)) - - -def _release_waiter(waiter, value=True, *args): - if not waiter.done(): - waiter.set_result(value) - - - at coroutine -def wait_for(fut, timeout, *, loop=None): - """Wait for the single Future or coroutine to complete, with timeout. - - Coroutine will be wrapped in Task. - - Returns result of the Future or coroutine. When a timeout occurs, - it cancels the task and raises TimeoutError. To avoid the task - cancellation, wrap it in shield(). - - Usage: - - result = yield from asyncio.wait_for(fut, 10.0) - - """ - if loop is None: - loop = events.get_event_loop() - - if timeout is None: - return (yield from fut) - - waiter = futures.Future(loop=loop) - timeout_handle = loop.call_later(timeout, _release_waiter, waiter, False) - cb = functools.partial(_release_waiter, waiter, True) - - fut = async(fut, loop=loop) - fut.add_done_callback(cb) - - try: - if (yield from waiter): - return fut.result() - else: - fut.remove_done_callback(cb) - fut.cancel() - raise futures.TimeoutError() - finally: - timeout_handle.cancel() - - - at coroutine -def _wait(fs, timeout, return_when, loop): - """Internal helper for wait() and _wait_for(). - - The fs argument must be a collection of Futures. - """ - assert fs, 'Set of Futures is empty.' - waiter = futures.Future(loop=loop) - timeout_handle = None - if timeout is not None: - timeout_handle = loop.call_later(timeout, _release_waiter, waiter) - counter = len(fs) - - def _on_completion(f): - nonlocal counter - counter -= 1 - if (counter <= 0 or - return_when == FIRST_COMPLETED or - return_when == FIRST_EXCEPTION and (not f.cancelled() and - f.exception() is not None)): - if timeout_handle is not None: - timeout_handle.cancel() - if not waiter.done(): - waiter.set_result(False) - - for f in fs: - f.add_done_callback(_on_completion) - - try: - yield from waiter - finally: - if timeout_handle is not None: - timeout_handle.cancel() - - done, pending = set(), set() - for f in fs: - f.remove_done_callback(_on_completion) - if f.done(): - done.add(f) - else: - pending.add(f) - return done, pending - - -# This is *not* a @coroutine! It is just an iterator (yielding Futures). -def as_completed(fs, *, loop=None, timeout=None): - """Return an iterator whose values are coroutines. - - When waiting for the yielded coroutines you'll get the results (or - exceptions!) of the original Futures (or coroutines), in the order - in which and as soon as they complete. - - This differs from PEP 3148; the proper way to use this is: - - for f in as_completed(fs): - result = yield from f # The 'yield from' may raise. - # Use result. - - If a timeout is specified, the 'yield from' will raise - TimeoutError when the timeout occurs before all Futures are done. - - Note: The futures 'f' are not necessarily members of fs. - """ - if isinstance(fs, futures.Future) or iscoroutine(fs): - raise TypeError("expect a list of futures, not %s" % type(fs).__name__) - loop = loop if loop is not None else events.get_event_loop() - todo = {async(f, loop=loop) for f in set(fs)} - from .queues import Queue # Import here to avoid circular import problem. - done = Queue(loop=loop) - timeout_handle = None - - def _on_timeout(): - for f in todo: - f.remove_done_callback(_on_completion) - done.put_nowait(None) # Queue a dummy value for _wait_for_one(). - todo.clear() # Can't do todo.remove(f) in the loop. - - def _on_completion(f): - if not todo: - return # _on_timeout() was here first. - todo.remove(f) - done.put_nowait(f) - if not todo and timeout_handle is not None: - timeout_handle.cancel() - - @coroutine - def _wait_for_one(): - f = yield from done.get() - if f is None: - # Dummy value from _on_timeout(). - raise futures.TimeoutError - return f.result() # May raise f.exception(). - - for f in todo: - f.add_done_callback(_on_completion) - if todo and timeout is not None: - timeout_handle = loop.call_later(timeout, _on_timeout) - for _ in range(len(todo)): - yield _wait_for_one() - - - at coroutine -def sleep(delay, result=None, *, loop=None): - """Coroutine that completes after a given time (in seconds).""" - future = futures.Future(loop=loop) - h = future._loop.call_later(delay, future.set_result, result) - try: - return (yield from future) - finally: - h.cancel() - - -def async(coro_or_future, *, loop=None): - """Wrap a coroutine in a future. - - If the argument is a Future, it is returned directly. - """ - if isinstance(coro_or_future, futures.Future): - if loop is not None and loop is not coro_or_future._loop: - raise ValueError('loop argument must agree with Future') - return coro_or_future - elif iscoroutine(coro_or_future): - task = Task(coro_or_future, loop=loop) - if task._source_traceback: - del task._source_traceback[-1] - return task - else: - raise TypeError('A Future or coroutine is required') - - -class _GatheringFuture(futures.Future): - """Helper for gather(). - - This overrides cancel() to cancel all the children and act more - like Task.cancel(), which doesn't immediately mark itself as - cancelled. - """ - - def __init__(self, children, *, loop=None): - super().__init__(loop=loop) - self._children = children - - def cancel(self): - if self.done(): - return False - for child in self._children: - child.cancel() - return True - - -def gather(*coros_or_futures, loop=None, return_exceptions=False): - """Return a future aggregating results from the given coroutines - or futures. - - 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 *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. - - Cancellation: if the outer Future is cancelled, all children (that - have not completed yet) are also cancelled. If any child is - cancelled, this is treated as if it raised CancelledError -- - the outer Future is *not* cancelled in this case. (This is to - prevent the cancellation of one child to cause other children to - be cancelled.) - """ - 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) - outer.set_result([]) - return outer - if loop is None: - loop = children[0]._loop - for fut in children: - if fut._loop is not loop: - raise ValueError("futures are tied to different event loops") - outer = _GatheringFuture(children, loop=loop) - nfinished = 0 - results = [None] * n - - def _done_callback(i, fut): - nonlocal nfinished - if outer._state != futures._PENDING: - if fut._exception is not None: - # Mark exception retrieved. - fut.exception() - return - if fut._state == futures._CANCELLED: - res = futures.CancelledError() - if not return_exceptions: - outer.set_exception(res) - return - elif fut._exception is not None: - res = fut.exception() # Mark exception retrieved. - if not return_exceptions: - outer.set_exception(res) - return - else: - res = fut._result - results[i] = res - nfinished += 1 - if nfinished == n: - outer.set_result(results) - - for i, fut in enumerate(children): - fut.add_done_callback(functools.partial(_done_callback, i)) - return outer - - -def shield(arg, *, loop=None): - """Wait for a future, shielding it from cancellation. - - The statement - - res = yield from shield(something()) - - is exactly equivalent to the statement - - res = yield from something() - - *except* that if the coroutine containing it is cancelled, the - task running in something() is not cancelled. From the POV of - something(), the cancellation did not happen. But its caller is - still cancelled, so the yield-from expression still raises - CancelledError. Note: If something() is cancelled by other means - this will still cancel shield(). - - If you want to completely ignore cancellation (not recommended) - you can combine shield() with a try/except clause, as follows: - - try: - res = yield from shield(something()) - except CancelledError: - res = None - """ - inner = async(arg, loop=loop) - if inner.done(): - # Shortcut. - return inner - loop = inner._loop - outer = futures.Future(loop=loop) - - def _done_callback(inner): - if outer.cancelled(): - # Mark inner's result as retrieved. - inner.cancelled() or inner.exception() - return - if inner.cancelled(): - outer.cancel() - else: - exc = inner.exception() - if exc is not None: - outer.set_exception(exc) - else: - outer.set_result(inner.result()) - - inner.add_done_callback(_done_callback) - return outer diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py --- a/Lib/asyncio/locks.py +++ b/Lib/asyncio/locks.py @@ -6,7 +6,7 @@ from . import events from . import futures -from . import tasks +from .coroutines import coroutine class _ContextManager: @@ -112,7 +112,7 @@ """Return True if lock is acquired.""" return self._locked - @tasks.coroutine + @coroutine def acquire(self): """Acquire a lock. @@ -225,7 +225,7 @@ to true again.""" self._value = False - @tasks.coroutine + @coroutine def wait(self): """Block until the internal flag is true. @@ -278,7 +278,7 @@ extra = '{},waiters:{}'.format(extra, len(self._waiters)) return '<{} [{}]>'.format(res[1:-1], extra) - @tasks.coroutine + @coroutine def wait(self): """Wait until notified. @@ -306,7 +306,7 @@ finally: yield from self.acquire() - @tasks.coroutine + @coroutine def wait_for(self, predicate): """Wait until a predicate becomes true. @@ -402,7 +402,7 @@ """Returns True if semaphore can not be acquired immediately.""" return self._value == 0 - @tasks.coroutine + @coroutine def acquire(self): """Acquire a semaphore. diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -10,10 +10,12 @@ if hasattr(socket, 'AF_UNIX'): __all__.extend(['open_unix_connection', 'start_unix_server']) +from . import coroutines from . import events from . import futures from . import protocols from . import tasks +from .coroutines import coroutine _DEFAULT_LIMIT = 2**16 @@ -33,7 +35,7 @@ self.expected = expected - at tasks.coroutine + at coroutine def open_connection(host=None, port=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """A wrapper for create_connection() returning a (reader, writer) pair. @@ -63,7 +65,7 @@ return reader, writer - at tasks.coroutine + at coroutine def start_server(client_connected_cb, host=None, port=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """Start a socket server, call back for each client connected. @@ -102,7 +104,7 @@ if hasattr(socket, 'AF_UNIX'): # UNIX Domain Sockets are supported on this platform - @tasks.coroutine + @coroutine def open_unix_connection(path=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """Similar to `open_connection` but works with UNIX Domain Sockets.""" @@ -116,7 +118,7 @@ return reader, writer - @tasks.coroutine + @coroutine def start_unix_server(client_connected_cb, path=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """Similar to `start_server` but works with UNIX Domain Sockets.""" @@ -210,7 +212,7 @@ self._loop) res = self._client_connected_cb(self._stream_reader, self._stream_writer) - if tasks.iscoroutine(res): + if coroutines.iscoroutine(res): tasks.Task(res, loop=self._loop) def connection_lost(self, exc): @@ -373,7 +375,7 @@ 'already waiting for incoming data' % func_name) return futures.Future(loop=self._loop) - @tasks.coroutine + @coroutine def readline(self): if self._exception is not None: raise self._exception @@ -410,7 +412,7 @@ self._maybe_resume_transport() return bytes(line) - @tasks.coroutine + @coroutine def read(self, n=-1): if self._exception is not None: raise self._exception @@ -449,7 +451,7 @@ self._maybe_resume_transport() return data - @tasks.coroutine + @coroutine def readexactly(self, n): if self._exception is not None: raise self._exception diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py --- a/Lib/asyncio/subprocess.py +++ b/Lib/asyncio/subprocess.py @@ -8,6 +8,7 @@ from . import protocols from . import streams from . import tasks +from .coroutines import coroutine PIPE = subprocess.PIPE @@ -94,7 +95,7 @@ def returncode(self): return self._transport.get_returncode() - @tasks.coroutine + @coroutine def wait(self): """Wait until the process exit and return the process return code.""" returncode = self._transport.get_returncode() @@ -122,17 +123,17 @@ self._check_alive() self._transport.kill() - @tasks.coroutine + @coroutine def _feed_stdin(self, input): self.stdin.write(input) yield from self.stdin.drain() self.stdin.close() - @tasks.coroutine + @coroutine def _noop(self): return None - @tasks.coroutine + @coroutine def _read_stream(self, fd): transport = self._transport.get_pipe_transport(fd) if fd == 2: @@ -144,7 +145,7 @@ transport.close() return output - @tasks.coroutine + @coroutine def communicate(self, input=None): if input: stdin = self._feed_stdin(input) @@ -164,7 +165,7 @@ return (stdout, stderr) - at tasks.coroutine + at coroutine def create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=streams._DEFAULT_LIMIT, **kwds): if loop is None: @@ -178,7 +179,7 @@ yield from protocol.waiter return Process(transport, protocol, loop) - at tasks.coroutine + at coroutine def create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, loop=None, limit=streams._DEFAULT_LIMIT, **kwds): diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -1,7 +1,6 @@ """Support for tasks, coroutines and the scheduler.""" -__all__ = ['coroutine', 'Task', - 'iscoroutinefunction', 'iscoroutine', +__all__ = ['Task', 'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED', 'wait', 'wait_for', 'as_completed', 'sleep', 'async', 'gather', 'shield', @@ -11,146 +10,20 @@ import functools import inspect import linecache -import os import sys import traceback import weakref +from . import coroutines from . import events from . import futures +from .coroutines import coroutine from .log import logger -# If you set _DEBUG to true, @coroutine will wrap the resulting -# generator objects in a CoroWrapper instance (defined below). That -# instance will log a message when the generator is never iterated -# over, which may happen when you forget to use "yield from" with a -# coroutine call. Note that the value of the _DEBUG flag is taken -# when the decorator is used, so to be of any use it must be set -# before you define your coroutines. A downside of using this feature -# is that tracebacks show entries for the CoroWrapper.__next__ method -# when _DEBUG is true. -_DEBUG = (not sys.flags.ignore_environment - and bool(os.environ.get('PYTHONASYNCIODEBUG'))) - _PY34 = (sys.version_info >= (3, 4)) _PY35 = (sys.version_info >= (3, 5)) -class CoroWrapper: - # Wrapper for coroutine in _DEBUG mode. - - def __init__(self, gen, func): - assert inspect.isgenerator(gen), gen - self.gen = gen - self.func = func - self._source_traceback = traceback.extract_stack(sys._getframe(1)) - - def __iter__(self): - return self - - def __next__(self): - return next(self.gen) - - def send(self, *value): - # We use `*value` because of a bug in CPythons prior - # to 3.4.1. See issue #21209 and test_yield_from_corowrapper - # for details. This workaround should be removed in 3.5.0. - if len(value) == 1: - value = value[0] - return self.gen.send(value) - - def throw(self, exc): - return self.gen.throw(exc) - - def close(self): - return self.gen.close() - - @property - def gi_frame(self): - return self.gen.gi_frame - - @property - def gi_running(self): - return self.gen.gi_running - - @property - def gi_code(self): - return self.gen.gi_code - - def __del__(self): - # Be careful accessing self.gen.frame -- self.gen might not exist. - gen = getattr(self, 'gen', None) - frame = getattr(gen, 'gi_frame', None) - if frame is not None and frame.f_lasti == -1: - func = events._format_callback(self.func, ()) - tb = ''.join(traceback.format_list(self._source_traceback)) - message = ('Coroutine %s was never yielded from\n' - 'Coroutine object created at (most recent call last):\n' - '%s' - % (func, tb.rstrip())) - logger.error(message) - - -def coroutine(func): - """Decorator to mark coroutines. - - If the coroutine is not yielded from before it is destroyed, - an error message is logged. - """ - if inspect.isgeneratorfunction(func): - coro = func - else: - @functools.wraps(func) - def coro(*args, **kw): - res = func(*args, **kw) - if isinstance(res, futures.Future) or inspect.isgenerator(res): - res = yield from res - return res - - if not _DEBUG: - wrapper = coro - else: - @functools.wraps(func) - def wrapper(*args, **kwds): - w = CoroWrapper(coro(*args, **kwds), func) - if w._source_traceback: - del w._source_traceback[-1] - w.__name__ = func.__name__ - if _PY35: - w.__qualname__ = func.__qualname__ - w.__doc__ = func.__doc__ - return w - - wrapper._is_coroutine = True # For iscoroutinefunction(). - return wrapper - - -def iscoroutinefunction(func): - """Return True if func is a decorated coroutine function.""" - return getattr(func, '_is_coroutine', False) - - -def iscoroutine(obj): - """Return True if obj is a coroutine object.""" - return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj) - - -def _format_coroutine(coro): - assert iscoroutine(coro) - if _PY35: - coro_name = coro.__qualname__ - else: - coro_name = coro.__name__ - - filename = coro.gi_code.co_filename - if coro.gi_frame is not None: - lineno = coro.gi_frame.f_lineno - return '%s() at %s:%s' % (coro_name, filename, lineno) - else: - lineno = coro.gi_code.co_firstlineno - return '%s() done at %s:%s' % (coro_name, filename, lineno) - - class Task(futures.Future): """A coroutine wrapped in a Future.""" @@ -193,7 +66,7 @@ return {t for t in cls._all_tasks if t._loop is loop} def __init__(self, coro, *, loop=None): - assert iscoroutine(coro), repr(coro) # Not a coroutine function! + assert coroutines.iscoroutine(coro), repr(coro) # Not a coroutine function! super().__init__(loop=loop) if self._source_traceback: del self._source_traceback[-1] @@ -225,7 +98,7 @@ else: info.append(self._state.lower()) - info.append(_format_coroutine(self._coro)) + info.append(coroutines._format_coroutine(self._coro)) if self._state == futures._FINISHED: info.append(self._format_result()) @@ -444,7 +317,7 @@ Note: This does not raise TimeoutError! Futures that aren't done when the timeout occurs are returned in the second set. """ - if isinstance(fs, futures.Future) or iscoroutine(fs): + if isinstance(fs, futures.Future) or coroutines.iscoroutine(fs): raise TypeError("expect a list of futures, not %s" % type(fs).__name__) if not fs: raise ValueError('Set of coroutines/Futures is empty.') @@ -566,7 +439,7 @@ Note: The futures 'f' are not necessarily members of fs. """ - if isinstance(fs, futures.Future) or iscoroutine(fs): + if isinstance(fs, futures.Future) or coroutines.iscoroutine(fs): raise TypeError("expect a list of futures, not %s" % type(fs).__name__) loop = loop if loop is not None else events.get_event_loop() todo = {async(f, loop=loop) for f in set(fs)} @@ -624,7 +497,7 @@ if loop is not None and loop is not coro_or_future._loop: raise ValueError('loop argument must agree with Future') return coro_or_future - elif iscoroutine(coro_or_future): + elif coroutines.iscoroutine(coro_or_future): task = Task(coro_or_future, loop=loop) if task._source_traceback: del task._source_traceback[-1] 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 @@ -27,6 +27,7 @@ from . import futures from . import selectors from . import tasks +from .coroutines import coroutine if sys.platform == 'win32': # pragma: no cover @@ -43,7 +44,7 @@ def run_briefly(loop): - @tasks.coroutine + @coroutine def once(): pass gen = once() 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 @@ -16,8 +16,8 @@ from . import constants from . import events from . import selector_events -from . import tasks from . import transports +from .coroutines import coroutine from .log import logger @@ -147,7 +147,7 @@ extra=None): return _UnixWritePipeTransport(self, pipe, protocol, waiter, extra) - @tasks.coroutine + @coroutine def _make_subprocess_transport(self, protocol, args, shell, stdin, stdout, stderr, bufsize, extra=None, **kwargs): @@ -164,7 +164,7 @@ def _child_watcher_callback(self, pid, returncode, transp): self.call_soon_threadsafe(transp._process_exited, returncode) - @tasks.coroutine + @coroutine def create_unix_connection(self, protocol_factory, path, *, ssl=None, sock=None, server_hostname=None): @@ -199,7 +199,7 @@ sock, protocol_factory, ssl, server_hostname) return transport, protocol - @tasks.coroutine + @coroutine def create_unix_server(self, protocol_factory, path=None, *, sock=None, backlog=100, ssl=None): if isinstance(ssl, bool): 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 @@ -14,8 +14,9 @@ from . import selector_events from . import tasks from . import windows_utils +from . import _overlapped +from .coroutines import coroutine from .log import logger -from . import _overlapped __all__ = ['SelectorEventLoop', 'ProactorEventLoop', 'IocpProactor', @@ -129,7 +130,7 @@ def _socketpair(self): return windows_utils.socketpair() - @tasks.coroutine + @coroutine def create_pipe_connection(self, protocol_factory, address): f = self._proactor.connect_pipe(address) pipe = yield from f @@ -138,7 +139,7 @@ extra={'addr': address}) return trans, protocol - @tasks.coroutine + @coroutine def start_serving_pipe(self, protocol_factory, address): server = PipeServer(address) @@ -172,7 +173,7 @@ self.call_soon(loop) return [server] - @tasks.coroutine + @coroutine def _make_subprocess_transport(self, protocol, args, shell, stdin, stdout, stderr, bufsize, extra=None, **kwargs): @@ -258,7 +259,7 @@ conn.settimeout(listener.gettimeout()) return conn, conn.getpeername() - @tasks.coroutine + @coroutine def accept_coro(future, conn): # Coroutine closing the accept socket if the future is cancelled try: 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 @@ -11,7 +11,7 @@ from unittest import mock import asyncio -from asyncio import tasks +from asyncio import coroutines from asyncio import test_utils @@ -193,7 +193,7 @@ # attribute). coro_name = 'notmuch' coro_qualname = 'TaskTests.test_task_repr_coro_decorator..notmuch' - elif tasks._DEBUG: + elif coroutines._DEBUG: # In debug mode, @coroutine decorator uses CoroWrapper which gets # its name (__name__ attribute) from the wrapped coroutine # function. @@ -1475,23 +1475,23 @@ self.assertIsNone(gen.gi_frame) # Save debug flag. - old_debug = asyncio.tasks._DEBUG + old_debug = asyncio.coroutines._DEBUG try: # Test with debug flag cleared. - asyncio.tasks._DEBUG = False + asyncio.coroutines._DEBUG = False check() # Test with debug flag set. - asyncio.tasks._DEBUG = True + asyncio.coroutines._DEBUG = True check() finally: # Restore original debug flag. - asyncio.tasks._DEBUG = old_debug + asyncio.coroutines._DEBUG = old_debug def test_yield_from_corowrapper(self): - old_debug = asyncio.tasks._DEBUG - asyncio.tasks._DEBUG = True + old_debug = asyncio.coroutines._DEBUG + asyncio.coroutines._DEBUG = True try: @asyncio.coroutine def t1(): @@ -1511,7 +1511,7 @@ val = self.loop.run_until_complete(task) self.assertEqual(val, (1, 2, 3)) finally: - asyncio.tasks._DEBUG = old_debug + asyncio.coroutines._DEBUG = old_debug def test_yield_from_corowrapper_send(self): def foo(): @@ -1519,7 +1519,7 @@ return a def call(arg): - cw = asyncio.tasks.CoroWrapper(foo(), foo) + cw = asyncio.coroutines.CoroWrapper(foo(), foo) cw.send(None) try: cw.send(arg) @@ -1534,7 +1534,7 @@ def test_corowrapper_weakref(self): wd = weakref.WeakValueDictionary() def foo(): yield from [] - cw = asyncio.tasks.CoroWrapper(foo(), foo) + cw = asyncio.coroutines.CoroWrapper(foo(), foo) wd['cw'] = cw # Would fail without __weakref__ slot. cw.gen = None # Suppress warning from __del__. @@ -1580,16 +1580,16 @@ }) mock_handler.reset_mock() - @mock.patch('asyncio.tasks.logger') + @mock.patch('asyncio.coroutines.logger') def test_coroutine_never_yielded(self, m_log): - debug = asyncio.tasks._DEBUG + debug = asyncio.coroutines._DEBUG try: - asyncio.tasks._DEBUG = True + asyncio.coroutines._DEBUG = True @asyncio.coroutine def coro_noop(): pass finally: - asyncio.tasks._DEBUG = debug + asyncio.coroutines._DEBUG = debug tb_filename = __file__ tb_lineno = sys._getframe().f_lineno + 1 @@ -1695,8 +1695,8 @@ def test_env_var_debug(self): code = '\n'.join(( - 'import asyncio.tasks', - 'print(asyncio.tasks._DEBUG)')) + 'import asyncio.coroutines', + 'print(asyncio.coroutines._DEBUG)')) # Test with -E to not fail if the unit test was run with # PYTHONASYNCIODEBUG set to a non-empty string -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Sun Jun 29 10:55:41 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 29 Jun 2014 10:55:41 +0200 Subject: [Python-checkins] Daily reference leaks (4f3a8829c069): sum=9 Message-ID: results for 4f3a8829c069 on branch "default" -------------------------------------------- test_collections leaked [0, -2, 2] references, sum=0 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogNpeCa8', '-x'] From root at python.org Sun Jun 29 12:10:22 2014 From: root at python.org (Cron Daemon) Date: Sun, 29 Jun 2014 12:10:22 +0200 Subject: [Python-checkins] Cron /home/docs/build-devguide Message-ID: abort: error: Connection timed out From python-checkins at python.org Sun Jun 29 14:56:22 2014 From: python-checkins at python.org (berker.peksag) Date: Sun, 29 Jun 2014 14:56:22 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIwNzUz?= =?utf-8?q?=3A_Skip_PasswordProtectedSiteTestCase_when_Python_is_built_wit?= =?utf-8?q?hout?= Message-ID: <3h1Wzp17qbz7LjZ@mail.python.org> http://hg.python.org/cpython/rev/0e08ca451b34 changeset: 91463:0e08ca451b34 branch: 3.4 parent: 91461:8734e881c400 user: Berker Peksag date: Sun Jun 29 15:54:56 2014 +0300 summary: Issue #20753: Skip PasswordProtectedSiteTestCase when Python is built without threads. files: Lib/test/test_robotparser.py | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -4,8 +4,11 @@ from urllib.error import URLError, HTTPError from urllib.request import urlopen from test import support -import threading from http.server import BaseHTTPRequestHandler, HTTPServer +try: + import threading +except ImportError: + threading = None class RobotTestCase(unittest.TestCase): @@ -259,6 +262,7 @@ pass + at unittest.skipUnless(threading, 'threading required for this test') class PasswordProtectedSiteTestCase(unittest.TestCase): def setUp(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 29 14:56:23 2014 From: python-checkins at python.org (berker.peksag) Date: Sun, 29 Jun 2014 14:56:23 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320753=3A_Merge_with_3=2E4=2E?= Message-ID: <3h1Wzq2Nxqz7LkC@mail.python.org> http://hg.python.org/cpython/rev/394e6bda5a70 changeset: 91464:394e6bda5a70 parent: 91462:4f3a8829c069 parent: 91463:0e08ca451b34 user: Berker Peksag date: Sun Jun 29 15:56:21 2014 +0300 summary: Issue #20753: Merge with 3.4. files: Lib/test/test_robotparser.py | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -4,8 +4,11 @@ from urllib.error import URLError, HTTPError from urllib.request import urlopen from test import support -import threading from http.server import BaseHTTPRequestHandler, HTTPServer +try: + import threading +except ImportError: + threading = None class RobotTestCase(unittest.TestCase): @@ -259,6 +262,7 @@ pass + at unittest.skipUnless(threading, 'threading required for this test') class PasswordProtectedSiteTestCase(unittest.TestCase): def setUp(self): -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 29 22:04:04 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 29 Jun 2014 22:04:04 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogZG9uJ3QgYWxsb3cg?= =?utf-8?q?unicode_into_type=5Fmap_on_Windows_=28closes_=2321652=29?= Message-ID: <3h1jTJ0hTGz7LjW@mail.python.org> http://hg.python.org/cpython/rev/1aafbdfba25a changeset: 91465:1aafbdfba25a branch: 2.7 parent: 91458:4ef517041573 user: Benjamin Peterson date: Sun Jun 29 12:58:16 2014 -0700 summary: don't allow unicode into type_map on Windows (closes #21652) Patch from Vladimir Iofik. files: Lib/mimetypes.py | 29 ++++++++++++++----------- Lib/test/test_mimetypes.py | 24 ++++++++++++++++++-- Misc/NEWS | 3 ++ 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -247,23 +247,26 @@ break i += 1 + default_encoding = sys.getdefaultencoding() with _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, '') as hkcr: for subkeyname in enum_types(hkcr): - # Only check file extensions, not all possible classes - if not subkeyname.startswith("."): - continue - - with _winreg.OpenKey(hkcr, subkeyname) as subkey: - # If there is no "Content Type" value, or if it is not - # a simple string, simply skip - try: + try: + with _winreg.OpenKey(hkcr, subkeyname) as subkey: + # Only check file extensions + if not subkeyname.startswith("."): + continue + # raises EnvironmentError if no 'Content Type' value mimetype, datatype = _winreg.QueryValueEx( subkey, 'Content Type') - except EnvironmentError: - continue - if datatype != _winreg.REG_SZ: - continue - self.add_type(mimetype, subkeyname, strict) + if datatype != _winreg.REG_SZ: + continue + try: + mimetype = mimetype.encode(default_encoding) + except UnicodeEncodeError: + continue + self.add_type(mimetype, subkeyname, strict) + except EnvironmentError: + continue def guess_type(url, strict=True): """Guess the type of a file based on its URL. diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import mimetypes import StringIO import unittest @@ -95,10 +97,10 @@ def __getattr__(self, name): if name == 'EnumKey': return lambda key, i: _winreg.EnumKey(key, i) + "\xa3" - elif name == "OpenKey": + elif name == 'OpenKey': return lambda key, name: _winreg.OpenKey(key, name.rstrip("\xa3")) elif name == 'QueryValueEx': - return lambda subkey, label: (label + "\xa3", _winreg.REG_SZ) + return lambda subkey, label: (u'?????/???????' , _winreg.REG_SZ) return getattr(_winreg, name) mimetypes._winreg = MockWinreg() @@ -115,7 +117,7 @@ class MockWinreg(object): def __getattr__(self, name): if name == 'QueryValueEx': - return lambda subkey, label: (label + "\xa3", _winreg.REG_SZ) + return lambda subkey, label: (u'?????/???????', _winreg.REG_SZ) return getattr(_winreg, name) mimetypes._winreg = MockWinreg() @@ -126,6 +128,22 @@ finally: mimetypes._winreg = _winreg + def test_type_map_values(self): + import _winreg + + class MockWinreg(object): + def __getattr__(self, name): + if name == 'QueryValueEx': + return lambda subkey, label: (u'text/plain', _winreg.REG_SZ) + return getattr(_winreg, name) + + mimetypes._winreg = MockWinreg() + try: + mimetypes.init() + self.assertTrue(isinstance(mimetypes.types_map.values()[0], str)) + finally: + mimetypes._winreg = _winreg + def test_main(): test_support.run_unittest(MimeTypesTestCase, Win32MimeTypesTestCase diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -35,6 +35,9 @@ Library ------- +- Issue #21652: Prevent mimetypes.type_map from containing unicode keys on + Windows. + - Issue #21729: Used the "with" statement in the dbm.dumb module to ensure files closing. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 29 22:04:05 2014 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 29 Jun 2014 22:04:05 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_add_a_test_for?= =?utf-8?q?_access_errors_from_OpenKey_=28closes_=2321871=29?= Message-ID: <3h1jTK24YZz7Lk0@mail.python.org> http://hg.python.org/cpython/rev/ee33d61f5e4b changeset: 91466:ee33d61f5e4b branch: 2.7 user: Benjamin Peterson date: Sun Jun 29 13:02:12 2014 -0700 summary: add a test for access errors from OpenKey (closes #21871) Patch from Vladimir Iofik. files: Lib/test/test_mimetypes.py | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -144,6 +144,23 @@ finally: mimetypes._winreg = _winreg + def test_registry_read_error(self): + import _winreg + + class MockWinreg(object): + def OpenKey(self, key, name): + if key != _winreg.HKEY_CLASSES_ROOT: + raise WindowsError(5, "Access is denied") + return _winreg.OpenKey(key, name) + def __getattr__(self, name): + return getattr(_winreg, name) + + mimetypes._winreg = MockWinreg() + try: + mimetypes.init() + finally: + mimetypes._winreg = _winreg + def test_main(): test_support.run_unittest(MimeTypesTestCase, Win32MimeTypesTestCase -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Sun Jun 29 23:56:40 2014 From: python-checkins at python.org (alexander.belopolsky) Date: Sun, 29 Jun 2014 23:56:40 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fixes_=2310541=3A_regrtest?= =?utf-8?q?_-T_is_broken?= Message-ID: <3h1lzD1d9Hz7LjN@mail.python.org> http://hg.python.org/cpython/rev/10cf594ace4b changeset: 91467:10cf594ace4b parent: 91464:394e6bda5a70 user: Alexander Belopolsky date: Sun Jun 29 17:44:05 2014 -0400 summary: Fixes #10541: regrtest -T is broken * makes test_trace tests restore the tracefunc after they run * write_results() in trace module will not terminate if lnotab cannot be found. files: Lib/test/test_trace.py | 6 +++++- Lib/trace.py | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -10,7 +10,6 @@ from test.tracedmodules import testmod - #------------------------------- Utilities -----------------------------------# def fix_ext_py(filename): @@ -224,6 +223,11 @@ self.addCleanup(sys.settrace, sys.gettrace()) self.tracer = Trace(count=0, trace=0, countfuncs=1) self.filemod = my_file_and_modname() + self._saved_tracefunc = sys.gettrace() + + def tearDown(self): + if self._saved_tracefunc is not None: + sys.settrace(self._saved_tracefunc) def test_simple_caller(self): self.tracer.runfunc(traced_func_simple_caller, 1) diff --git a/Lib/trace.py b/Lib/trace.py --- a/Lib/trace.py +++ b/Lib/trace.py @@ -326,16 +326,17 @@ lnotab = _find_executable_linenos(filename) else: lnotab = {} + if lnotab: + source = linecache.getlines(filename) + coverpath = os.path.join(dir, modulename + ".cover") + with open(filename, 'rb') as fp: + encoding, _ = tokenize.detect_encoding(fp.readline) + n_hits, n_lines = self.write_results_file(coverpath, source, + lnotab, count, encoding) + if summary and n_lines: + percent = int(100 * n_hits / n_lines) + sums[modulename] = n_lines, percent, modulename, filename - source = linecache.getlines(filename) - coverpath = os.path.join(dir, modulename + ".cover") - with open(filename, 'rb') as fp: - encoding, _ = tokenize.detect_encoding(fp.readline) - n_hits, n_lines = self.write_results_file(coverpath, source, - lnotab, count, encoding) - if summary and n_lines: - percent = int(100 * n_hits / n_lines) - sums[modulename] = n_lines, percent, modulename, filename if summary and sums: print("lines cov% module (path)") -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 00:17:00 2014 From: python-checkins at python.org (stefan.krah) Date: Mon, 30 Jun 2014 00:17:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNzc4?= =?utf-8?q?=3A__Clarify_use_of_flags_if_PyBuffer=5FFillInfo=28=29_is_used_?= =?utf-8?q?inside_a?= Message-ID: <3h1mQh4Kccz7LjP@mail.python.org> http://hg.python.org/cpython/rev/49cdb04bfcc6 changeset: 91468:49cdb04bfcc6 branch: 3.4 parent: 91463:0e08ca451b34 user: Stefan Krah date: Mon Jun 30 00:15:45 2014 +0200 summary: Issue #21778: Clarify use of flags if PyBuffer_FillInfo() is used inside a getbufferproc(). files: Doc/c-api/buffer.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -489,8 +489,8 @@ :c:member:`view->obj` to *NULL* and return -1; If this function is used as part of a :ref:`getbufferproc `, - *exporter* MUST be set to the exporting object. Otherwise, *exporter* MUST - be NULL. + *exporter* MUST be set to the exporting object and *flags* must be passed + unmodified. Otherwise, *exporter* MUST be NULL. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 00:17:01 2014 From: python-checkins at python.org (stefan.krah) Date: Mon, 30 Jun 2014 00:17:01 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogTWVyZ2UgMy40Lg==?= Message-ID: <3h1mQj5Zdmz7LjV@mail.python.org> http://hg.python.org/cpython/rev/04562803d346 changeset: 91469:04562803d346 parent: 91467:10cf594ace4b parent: 91468:49cdb04bfcc6 user: Stefan Krah date: Mon Jun 30 00:16:09 2014 +0200 summary: Merge 3.4. files: Doc/c-api/buffer.rst | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -489,8 +489,8 @@ :c:member:`view->obj` to *NULL* and return -1; If this function is used as part of a :ref:`getbufferproc `, - *exporter* MUST be set to the exporting object. Otherwise, *exporter* MUST - be NULL. + *exporter* MUST be set to the exporting object and *flags* must be passed + unmodified. Otherwise, *exporter* MUST be NULL. -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 02:12:18 2014 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 30 Jun 2014 02:12:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321679=3A_Prevent_?= =?utf-8?q?extraneous_fstat=28=29_calls_during_open=28=29=2E__Patch_by?= Message-ID: <3h1pzk4Z1cz7LjP@mail.python.org> http://hg.python.org/cpython/rev/3b5279b5bfd1 changeset: 91470:3b5279b5bfd1 user: Antoine Pitrou date: Sun Jun 29 20:07:28 2014 -0400 summary: Issue #21679: Prevent extraneous fstat() calls during open(). Patch by Bohuslav Kabrda. files: Lib/test/test_fileio.py | 11 ++++++- Misc/NEWS | 3 + Modules/_io/_iomodule.c | 28 +++++----------- Modules/_io/fileio.c | 46 +++++++++++++++------------- 4 files changed, 47 insertions(+), 41 deletions(-) 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 @@ -60,6 +60,15 @@ self.assertRaises((AttributeError, TypeError), setattr, f, attr, 'oops') + def testBlksize(self): + # test private _blksize attribute + blksize = io.DEFAULT_BUFFER_SIZE + # try to get preferred blksize from stat.st_blksize, if available + if hasattr(os, 'fstat'): + fst = os.fstat(self.f.fileno()) + blksize = getattr(fst, 'st_blksize', blksize) + self.assertEqual(self.f._blksize, blksize) + def testReadinto(self): # verify readinto self.f.write(bytes([1, 2])) @@ -141,7 +150,7 @@ def testOpendir(self): # Issue 3703: opening a directory should fill the errno # Windows always returns "[Errno 13]: Permission denied - # Unix calls dircheck() and returns "[Errno 21]: Is a directory" + # Unix uses fstat and returns "[Errno 21]: Is a directory" try: _FileIO('.', 'r') except OSError as e: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,9 @@ Library ------- +- Issue #21679: Prevent extraneous fstat() calls during open(). Patch by + Bohuslav Kabrda. + - Issue #21863: cProfile now displays the module name of C extension functions, in addition to their own name. diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -237,8 +237,8 @@ PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL; + _Py_IDENTIFIER(_blksize); _Py_IDENTIFIER(isatty); - _Py_IDENTIFIER(fileno); _Py_IDENTIFIER(mode); _Py_IDENTIFIER(close); @@ -380,24 +380,14 @@ line_buffering = 0; if (buffering < 0) { - buffering = DEFAULT_BUFFER_SIZE; -#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE - { - struct stat st; - long fileno; - PyObject *res = _PyObject_CallMethodId(raw, &PyId_fileno, NULL); - if (res == NULL) - goto error; - - fileno = PyLong_AsLong(res); - Py_DECREF(res); - if (fileno == -1 && PyErr_Occurred()) - goto error; - - if (fstat(fileno, &st) >= 0 && st.st_blksize > 1) - buffering = st.st_blksize; - } -#endif + PyObject *blksize_obj; + blksize_obj = _PyObject_GetAttrId(raw, &PyId__blksize); + if (blksize_obj == NULL) + goto error; + buffering = PyLong_AsLong(blksize_obj); + Py_DECREF(blksize_obj); + if (buffering == -1 && PyErr_Occurred()) + goto error; } if (buffering < 0) { PyErr_SetString(PyExc_ValueError, diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -53,6 +53,7 @@ signed int seekable : 2; /* -1 means unknown */ unsigned int closefd : 1; char finalizing; + unsigned int blksize; PyObject *weakreflist; PyObject *dict; } fileio; @@ -161,6 +162,7 @@ self->writable = 0; self->appending = 0; self->seekable = -1; + self->blksize = 0; self->closefd = 1; self->weakreflist = NULL; } @@ -168,26 +170,6 @@ return (PyObject *) self; } -/* On Unix, open will succeed for directories. - In Python, there should be no file objects referring to - directories, so we need a check. */ - -static int -dircheck(fileio* self, PyObject *nameobj) -{ -#if defined(HAVE_FSTAT) && defined(S_ISDIR) && defined(EISDIR) - struct stat buf; - if (self->fd < 0) - return 0; - if (fstat(self->fd, &buf) == 0 && S_ISDIR(buf.st_mode)) { - errno = EISDIR; - PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj); - return -1; - } -#endif - return 0; -} - static int check_fd(int fd) { @@ -233,6 +215,9 @@ #elif !defined(MS_WINDOWS) int *atomic_flag_works = NULL; #endif +#ifdef HAVE_FSTAT + struct stat fdfstat; +#endif assert(PyFileIO_Check(oself)); if (self->fd >= 0) { @@ -421,8 +406,26 @@ goto error; #endif } - if (dircheck(self, nameobj) < 0) + + self->blksize = DEFAULT_BUFFER_SIZE; +#ifdef HAVE_FSTAT + if (fstat(self->fd, &fdfstat) < 0) goto error; +#if defined(S_ISDIR) && defined(EISDIR) + /* On Unix, open will succeed for directories. + In Python, there should be no file objects referring to + directories, so we need a check. */ + if (S_ISDIR(fdfstat.st_mode)) { + errno = EISDIR; + PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj); + goto error; + } +#endif /* defined(S_ISDIR) */ +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + if (fdfstat.st_blksize > 1) + self->blksize = fdfstat.st_blksize; +#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ +#endif /* HAVE_FSTAT */ #if defined(MS_WINDOWS) || defined(__CYGWIN__) /* don't translate newlines (\r\n <=> \n) */ @@ -1216,6 +1219,7 @@ }; static PyMemberDef fileio_members[] = { + {"_blksize", T_UINT, offsetof(fileio, blksize), 0}, {"_finalizing", T_BOOL, offsetof(fileio, finalizing), 0}, {NULL} }; -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 03:05:52 2014 From: python-checkins at python.org (berker.peksag) Date: Mon, 30 Jun 2014 03:05:52 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE0MjM1?= =?utf-8?q?=3A_Use_importlib=2Ereload=28=29_in_test=5Fcmd=2Etest=5Fcoverag?= =?utf-8?q?e=2E?= Message-ID: <3h1r9X42lJz7LjS@mail.python.org> http://hg.python.org/cpython/rev/d943089af1c6 changeset: 91471:d943089af1c6 branch: 3.4 parent: 91468:49cdb04bfcc6 user: Berker Peksag date: Mon Jun 30 04:04:52 2014 +0300 summary: Issue #14235: Use importlib.reload() in test_cmd.test_coverage. files: Lib/test/test_cmd.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py --- a/Lib/test/test_cmd.py +++ b/Lib/test/test_cmd.py @@ -229,7 +229,7 @@ trace = support.import_module('trace') tracer=trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,], trace=0, count=1) - tracer.run('reload(cmd);test_main()') + tracer.run('import importlib; importlib.reload(cmd); test_main()') r=tracer.results() print("Writing coverage results...") r.write_results(show_missing=True, summary=True, coverdir=coverdir) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 03:05:53 2014 From: python-checkins at python.org (berker.peksag) Date: Mon, 30 Jun 2014 03:05:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2314235=3A_Merge_from_3=2E4=2E?= Message-ID: <3h1r9Y5M0lz7LkJ@mail.python.org> http://hg.python.org/cpython/rev/10a1e7780ee7 changeset: 91472:10a1e7780ee7 parent: 91470:3b5279b5bfd1 parent: 91471:d943089af1c6 user: Berker Peksag date: Mon Jun 30 04:05:54 2014 +0300 summary: Issue #14235: Merge from 3.4. files: Lib/test/test_cmd.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py --- a/Lib/test/test_cmd.py +++ b/Lib/test/test_cmd.py @@ -229,7 +229,7 @@ trace = support.import_module('trace') tracer=trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,], trace=0, count=1) - tracer.run('reload(cmd);test_main()') + tracer.run('import importlib; importlib.reload(cmd); test_main()') r=tracer.results() print("Writing coverage results...") r.write_results(show_missing=True, summary=True, coverdir=coverdir) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 03:57:22 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 30 Jun 2014 03:57:22 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_update_pydoc-t?= =?utf-8?q?opics?= Message-ID: <3h1sJy51CYz7Ljy@mail.python.org> http://hg.python.org/cpython/rev/30eefd7e62a6 changeset: 91473:30eefd7e62a6 branch: 2.7 parent: 91466:ee33d61f5e4b user: Benjamin Peterson date: Sun Jun 29 18:57:11 2014 -0700 summary: update pydoc-topics files: Lib/pydoc_data/topics.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,4 +1,4 @@ -# Autogenerated by Sphinx on Sat May 17 17:29:52 2014 +# Autogenerated by Sphinx on Sun Jun 29 18:55:25 2014 topics = {'assert': '\nThe ``assert`` statement\n************************\n\nAssert statements are a convenient way to insert debugging assertions\ninto a program:\n\n assert_stmt ::= "assert" expression ["," expression]\n\nThe simple form, ``assert expression``, is equivalent to\n\n if __debug__:\n if not expression: raise AssertionError\n\nThe extended form, ``assert expression1, expression2``, is equivalent\nto\n\n if __debug__:\n if not expression1: raise AssertionError(expression2)\n\nThese equivalences assume that ``__debug__`` and ``AssertionError``\nrefer to the built-in variables with those names. In the current\nimplementation, the built-in variable ``__debug__`` is ``True`` under\nnormal circumstances, ``False`` when optimization is requested\n(command line option -O). The current code generator emits no code\nfor an assert statement when optimization is requested at compile\ntime. Note that it is unnecessary to include the source code for the\nexpression that failed in the error message; it will be displayed as\npart of the stack trace.\n\nAssignments to ``__debug__`` are illegal. The value for the built-in\nvariable is determined when the interpreter starts.\n', 'assignment': '\nAssignment statements\n*********************\n\nAssignment statements are used to (re)bind names to values and to\nmodify attributes or items of mutable objects:\n\n assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression)\n target_list ::= target ("," target)* [","]\n target ::= identifier\n | "(" target_list ")"\n | "[" target_list "]"\n | attributeref\n | subscription\n | slicing\n\n(See section *Primaries* for the syntax definitions for the last three\nsymbols.)\n\nAn assignment statement evaluates the expression list (remember that\nthis can be a single expression or a comma-separated list, the latter\nyielding a tuple) and assigns the single resulting object to each of\nthe target lists, from left to right.\n\nAssignment is defined recursively depending on the form of the target\n(list). When a target is part of a mutable object (an attribute\nreference, subscription or slicing), the mutable object must\nultimately perform the assignment and decide about its validity, and\nmay raise an exception if the assignment is unacceptable. The rules\nobserved by various types and the exceptions raised are given with the\ndefinition of the object types (see section *The standard type\nhierarchy*).\n\nAssignment of an object to a target list is recursively defined as\nfollows.\n\n* If the target list is a single target: The object is assigned to\n that target.\n\n* If the target list is a comma-separated list of targets: The object\n must be an iterable with the same number of items as there are\n targets in the target list, and the items are assigned, from left to\n right, to the corresponding targets.\n\nAssignment of an object to a single target is recursively defined as\nfollows.\n\n* If the target is an identifier (name):\n\n * If the name does not occur in a ``global`` statement in the\n current code block: the name is bound to the object in the current\n local namespace.\n\n * Otherwise: the name is bound to the object in the current global\n namespace.\n\n The name is rebound if it was already bound. This may cause the\n reference count for the object previously bound to the name to reach\n zero, causing the object to be deallocated and its destructor (if it\n has one) to be called.\n\n* If the target is a target list enclosed in parentheses or in square\n brackets: The object must be an iterable with the same number of\n items as there are targets in the target list, and its items are\n assigned, from left to right, to the corresponding targets.\n\n* If the target is an attribute reference: The primary expression in\n the reference is evaluated. It should yield an object with\n assignable attributes; if this is not the case, ``TypeError`` is\n raised. That object is then asked to assign the assigned object to\n the given attribute; if it cannot perform the assignment, it raises\n an exception (usually but not necessarily ``AttributeError``).\n\n Note: If the object is a class instance and the attribute reference\n occurs on both sides of the assignment operator, the RHS expression,\n ``a.x`` can access either an instance attribute or (if no instance\n attribute exists) a class attribute. The LHS target ``a.x`` is\n always set as an instance attribute, creating it if necessary.\n Thus, the two occurrences of ``a.x`` do not necessarily refer to the\n same attribute: if the RHS expression refers to a class attribute,\n the LHS creates a new instance attribute as the target of the\n assignment:\n\n class Cls:\n x = 3 # class variable\n inst = Cls()\n inst.x = inst.x + 1 # writes inst.x as 4 leaving Cls.x as 3\n\n This description does not necessarily apply to descriptor\n attributes, such as properties created with ``property()``.\n\n* If the target is a subscription: The primary expression in the\n reference is evaluated. It should yield either a mutable sequence\n object (such as a list) or a mapping object (such as a dictionary).\n Next, the subscript expression is evaluated.\n\n If the primary is a mutable sequence object (such as a list), the\n subscript must yield a plain integer. If it is negative, the\n sequence\'s length is added to it. The resulting value must be a\n nonnegative integer less than the sequence\'s length, and the\n sequence is asked to assign the assigned object to its item with\n that index. If the index is out of range, ``IndexError`` is raised\n (assignment to a subscripted sequence cannot add new items to a\n list).\n\n If the primary is a mapping object (such as a dictionary), the\n subscript must have a type compatible with the mapping\'s key type,\n and the mapping is then asked to create a key/datum pair which maps\n the subscript to the assigned object. This can either replace an\n existing key/value pair with the same key value, or insert a new\n key/value pair (if no key with the same value existed).\n\n* If the target is a slicing: The primary expression in the reference\n is evaluated. It should yield a mutable sequence object (such as a\n list). The assigned object should be a sequence object of the same\n type. Next, the lower and upper bound expressions are evaluated,\n insofar they are present; defaults are zero and the sequence\'s\n length. The bounds should evaluate to (small) integers. If either\n bound is negative, the sequence\'s length is added to it. The\n resulting bounds are clipped to lie between zero and the sequence\'s\n length, inclusive. Finally, the sequence object is asked to replace\n the slice with the items of the assigned sequence. The length of\n the slice may be different from the length of the assigned sequence,\n thus changing the length of the target sequence, if the object\n allows it.\n\n**CPython implementation detail:** In the current implementation, the\nsyntax for targets is taken to be the same as for expressions, and\ninvalid syntax is rejected during the code generation phase, causing\nless detailed error messages.\n\nWARNING: Although the definition of assignment implies that overlaps\nbetween the left-hand side and the right-hand side are \'safe\' (for\nexample ``a, b = b, a`` swaps two variables), overlaps *within* the\ncollection of assigned-to variables are not safe! For instance, the\nfollowing program prints ``[0, 2]``:\n\n x = [0, 1]\n i = 0\n i, x[i] = 1, 2\n print x\n\n\nAugmented assignment statements\n===============================\n\nAugmented assignment is the combination, in a single statement, of a\nbinary operation and an assignment statement:\n\n augmented_assignment_stmt ::= augtarget augop (expression_list | yield_expression)\n augtarget ::= identifier | attributeref | subscription | slicing\n augop ::= "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**="\n | ">>=" | "<<=" | "&=" | "^=" | "|="\n\n(See section *Primaries* for the syntax definitions for the last three\nsymbols.)\n\nAn augmented assignment evaluates the target (which, unlike normal\nassignment statements, cannot be an unpacking) and the expression\nlist, performs the binary operation specific to the type of assignment\non the two operands, and assigns the result to the original target.\nThe target is only evaluated once.\n\nAn augmented assignment expression like ``x += 1`` can be rewritten as\n``x = x + 1`` to achieve a similar, but not exactly equal effect. In\nthe augmented version, ``x`` is only evaluated once. Also, when\npossible, the actual operation is performed *in-place*, meaning that\nrather than creating a new object and assigning that to the target,\nthe old object is modified instead.\n\nWith the exception of assigning to tuples and multiple targets in a\nsingle statement, the assignment done by augmented assignment\nstatements is handled the same way as normal assignments. Similarly,\nwith the exception of the possible *in-place* behavior, the binary\noperation performed by augmented assignment is the same as the normal\nbinary operations.\n\nFor targets which are attribute references, the same *caveat about\nclass and instance attributes* applies as for regular assignments.\n', 'atom-identifiers': '\nIdentifiers (Names)\n*******************\n\nAn identifier occurring as an atom is a name. See section\n*Identifiers and keywords* for lexical definition and section *Naming\nand binding* for documentation of naming and binding.\n\nWhen the name is bound to an object, evaluation of the atom yields\nthat object. When a name is not bound, an attempt to evaluate it\nraises a ``NameError`` exception.\n\n**Private name mangling:** When an identifier that textually occurs in\na class definition begins with two or more underscore characters and\ndoes not end in two or more underscores, it is considered a *private\nname* of that class. Private names are transformed to a longer form\nbefore code is generated for them. The transformation inserts the\nclass name, with leading underscores removed and a single underscore\ninserted, in front of the name. For example, the identifier\n``__spam`` occurring in a class named ``Ham`` will be transformed to\n``_Ham__spam``. This transformation is independent of the syntactical\ncontext in which the identifier is used. If the transformed name is\nextremely long (longer than 255 characters), implementation defined\ntruncation may happen. If the class name consists only of underscores,\nno transformation is done.\n', @@ -68,7 +68,7 @@ 'try': '\nThe ``try`` statement\n*********************\n\nThe ``try`` statement specifies exception handlers and/or cleanup code\nfor a group of statements:\n\n try_stmt ::= try1_stmt | try2_stmt\n try1_stmt ::= "try" ":" suite\n ("except" [expression [("as" | ",") target]] ":" suite)+\n ["else" ":" suite]\n ["finally" ":" suite]\n try2_stmt ::= "try" ":" suite\n "finally" ":" suite\n\nChanged in version 2.5: In previous versions of Python,\n``try``...``except``...``finally`` did not work. ``try``...``except``\nhad to be nested in ``try``...``finally``.\n\nThe ``except`` clause(s) specify one or more exception handlers. When\nno exception occurs in the ``try`` clause, no exception handler is\nexecuted. When an exception occurs in the ``try`` suite, a search for\nan exception handler is started. This search inspects the except\nclauses in turn until one is found that matches the exception. An\nexpression-less except clause, if present, must be last; it matches\nany exception. For an except clause with an expression, that\nexpression is evaluated, and the clause matches the exception if the\nresulting object is "compatible" with the exception. An object is\ncompatible with an exception if it is the class or a base class of the\nexception object, or a tuple containing an item compatible with the\nexception.\n\nIf no except clause matches the exception, the search for an exception\nhandler continues in the surrounding code and on the invocation stack.\n[1]\n\nIf the evaluation of an expression in the header of an except clause\nraises an exception, the original search for a handler is canceled and\na search starts for the new exception in the surrounding code and on\nthe call stack (it is treated as if the entire ``try`` statement\nraised the exception).\n\nWhen a matching except clause is found, the exception is assigned to\nthe target specified in that except clause, if present, and the except\nclause\'s suite is executed. All except clauses must have an\nexecutable block. When the end of this block is reached, execution\ncontinues normally after the entire try statement. (This means that\nif two nested handlers exist for the same exception, and the exception\noccurs in the try clause of the inner handler, the outer handler will\nnot handle the exception.)\n\nBefore an except clause\'s suite is executed, details about the\nexception are assigned to three variables in the ``sys`` module:\n``sys.exc_type`` receives the object identifying the exception;\n``sys.exc_value`` receives the exception\'s parameter;\n``sys.exc_traceback`` receives a traceback object (see section *The\nstandard type hierarchy*) identifying the point in the program where\nthe exception occurred. These details are also available through the\n``sys.exc_info()`` function, which returns a tuple ``(exc_type,\nexc_value, exc_traceback)``. Use of the corresponding variables is\ndeprecated in favor of this function, since their use is unsafe in a\nthreaded program. As of Python 1.5, the variables are restored to\ntheir previous values (before the call) when returning from a function\nthat handled an exception.\n\nThe optional ``else`` clause is executed if and when control flows off\nthe end of the ``try`` clause. [2] Exceptions in the ``else`` clause\nare not handled by the preceding ``except`` clauses.\n\nIf ``finally`` is present, it specifies a \'cleanup\' handler. The\n``try`` clause is executed, including any ``except`` and ``else``\nclauses. If an exception occurs in any of the clauses and is not\nhandled, the exception is temporarily saved. The ``finally`` clause is\nexecuted. If there is a saved exception, it is re-raised at the end\nof the ``finally`` clause. If the ``finally`` clause raises another\nexception or executes a ``return`` or ``break`` statement, the saved\nexception is discarded:\n\n >>> def f():\n ... try:\n ... 1/0\n ... finally:\n ... return 42\n ...\n >>> f()\n 42\n\nThe exception information is not available to the program during\nexecution of the ``finally`` clause.\n\nWhen a ``return``, ``break`` or ``continue`` statement is executed in\nthe ``try`` suite of a ``try``...``finally`` statement, the\n``finally`` clause is also executed \'on the way out.\' A ``continue``\nstatement is illegal in the ``finally`` clause. (The reason is a\nproblem with the current implementation --- this restriction may be\nlifted in the future).\n\nThe return value of a function is determined by the last ``return``\nstatement executed. Since the ``finally`` clause always executes, a\n``return`` statement executed in the ``finally`` clause will always be\nthe last one executed:\n\n >>> def foo():\n ... try:\n ... return \'try\'\n ... finally:\n ... return \'finally\'\n ...\n >>> foo()\n \'finally\'\n\nAdditional information on exceptions can be found in section\n*Exceptions*, and information on using the ``raise`` statement to\ngenerate exceptions may be found in section *The raise statement*.\n', 'types': '\nThe standard type hierarchy\n***************************\n\nBelow is a list of the types that are built into Python. Extension\nmodules (written in C, Java, or other languages, depending on the\nimplementation) can define additional types. Future versions of\nPython may add types to the type hierarchy (e.g., rational numbers,\nefficiently stored arrays of integers, etc.).\n\nSome of the type descriptions below contain a paragraph listing\n\'special attributes.\' These are attributes that provide access to the\nimplementation and are not intended for general use. Their definition\nmay change in the future.\n\nNone\n This type has a single value. There is a single object with this\n value. This object is accessed through the built-in name ``None``.\n It is used to signify the absence of a value in many situations,\n e.g., it is returned from functions that don\'t explicitly return\n anything. Its truth value is false.\n\nNotImplemented\n This type has a single value. There is a single object with this\n value. This object is accessed through the built-in name\n ``NotImplemented``. Numeric methods and rich comparison methods may\n return this value if they do not implement the operation for the\n operands provided. (The interpreter will then try the reflected\n operation, or some other fallback, depending on the operator.) Its\n truth value is true.\n\nEllipsis\n This type has a single value. There is a single object with this\n value. This object is accessed through the built-in name\n ``Ellipsis``. It is used to indicate the presence of the ``...``\n syntax in a slice. Its truth value is true.\n\n``numbers.Number``\n These are created by numeric literals and returned as results by\n arithmetic operators and arithmetic built-in functions. Numeric\n objects are immutable; once created their value never changes.\n Python numbers are of course strongly related to mathematical\n numbers, but subject to the limitations of numerical representation\n in computers.\n\n Python distinguishes between integers, floating point numbers, and\n complex numbers:\n\n ``numbers.Integral``\n These represent elements from the mathematical set of integers\n (positive and negative).\n\n There are three types of integers:\n\n Plain integers\n These represent numbers in the range -2147483648 through\n 2147483647. (The range may be larger on machines with a\n larger natural word size, but not smaller.) When the result\n of an operation would fall outside this range, the result is\n normally returned as a long integer (in some cases, the\n exception ``OverflowError`` is raised instead). For the\n purpose of shift and mask operations, integers are assumed to\n have a binary, 2\'s complement notation using 32 or more bits,\n and hiding no bits from the user (i.e., all 4294967296\n different bit patterns correspond to different values).\n\n Long integers\n These represent numbers in an unlimited range, subject to\n available (virtual) memory only. For the purpose of shift\n and mask operations, a binary representation is assumed, and\n negative numbers are represented in a variant of 2\'s\n complement which gives the illusion of an infinite string of\n sign bits extending to the left.\n\n Booleans\n These represent the truth values False and True. The two\n objects representing the values ``False`` and ``True`` are\n the only Boolean objects. The Boolean type is a subtype of\n plain integers, and Boolean values behave like the values 0\n and 1, respectively, in almost all contexts, the exception\n being that when converted to a string, the strings\n ``"False"`` or ``"True"`` are returned, respectively.\n\n The rules for integer representation are intended to give the\n most meaningful interpretation of shift and mask operations\n involving negative integers and the least surprises when\n switching between the plain and long integer domains. Any\n operation, if it yields a result in the plain integer domain,\n will yield the same result in the long integer domain or when\n using mixed operands. The switch between domains is transparent\n to the programmer.\n\n ``numbers.Real`` (``float``)\n These represent machine-level double precision floating point\n numbers. You are at the mercy of the underlying machine\n architecture (and C or Java implementation) for the accepted\n range and handling of overflow. Python does not support single-\n precision floating point numbers; the savings in processor and\n memory usage that are usually the reason for using these is\n dwarfed by the overhead of using objects in Python, so there is\n no reason to complicate the language with two kinds of floating\n point numbers.\n\n ``numbers.Complex``\n These represent complex numbers as a pair of machine-level\n double precision floating point numbers. The same caveats apply\n as for floating point numbers. The real and imaginary parts of a\n complex number ``z`` can be retrieved through the read-only\n attributes ``z.real`` and ``z.imag``.\n\nSequences\n These represent finite ordered sets indexed by non-negative\n numbers. The built-in function ``len()`` returns the number of\n items of a sequence. When the length of a sequence is *n*, the\n index set contains the numbers 0, 1, ..., *n*-1. Item *i* of\n sequence *a* is selected by ``a[i]``.\n\n Sequences also support slicing: ``a[i:j]`` selects all items with\n index *k* such that *i* ``<=`` *k* ``<`` *j*. When used as an\n expression, a slice is a sequence of the same type. This implies\n that the index set is renumbered so that it starts at 0.\n\n Some sequences also support "extended slicing" with a third "step"\n parameter: ``a[i:j:k]`` selects all items of *a* with index *x*\n where ``x = i + n*k``, *n* ``>=`` ``0`` and *i* ``<=`` *x* ``<``\n *j*.\n\n Sequences are distinguished according to their mutability:\n\n Immutable sequences\n An object of an immutable sequence type cannot change once it is\n created. (If the object contains references to other objects,\n these other objects may be mutable and may be changed; however,\n the collection of objects directly referenced by an immutable\n object cannot change.)\n\n The following types are immutable sequences:\n\n Strings\n The items of a string are characters. There is no separate\n character type; a character is represented by a string of one\n item. Characters represent (at least) 8-bit bytes. The\n built-in functions ``chr()`` and ``ord()`` convert between\n characters and nonnegative integers representing the byte\n values. Bytes with the values 0-127 usually represent the\n corresponding ASCII values, but the interpretation of values\n is up to the program. The string data type is also used to\n represent arrays of bytes, e.g., to hold data read from a\n file.\n\n (On systems whose native character set is not ASCII, strings\n may use EBCDIC in their internal representation, provided the\n functions ``chr()`` and ``ord()`` implement a mapping between\n ASCII and EBCDIC, and string comparison preserves the ASCII\n order. Or perhaps someone can propose a better rule?)\n\n Unicode\n The items of a Unicode object are Unicode code units. A\n Unicode code unit is represented by a Unicode object of one\n item and can hold either a 16-bit or 32-bit value\n representing a Unicode ordinal (the maximum value for the\n ordinal is given in ``sys.maxunicode``, and depends on how\n Python is configured at compile time). Surrogate pairs may\n be present in the Unicode object, and will be reported as two\n separate items. The built-in functions ``unichr()`` and\n ``ord()`` convert between code units and nonnegative integers\n representing the Unicode ordinals as defined in the Unicode\n Standard 3.0. Conversion from and to other encodings are\n possible through the Unicode method ``encode()`` and the\n built-in function ``unicode()``.\n\n Tuples\n The items of a tuple are arbitrary Python objects. Tuples of\n two or more items are formed by comma-separated lists of\n expressions. A tuple of one item (a \'singleton\') can be\n formed by affixing a comma to an expression (an expression by\n itself does not create a tuple, since parentheses must be\n usable for grouping of expressions). An empty tuple can be\n formed by an empty pair of parentheses.\n\n Mutable sequences\n Mutable sequences can be changed after they are created. The\n subscription and slicing notations can be used as the target of\n assignment and ``del`` (delete) statements.\n\n There are currently two intrinsic mutable sequence types:\n\n Lists\n The items of a list are arbitrary Python objects. Lists are\n formed by placing a comma-separated list of expressions in\n square brackets. (Note that there are no special cases needed\n to form lists of length 0 or 1.)\n\n Byte Arrays\n A bytearray object is a mutable array. They are created by\n the built-in ``bytearray()`` constructor. Aside from being\n mutable (and hence unhashable), byte arrays otherwise provide\n the same interface and functionality as immutable bytes\n objects.\n\n The extension module ``array`` provides an additional example of\n a mutable sequence type.\n\nSet types\n These represent unordered, finite sets of unique, immutable\n objects. As such, they cannot be indexed by any subscript. However,\n they can be iterated over, and the built-in function ``len()``\n returns the number of items in a set. Common uses for sets are fast\n membership testing, removing duplicates from a sequence, and\n computing mathematical operations such as intersection, union,\n difference, and symmetric difference.\n\n For set elements, the same immutability rules apply as for\n dictionary keys. Note that numeric types obey the normal rules for\n numeric comparison: if two numbers compare equal (e.g., ``1`` and\n ``1.0``), only one of them can be contained in a set.\n\n There are currently two intrinsic set types:\n\n Sets\n These represent a mutable set. They are created by the built-in\n ``set()`` constructor and can be modified afterwards by several\n methods, such as ``add()``.\n\n Frozen sets\n These represent an immutable set. They are created by the\n built-in ``frozenset()`` constructor. As a frozenset is\n immutable and *hashable*, it can be used again as an element of\n another set, or as a dictionary key.\n\nMappings\n These represent finite sets of objects indexed by arbitrary index\n sets. The subscript notation ``a[k]`` selects the item indexed by\n ``k`` from the mapping ``a``; this can be used in expressions and\n as the target of assignments or ``del`` statements. The built-in\n function ``len()`` returns the number of items in a mapping.\n\n There is currently a single intrinsic mapping type:\n\n Dictionaries\n These represent finite sets of objects indexed by nearly\n arbitrary values. The only types of values not acceptable as\n keys are values containing lists or dictionaries or other\n mutable types that are compared by value rather than by object\n identity, the reason being that the efficient implementation of\n dictionaries requires a key\'s hash value to remain constant.\n Numeric types used for keys obey the normal rules for numeric\n comparison: if two numbers compare equal (e.g., ``1`` and\n ``1.0``) then they can be used interchangeably to index the same\n dictionary entry.\n\n Dictionaries are mutable; they can be created by the ``{...}``\n notation (see section *Dictionary displays*).\n\n The extension modules ``dbm``, ``gdbm``, and ``bsddb`` provide\n additional examples of mapping types.\n\nCallable types\n These are the types to which the function call operation (see\n section *Calls*) can be applied:\n\n User-defined functions\n A user-defined function object is created by a function\n definition (see section *Function definitions*). It should be\n called with an argument list containing the same number of items\n as the function\'s formal parameter list.\n\n Special attributes:\n\n +-------------------------+---------------------------------+-------------+\n | Attribute | Meaning | |\n +=========================+=================================+=============+\n | ``__doc__`` | The function\'s documentation | Writable |\n | ``func_doc`` | string, or ``None`` if | |\n | | unavailable. | |\n +-------------------------+---------------------------------+-------------+\n | ``__name__`` | The function\'s name. | Writable |\n | ``func_name`` | | |\n +-------------------------+---------------------------------+-------------+\n | ``__module__`` | The name of the module the | Writable |\n | | function was defined in, or | |\n | | ``None`` if unavailable. | |\n +-------------------------+---------------------------------+-------------+\n | ``__defaults__`` | A tuple containing default | Writable |\n | ``func_defaults`` | argument values for those | |\n | | arguments that have defaults, | |\n | | or ``None`` if no arguments | |\n | | have a default value. | |\n +-------------------------+---------------------------------+-------------+\n | ``__code__`` | The code object representing | Writable |\n | ``func_code`` | the compiled function body. | |\n +-------------------------+---------------------------------+-------------+\n | ``__globals__`` | A reference to the dictionary | Read-only |\n | ``func_globals`` | that holds the function\'s | |\n | | global variables --- the global | |\n | | namespace of the module in | |\n | | which the function was defined. | |\n +-------------------------+---------------------------------+-------------+\n | ``__dict__`` | The namespace supporting | Writable |\n | ``func_dict`` | arbitrary function attributes. | |\n +-------------------------+---------------------------------+-------------+\n | ``__closure__`` | ``None`` or a tuple of cells | Read-only |\n | ``func_closure`` | that contain bindings for the | |\n | | function\'s free variables. | |\n +-------------------------+---------------------------------+-------------+\n\n Most of the attributes labelled "Writable" check the type of the\n assigned value.\n\n Changed in version 2.4: ``func_name`` is now writable.\n\n Changed in version 2.6: The double-underscore attributes\n ``__closure__``, ``__code__``, ``__defaults__``, and\n ``__globals__`` were introduced as aliases for the corresponding\n ``func_*`` attributes for forwards compatibility with Python 3.\n\n Function objects also support getting and setting arbitrary\n attributes, which can be used, for example, to attach metadata\n to functions. Regular attribute dot-notation is used to get and\n set such attributes. *Note that the current implementation only\n supports function attributes on user-defined functions. Function\n attributes on built-in functions may be supported in the\n future.*\n\n Additional information about a function\'s definition can be\n retrieved from its code object; see the description of internal\n types below.\n\n User-defined methods\n A user-defined method object combines a class, a class instance\n (or ``None``) and any callable object (normally a user-defined\n function).\n\n Special read-only attributes: ``im_self`` is the class instance\n object, ``im_func`` is the function object; ``im_class`` is the\n class of ``im_self`` for bound methods or the class that asked\n for the method for unbound methods; ``__doc__`` is the method\'s\n documentation (same as ``im_func.__doc__``); ``__name__`` is the\n method name (same as ``im_func.__name__``); ``__module__`` is\n the name of the module the method was defined in, or ``None`` if\n unavailable.\n\n Changed in version 2.2: ``im_self`` used to refer to the class\n that defined the method.\n\n Changed in version 2.6: For Python 3 forward-compatibility,\n ``im_func`` is also available as ``__func__``, and ``im_self``\n as ``__self__``.\n\n Methods also support accessing (but not setting) the arbitrary\n function attributes on the underlying function object.\n\n User-defined method objects may be created when getting an\n attribute of a class (perhaps via an instance of that class), if\n that attribute is a user-defined function object, an unbound\n user-defined method object, or a class method object. When the\n attribute is a user-defined method object, a new method object\n is only created if the class from which it is being retrieved is\n the same as, or a derived class of, the class stored in the\n original method object; otherwise, the original method object is\n used as it is.\n\n When a user-defined method object is created by retrieving a\n user-defined function object from a class, its ``im_self``\n attribute is ``None`` and the method object is said to be\n unbound. When one is created by retrieving a user-defined\n function object from a class via one of its instances, its\n ``im_self`` attribute is the instance, and the method object is\n said to be bound. In either case, the new method\'s ``im_class``\n attribute is the class from which the retrieval takes place, and\n its ``im_func`` attribute is the original function object.\n\n When a user-defined method object is created by retrieving\n another method object from a class or instance, the behaviour is\n the same as for a function object, except that the ``im_func``\n attribute of the new instance is not the original method object\n but its ``im_func`` attribute.\n\n When a user-defined method object is created by retrieving a\n class method object from a class or instance, its ``im_self``\n attribute is the class itself, and its ``im_func`` attribute is\n the function object underlying the class method.\n\n When an unbound user-defined method object is called, the\n underlying function (``im_func``) is called, with the\n restriction that the first argument must be an instance of the\n proper class (``im_class``) or of a derived class thereof.\n\n When a bound user-defined method object is called, the\n underlying function (``im_func``) is called, inserting the class\n instance (``im_self``) in front of the argument list. For\n instance, when ``C`` is a class which contains a definition for\n a function ``f()``, and ``x`` is an instance of ``C``, calling\n ``x.f(1)`` is equivalent to calling ``C.f(x, 1)``.\n\n When a user-defined method object is derived from a class method\n object, the "class instance" stored in ``im_self`` will actually\n be the class itself, so that calling either ``x.f(1)`` or\n ``C.f(1)`` is equivalent to calling ``f(C,1)`` where ``f`` is\n the underlying function.\n\n Note that the transformation from function object to (unbound or\n bound) method object happens each time the attribute is\n retrieved from the class or instance. In some cases, a fruitful\n optimization is to assign the attribute to a local variable and\n call that local variable. Also notice that this transformation\n only happens for user-defined functions; other callable objects\n (and all non-callable objects) are retrieved without\n transformation. It is also important to note that user-defined\n functions which are attributes of a class instance are not\n converted to bound methods; this *only* happens when the\n function is an attribute of the class.\n\n Generator functions\n A function or method which uses the ``yield`` statement (see\n section *The yield statement*) is called a *generator function*.\n Such a function, when called, always returns an iterator object\n which can be used to execute the body of the function: calling\n the iterator\'s ``next()`` method will cause the function to\n execute until it provides a value using the ``yield`` statement.\n When the function executes a ``return`` statement or falls off\n the end, a ``StopIteration`` exception is raised and the\n iterator will have reached the end of the set of values to be\n returned.\n\n Built-in functions\n A built-in function object is a wrapper around a C function.\n Examples of built-in functions are ``len()`` and ``math.sin()``\n (``math`` is a standard built-in module). The number and type of\n the arguments are determined by the C function. Special read-\n only attributes: ``__doc__`` is the function\'s documentation\n string, or ``None`` if unavailable; ``__name__`` is the\n function\'s name; ``__self__`` is set to ``None`` (but see the\n next item); ``__module__`` is the name of the module the\n function was defined in or ``None`` if unavailable.\n\n Built-in methods\n This is really a different disguise of a built-in function, this\n time containing an object passed to the C function as an\n implicit extra argument. An example of a built-in method is\n ``alist.append()``, assuming *alist* is a list object. In this\n case, the special read-only attribute ``__self__`` is set to the\n object denoted by *alist*.\n\n Class Types\n Class types, or "new-style classes," are callable. These\n objects normally act as factories for new instances of\n themselves, but variations are possible for class types that\n override ``__new__()``. The arguments of the call are passed to\n ``__new__()`` and, in the typical case, to ``__init__()`` to\n initialize the new instance.\n\n Classic Classes\n Class objects are described below. When a class object is\n called, a new class instance (also described below) is created\n and returned. This implies a call to the class\'s ``__init__()``\n method if it has one. Any arguments are passed on to the\n ``__init__()`` method. If there is no ``__init__()`` method,\n the class must be called without arguments.\n\n Class instances\n Class instances are described below. Class instances are\n callable only when the class has a ``__call__()`` method;\n ``x(arguments)`` is a shorthand for ``x.__call__(arguments)``.\n\nModules\n Modules are imported by the ``import`` statement (see section *The\n import statement*). A module object has a namespace implemented by\n a dictionary object (this is the dictionary referenced by the\n func_globals attribute of functions defined in the module).\n Attribute references are translated to lookups in this dictionary,\n e.g., ``m.x`` is equivalent to ``m.__dict__["x"]``. A module object\n does not contain the code object used to initialize the module\n (since it isn\'t needed once the initialization is done).\n\n Attribute assignment updates the module\'s namespace dictionary,\n e.g., ``m.x = 1`` is equivalent to ``m.__dict__["x"] = 1``.\n\n Special read-only attribute: ``__dict__`` is the module\'s namespace\n as a dictionary object.\n\n **CPython implementation detail:** Because of the way CPython\n clears module dictionaries, the module dictionary will be cleared\n when the module falls out of scope even if the dictionary still has\n live references. To avoid this, copy the dictionary or keep the\n module around while using its dictionary directly.\n\n Predefined (writable) attributes: ``__name__`` is the module\'s\n name; ``__doc__`` is the module\'s documentation string, or ``None``\n if unavailable; ``__file__`` is the pathname of the file from which\n the module was loaded, if it was loaded from a file. The\n ``__file__`` attribute is not present for C modules that are\n statically linked into the interpreter; for extension modules\n loaded dynamically from a shared library, it is the pathname of the\n shared library file.\n\nClasses\n Both class types (new-style classes) and class objects (old-\n style/classic classes) are typically created by class definitions\n (see section *Class definitions*). A class has a namespace\n implemented by a dictionary object. Class attribute references are\n translated to lookups in this dictionary, e.g., ``C.x`` is\n translated to ``C.__dict__["x"]`` (although for new-style classes\n in particular there are a number of hooks which allow for other\n means of locating attributes). When the attribute name is not found\n there, the attribute search continues in the base classes. For\n old-style classes, the search is depth-first, left-to-right in the\n order of occurrence in the base class list. New-style classes use\n the more complex C3 method resolution order which behaves correctly\n even in the presence of \'diamond\' inheritance structures where\n there are multiple inheritance paths leading back to a common\n ancestor. Additional details on the C3 MRO used by new-style\n classes can be found in the documentation accompanying the 2.3\n release at http://www.python.org/download/releases/2.3/mro/.\n\n When a class attribute reference (for class ``C``, say) would yield\n a user-defined function object or an unbound user-defined method\n object whose associated class is either ``C`` or one of its base\n classes, it is transformed into an unbound user-defined method\n object whose ``im_class`` attribute is ``C``. When it would yield a\n class method object, it is transformed into a bound user-defined\n method object whose ``im_self`` attribute is ``C``. When it would\n yield a static method object, it is transformed into the object\n wrapped by the static method object. See section *Implementing\n Descriptors* for another way in which attributes retrieved from a\n class may differ from those actually contained in its ``__dict__``\n (note that only new-style classes support descriptors).\n\n Class attribute assignments update the class\'s dictionary, never\n the dictionary of a base class.\n\n A class object can be called (see above) to yield a class instance\n (see below).\n\n Special attributes: ``__name__`` is the class name; ``__module__``\n is the module name in which the class was defined; ``__dict__`` is\n the dictionary containing the class\'s namespace; ``__bases__`` is a\n tuple (possibly empty or a singleton) containing the base classes,\n in the order of their occurrence in the base class list;\n ``__doc__`` is the class\'s documentation string, or None if\n undefined.\n\nClass instances\n A class instance is created by calling a class object (see above).\n A class instance has a namespace implemented as a dictionary which\n is the first place in which attribute references are searched.\n When an attribute is not found there, and the instance\'s class has\n an attribute by that name, the search continues with the class\n attributes. If a class attribute is found that is a user-defined\n function object or an unbound user-defined method object whose\n associated class is the class (call it ``C``) of the instance for\n which the attribute reference was initiated or one of its bases, it\n is transformed into a bound user-defined method object whose\n ``im_class`` attribute is ``C`` and whose ``im_self`` attribute is\n the instance. Static method and class method objects are also\n transformed, as if they had been retrieved from class ``C``; see\n above under "Classes". See section *Implementing Descriptors* for\n another way in which attributes of a class retrieved via its\n instances may differ from the objects actually stored in the\n class\'s ``__dict__``. If no class attribute is found, and the\n object\'s class has a ``__getattr__()`` method, that is called to\n satisfy the lookup.\n\n Attribute assignments and deletions update the instance\'s\n dictionary, never a class\'s dictionary. If the class has a\n ``__setattr__()`` or ``__delattr__()`` method, this is called\n instead of updating the instance dictionary directly.\n\n Class instances can pretend to be numbers, sequences, or mappings\n if they have methods with certain special names. See section\n *Special method names*.\n\n Special attributes: ``__dict__`` is the attribute dictionary;\n ``__class__`` is the instance\'s class.\n\nFiles\n A file object represents an open file. File objects are created by\n the ``open()`` built-in function, and also by ``os.popen()``,\n ``os.fdopen()``, and the ``makefile()`` method of socket objects\n (and perhaps by other functions or methods provided by extension\n modules). The objects ``sys.stdin``, ``sys.stdout`` and\n ``sys.stderr`` are initialized to file objects corresponding to the\n interpreter\'s standard input, output and error streams. See *File\n Objects* for complete documentation of file objects.\n\nInternal types\n A few types used internally by the interpreter are exposed to the\n user. Their definitions may change with future versions of the\n interpreter, but they are mentioned here for completeness.\n\n Code objects\n Code objects represent *byte-compiled* executable Python code,\n or *bytecode*. The difference between a code object and a\n function object is that the function object contains an explicit\n reference to the function\'s globals (the module in which it was\n defined), while a code object contains no context; also the\n default argument values are stored in the function object, not\n in the code object (because they represent values calculated at\n run-time). Unlike function objects, code objects are immutable\n and contain no references (directly or indirectly) to mutable\n objects.\n\n Special read-only attributes: ``co_name`` gives the function\n name; ``co_argcount`` is the number of positional arguments\n (including arguments with default values); ``co_nlocals`` is the\n number of local variables used by the function (including\n arguments); ``co_varnames`` is a tuple containing the names of\n the local variables (starting with the argument names);\n ``co_cellvars`` is a tuple containing the names of local\n variables that are referenced by nested functions;\n ``co_freevars`` is a tuple containing the names of free\n variables; ``co_code`` is a string representing the sequence of\n bytecode instructions; ``co_consts`` is a tuple containing the\n literals used by the bytecode; ``co_names`` is a tuple\n containing the names used by the bytecode; ``co_filename`` is\n the filename from which the code was compiled;\n ``co_firstlineno`` is the first line number of the function;\n ``co_lnotab`` is a string encoding the mapping from bytecode\n offsets to line numbers (for details see the source code of the\n interpreter); ``co_stacksize`` is the required stack size\n (including local variables); ``co_flags`` is an integer encoding\n a number of flags for the interpreter.\n\n The following flag bits are defined for ``co_flags``: bit\n ``0x04`` is set if the function uses the ``*arguments`` syntax\n to accept an arbitrary number of positional arguments; bit\n ``0x08`` is set if the function uses the ``**keywords`` syntax\n to accept arbitrary keyword arguments; bit ``0x20`` is set if\n the function is a generator.\n\n Future feature declarations (``from __future__ import\n division``) also use bits in ``co_flags`` to indicate whether a\n code object was compiled with a particular feature enabled: bit\n ``0x2000`` is set if the function was compiled with future\n division enabled; bits ``0x10`` and ``0x1000`` were used in\n earlier versions of Python.\n\n Other bits in ``co_flags`` are reserved for internal use.\n\n If a code object represents a function, the first item in\n ``co_consts`` is the documentation string of the function, or\n ``None`` if undefined.\n\n Frame objects\n Frame objects represent execution frames. They may occur in\n traceback objects (see below).\n\n Special read-only attributes: ``f_back`` is to the previous\n stack frame (towards the caller), or ``None`` if this is the\n bottom stack frame; ``f_code`` is the code object being executed\n in this frame; ``f_locals`` is the dictionary used to look up\n local variables; ``f_globals`` is used for global variables;\n ``f_builtins`` is used for built-in (intrinsic) names;\n ``f_restricted`` is a flag indicating whether the function is\n executing in restricted execution mode; ``f_lasti`` gives the\n precise instruction (this is an index into the bytecode string\n of the code object).\n\n Special writable attributes: ``f_trace``, if not ``None``, is a\n function called at the start of each source code line (this is\n used by the debugger); ``f_exc_type``, ``f_exc_value``,\n ``f_exc_traceback`` represent the last exception raised in the\n parent frame provided another exception was ever raised in the\n current frame (in all other cases they are None); ``f_lineno``\n is the current line number of the frame --- writing to this from\n within a trace function jumps to the given line (only for the\n bottom-most frame). A debugger can implement a Jump command\n (aka Set Next Statement) by writing to f_lineno.\n\n Traceback objects\n Traceback objects represent a stack trace of an exception. A\n traceback object is created when an exception occurs. When the\n search for an exception handler unwinds the execution stack, at\n each unwound level a traceback object is inserted in front of\n the current traceback. When an exception handler is entered,\n the stack trace is made available to the program. (See section\n *The try statement*.) It is accessible as ``sys.exc_traceback``,\n and also as the third item of the tuple returned by\n ``sys.exc_info()``. The latter is the preferred interface,\n since it works correctly when the program is using multiple\n threads. When the program contains no suitable handler, the\n stack trace is written (nicely formatted) to the standard error\n stream; if the interpreter is interactive, it is also made\n available to the user as ``sys.last_traceback``.\n\n Special read-only attributes: ``tb_next`` is the next level in\n the stack trace (towards the frame where the exception\n occurred), or ``None`` if there is no next level; ``tb_frame``\n points to the execution frame of the current level;\n ``tb_lineno`` gives the line number where the exception\n occurred; ``tb_lasti`` indicates the precise instruction. The\n line number and last instruction in the traceback may differ\n from the line number of its frame object if the exception\n occurred in a ``try`` statement with no matching except clause\n or with a finally clause.\n\n Slice objects\n Slice objects are used to represent slices when *extended slice\n syntax* is used. This is a slice using two colons, or multiple\n slices or ellipses separated by commas, e.g., ``a[i:j:step]``,\n ``a[i:j, k:l]``, or ``a[..., i:j]``. They are also created by\n the built-in ``slice()`` function.\n\n Special read-only attributes: ``start`` is the lower bound;\n ``stop`` is the upper bound; ``step`` is the step value; each is\n ``None`` if omitted. These attributes can have any type.\n\n Slice objects support one method:\n\n slice.indices(self, length)\n\n This method takes a single integer argument *length* and\n computes information about the extended slice that the slice\n object would describe if applied to a sequence of *length*\n items. It returns a tuple of three integers; respectively\n these are the *start* and *stop* indices and the *step* or\n stride length of the slice. Missing or out-of-bounds indices\n are handled in a manner consistent with regular slices.\n\n New in version 2.3.\n\n Static method objects\n Static method objects provide a way of defeating the\n transformation of function objects to method objects described\n above. A static method object is a wrapper around any other\n object, usually a user-defined method object. When a static\n method object is retrieved from a class or a class instance, the\n object actually returned is the wrapped object, which is not\n subject to any further transformation. Static method objects are\n not themselves callable, although the objects they wrap usually\n are. Static method objects are created by the built-in\n ``staticmethod()`` constructor.\n\n Class method objects\n A class method object, like a static method object, is a wrapper\n around another object that alters the way in which that object\n is retrieved from classes and class instances. The behaviour of\n class method objects upon such retrieval is described above,\n under "User-defined methods". Class method objects are created\n by the built-in ``classmethod()`` constructor.\n', 'typesfunctions': '\nFunctions\n*********\n\nFunction objects are created by function definitions. The only\noperation on a function object is to call it: ``func(argument-list)``.\n\nThere are really two flavors of function objects: built-in functions\nand user-defined functions. Both support the same operation (to call\nthe function), but the implementation is different, hence the\ndifferent object types.\n\nSee *Function definitions* for more information.\n', - 'typesmapping': '\nMapping Types --- ``dict``\n**************************\n\nA *mapping* object maps *hashable* values to arbitrary objects.\nMappings are mutable objects. There is currently only one standard\nmapping type, the *dictionary*. (For other containers see the built\nin ``list``, ``set``, and ``tuple`` classes, and the ``collections``\nmodule.)\n\nA dictionary\'s keys are *almost* arbitrary values. Values that are\nnot *hashable*, that is, values containing lists, dictionaries or\nother mutable types (that are compared by value rather than by object\nidentity) may not be used as keys. Numeric types used for keys obey\nthe normal rules for numeric comparison: if two numbers compare equal\n(such as ``1`` and ``1.0``) then they can be used interchangeably to\nindex the same dictionary entry. (Note however, that since computers\nstore floating-point numbers as approximations it is usually unwise to\nuse them as dictionary keys.)\n\nDictionaries can be created by placing a comma-separated list of\n``key: value`` pairs within braces, for example: ``{\'jack\': 4098,\n\'sjoerd\': 4127}`` or ``{4098: \'jack\', 4127: \'sjoerd\'}``, or by the\n``dict`` constructor.\n\nclass class dict(**kwarg)\nclass class dict(mapping, **kwarg)\nclass class dict(iterable, **kwarg)\n\n Return a new dictionary initialized from an optional positional\n argument and a possibly empty set of keyword arguments.\n\n If no positional argument is given, an empty dictionary is created.\n If a positional argument is given and it is a mapping object, a\n dictionary is created with the same key-value pairs as the mapping\n object. Otherwise, the positional argument must be an *iterator*\n object. Each item in the iterable must itself be an iterator with\n exactly two objects. The first object of each item becomes a key\n in the new dictionary, and the second object the corresponding\n value. If a key occurs more than once, the last value for that key\n becomes the corresponding value in the new dictionary.\n\n If keyword arguments are given, the keyword arguments and their\n values are added to the dictionary created from the positional\n argument. If a key being added is already present, the value from\n the keyword argument replaces the value from the positional\n argument.\n\n To illustrate, the following examples all return a dictionary equal\n to ``{"one": 1, "two": 2, "three": 3}``:\n\n >>> a = dict(one=1, two=2, three=3)\n >>> b = {\'one\': 1, \'two\': 2, \'three\': 3}\n >>> c = dict(zip([\'one\', \'two\', \'three\'], [1, 2, 3]))\n >>> d = dict([(\'two\', 2), (\'one\', 1), (\'three\', 3)])\n >>> e = dict({\'three\': 3, \'one\': 1, \'two\': 2})\n >>> a == b == c == d == e\n True\n\n Providing keyword arguments as in the first example only works for\n keys that are valid Python identifiers. Otherwise, any valid keys\n can be used.\n\n New in version 2.2.\n\n Changed in version 2.3: Support for building a dictionary from\n keyword arguments added.\n\n These are the operations that dictionaries support (and therefore,\n custom mapping types should support too):\n\n len(d)\n\n Return the number of items in the dictionary *d*.\n\n d[key]\n\n Return the item of *d* with key *key*. Raises a ``KeyError`` if\n *key* is not in the map.\n\n New in version 2.5: If a subclass of dict defines a method\n ``__missing__()``, if the key *key* is not present, the\n ``d[key]`` operation calls that method with the key *key* as\n argument. The ``d[key]`` operation then returns or raises\n whatever is returned or raised by the ``__missing__(key)`` call\n if the key is not present. No other operations or methods invoke\n ``__missing__()``. If ``__missing__()`` is not defined,\n ``KeyError`` is raised. ``__missing__()`` must be a method; it\n cannot be an instance variable. For an example, see\n ``collections.defaultdict``.\n\n d[key] = value\n\n Set ``d[key]`` to *value*.\n\n del d[key]\n\n Remove ``d[key]`` from *d*. Raises a ``KeyError`` if *key* is\n not in the map.\n\n key in d\n\n Return ``True`` if *d* has a key *key*, else ``False``.\n\n New in version 2.2.\n\n key not in d\n\n Equivalent to ``not key in d``.\n\n New in version 2.2.\n\n iter(d)\n\n Return an iterator over the keys of the dictionary. This is a\n shortcut for ``iterkeys()``.\n\n clear()\n\n Remove all items from the dictionary.\n\n copy()\n\n Return a shallow copy of the dictionary.\n\n fromkeys(seq[, value])\n\n Create a new dictionary with keys from *seq* and values set to\n *value*.\n\n ``fromkeys()`` is a class method that returns a new dictionary.\n *value* defaults to ``None``.\n\n New in version 2.3.\n\n get(key[, default])\n\n Return the value for *key* if *key* is in the dictionary, else\n *default*. If *default* is not given, it defaults to ``None``,\n so that this method never raises a ``KeyError``.\n\n has_key(key)\n\n Test for the presence of *key* in the dictionary. ``has_key()``\n is deprecated in favor of ``key in d``.\n\n items()\n\n Return a copy of the dictionary\'s list of ``(key, value)``\n pairs.\n\n **CPython implementation detail:** Keys and values are listed in\n an arbitrary order which is non-random, varies across Python\n implementations, and depends on the dictionary\'s history of\n insertions and deletions.\n\n If ``items()``, ``keys()``, ``values()``, ``iteritems()``,\n ``iterkeys()``, and ``itervalues()`` are called with no\n intervening modifications to the dictionary, the lists will\n directly correspond. This allows the creation of ``(value,\n key)`` pairs using ``zip()``: ``pairs = zip(d.values(),\n d.keys())``. The same relationship holds for the ``iterkeys()``\n and ``itervalues()`` methods: ``pairs = zip(d.itervalues(),\n d.iterkeys())`` provides the same value for ``pairs``. Another\n way to create the same list is ``pairs = [(v, k) for (k, v) in\n d.iteritems()]``.\n\n iteritems()\n\n Return an iterator over the dictionary\'s ``(key, value)`` pairs.\n See the note for ``dict.items()``.\n\n Using ``iteritems()`` while adding or deleting entries in the\n dictionary may raise a ``RuntimeError`` or fail to iterate over\n all entries.\n\n New in version 2.2.\n\n iterkeys()\n\n Return an iterator over the dictionary\'s keys. See the note for\n ``dict.items()``.\n\n Using ``iterkeys()`` while adding or deleting entries in the\n dictionary may raise a ``RuntimeError`` or fail to iterate over\n all entries.\n\n New in version 2.2.\n\n itervalues()\n\n Return an iterator over the dictionary\'s values. See the note\n for ``dict.items()``.\n\n Using ``itervalues()`` while adding or deleting entries in the\n dictionary may raise a ``RuntimeError`` or fail to iterate over\n all entries.\n\n New in version 2.2.\n\n keys()\n\n Return a copy of the dictionary\'s list of keys. See the note\n for ``dict.items()``.\n\n pop(key[, default])\n\n If *key* is in the dictionary, remove it and return its value,\n else return *default*. If *default* is not given and *key* is\n not in the dictionary, a ``KeyError`` is raised.\n\n New in version 2.3.\n\n popitem()\n\n Remove and return an arbitrary ``(key, value)`` pair from the\n dictionary.\n\n ``popitem()`` is useful to destructively iterate over a\n dictionary, as often used in set algorithms. If the dictionary\n is empty, calling ``popitem()`` raises a ``KeyError``.\n\n setdefault(key[, default])\n\n If *key* is in the dictionary, return its value. If not, insert\n *key* with a value of *default* and return *default*. *default*\n defaults to ``None``.\n\n update([other])\n\n Update the dictionary with the key/value pairs from *other*,\n overwriting existing keys. Return ``None``.\n\n ``update()`` accepts either another dictionary object or an\n iterable of key/value pairs (as tuples or other iterables of\n length two). If keyword arguments are specified, the dictionary\n is then updated with those key/value pairs: ``d.update(red=1,\n blue=2)``.\n\n Changed in version 2.4: Allowed the argument to be an iterable\n of key/value pairs and allowed keyword arguments.\n\n values()\n\n Return a copy of the dictionary\'s list of values. See the note\n for ``dict.items()``.\n\n viewitems()\n\n Return a new view of the dictionary\'s items (``(key, value)``\n pairs). See below for documentation of view objects.\n\n New in version 2.7.\n\n viewkeys()\n\n Return a new view of the dictionary\'s keys. See below for\n documentation of view objects.\n\n New in version 2.7.\n\n viewvalues()\n\n Return a new view of the dictionary\'s values. See below for\n documentation of view objects.\n\n New in version 2.7.\n\n\nDictionary view objects\n=======================\n\nThe objects returned by ``dict.viewkeys()``, ``dict.viewvalues()`` and\n``dict.viewitems()`` are *view objects*. They provide a dynamic view\non the dictionary\'s entries, which means that when the dictionary\nchanges, the view reflects these changes.\n\nDictionary views can be iterated over to yield their respective data,\nand support membership tests:\n\nlen(dictview)\n\n Return the number of entries in the dictionary.\n\niter(dictview)\n\n Return an iterator over the keys, values or items (represented as\n tuples of ``(key, value)``) in the dictionary.\n\n Keys and values are iterated over in an arbitrary order which is\n non-random, varies across Python implementations, and depends on\n the dictionary\'s history of insertions and deletions. If keys,\n values and items views are iterated over with no intervening\n modifications to the dictionary, the order of items will directly\n correspond. This allows the creation of ``(value, key)`` pairs\n using ``zip()``: ``pairs = zip(d.values(), d.keys())``. Another\n way to create the same list is ``pairs = [(v, k) for (k, v) in\n d.items()]``.\n\n Iterating views while adding or deleting entries in the dictionary\n may raise a ``RuntimeError`` or fail to iterate over all entries.\n\nx in dictview\n\n Return ``True`` if *x* is in the underlying dictionary\'s keys,\n values or items (in the latter case, *x* should be a ``(key,\n value)`` tuple).\n\nKeys views are set-like since their entries are unique and hashable.\nIf all values are hashable, so that (key, value) pairs are unique and\nhashable, then the items view is also set-like. (Values views are not\ntreated as set-like since the entries are generally not unique.) Then\nthese set operations are available ("other" refers either to another\nview or a set):\n\ndictview & other\n\n Return the intersection of the dictview and the other object as a\n new set.\n\ndictview | other\n\n Return the union of the dictview and the other object as a new set.\n\ndictview - other\n\n Return the difference between the dictview and the other object\n (all elements in *dictview* that aren\'t in *other*) as a new set.\n\ndictview ^ other\n\n Return the symmetric difference (all elements either in *dictview*\n or *other*, but not in both) of the dictview and the other object\n as a new set.\n\nAn example of dictionary view usage:\n\n >>> dishes = {\'eggs\': 2, \'sausage\': 1, \'bacon\': 1, \'spam\': 500}\n >>> keys = dishes.viewkeys()\n >>> values = dishes.viewvalues()\n\n >>> # iteration\n >>> n = 0\n >>> for val in values:\n ... n += val\n >>> print(n)\n 504\n\n >>> # keys and values are iterated over in the same order\n >>> list(keys)\n [\'eggs\', \'bacon\', \'sausage\', \'spam\']\n >>> list(values)\n [2, 1, 1, 500]\n\n >>> # view objects are dynamic and reflect dict changes\n >>> del dishes[\'eggs\']\n >>> del dishes[\'sausage\']\n >>> list(keys)\n [\'spam\', \'bacon\']\n\n >>> # set operations\n >>> keys & {\'eggs\', \'bacon\', \'salad\'}\n {\'bacon\'}\n', + 'typesmapping': '\nMapping Types --- ``dict``\n**************************\n\nA *mapping* object maps *hashable* values to arbitrary objects.\nMappings are mutable objects. There is currently only one standard\nmapping type, the *dictionary*. (For other containers see the built\nin ``list``, ``set``, and ``tuple`` classes, and the ``collections``\nmodule.)\n\nA dictionary\'s keys are *almost* arbitrary values. Values that are\nnot *hashable*, that is, values containing lists, dictionaries or\nother mutable types (that are compared by value rather than by object\nidentity) may not be used as keys. Numeric types used for keys obey\nthe normal rules for numeric comparison: if two numbers compare equal\n(such as ``1`` and ``1.0``) then they can be used interchangeably to\nindex the same dictionary entry. (Note however, that since computers\nstore floating-point numbers as approximations it is usually unwise to\nuse them as dictionary keys.)\n\nDictionaries can be created by placing a comma-separated list of\n``key: value`` pairs within braces, for example: ``{\'jack\': 4098,\n\'sjoerd\': 4127}`` or ``{4098: \'jack\', 4127: \'sjoerd\'}``, or by the\n``dict`` constructor.\n\nclass class dict(**kwarg)\nclass class dict(mapping, **kwarg)\nclass class dict(iterable, **kwarg)\n\n Return a new dictionary initialized from an optional positional\n argument and a possibly empty set of keyword arguments.\n\n If no positional argument is given, an empty dictionary is created.\n If a positional argument is given and it is a mapping object, a\n dictionary is created with the same key-value pairs as the mapping\n object. Otherwise, the positional argument must be an *iterable*\n object. Each item in the iterable must itself be an iterable with\n exactly two objects. The first object of each item becomes a key\n in the new dictionary, and the second object the corresponding\n value. If a key occurs more than once, the last value for that key\n becomes the corresponding value in the new dictionary.\n\n If keyword arguments are given, the keyword arguments and their\n values are added to the dictionary created from the positional\n argument. If a key being added is already present, the value from\n the keyword argument replaces the value from the positional\n argument.\n\n To illustrate, the following examples all return a dictionary equal\n to ``{"one": 1, "two": 2, "three": 3}``:\n\n >>> a = dict(one=1, two=2, three=3)\n >>> b = {\'one\': 1, \'two\': 2, \'three\': 3}\n >>> c = dict(zip([\'one\', \'two\', \'three\'], [1, 2, 3]))\n >>> d = dict([(\'two\', 2), (\'one\', 1), (\'three\', 3)])\n >>> e = dict({\'three\': 3, \'one\': 1, \'two\': 2})\n >>> a == b == c == d == e\n True\n\n Providing keyword arguments as in the first example only works for\n keys that are valid Python identifiers. Otherwise, any valid keys\n can be used.\n\n New in version 2.2.\n\n Changed in version 2.3: Support for building a dictionary from\n keyword arguments added.\n\n These are the operations that dictionaries support (and therefore,\n custom mapping types should support too):\n\n len(d)\n\n Return the number of items in the dictionary *d*.\n\n d[key]\n\n Return the item of *d* with key *key*. Raises a ``KeyError`` if\n *key* is not in the map.\n\n New in version 2.5: If a subclass of dict defines a method\n ``__missing__()``, if the key *key* is not present, the\n ``d[key]`` operation calls that method with the key *key* as\n argument. The ``d[key]`` operation then returns or raises\n whatever is returned or raised by the ``__missing__(key)`` call\n if the key is not present. No other operations or methods invoke\n ``__missing__()``. If ``__missing__()`` is not defined,\n ``KeyError`` is raised. ``__missing__()`` must be a method; it\n cannot be an instance variable. For an example, see\n ``collections.defaultdict``.\n\n d[key] = value\n\n Set ``d[key]`` to *value*.\n\n del d[key]\n\n Remove ``d[key]`` from *d*. Raises a ``KeyError`` if *key* is\n not in the map.\n\n key in d\n\n Return ``True`` if *d* has a key *key*, else ``False``.\n\n New in version 2.2.\n\n key not in d\n\n Equivalent to ``not key in d``.\n\n New in version 2.2.\n\n iter(d)\n\n Return an iterator over the keys of the dictionary. This is a\n shortcut for ``iterkeys()``.\n\n clear()\n\n Remove all items from the dictionary.\n\n copy()\n\n Return a shallow copy of the dictionary.\n\n fromkeys(seq[, value])\n\n Create a new dictionary with keys from *seq* and values set to\n *value*.\n\n ``fromkeys()`` is a class method that returns a new dictionary.\n *value* defaults to ``None``.\n\n New in version 2.3.\n\n get(key[, default])\n\n Return the value for *key* if *key* is in the dictionary, else\n *default*. If *default* is not given, it defaults to ``None``,\n so that this method never raises a ``KeyError``.\n\n has_key(key)\n\n Test for the presence of *key* in the dictionary. ``has_key()``\n is deprecated in favor of ``key in d``.\n\n items()\n\n Return a copy of the dictionary\'s list of ``(key, value)``\n pairs.\n\n **CPython implementation detail:** Keys and values are listed in\n an arbitrary order which is non-random, varies across Python\n implementations, and depends on the dictionary\'s history of\n insertions and deletions.\n\n If ``items()``, ``keys()``, ``values()``, ``iteritems()``,\n ``iterkeys()``, and ``itervalues()`` are called with no\n intervening modifications to the dictionary, the lists will\n directly correspond. This allows the creation of ``(value,\n key)`` pairs using ``zip()``: ``pairs = zip(d.values(),\n d.keys())``. The same relationship holds for the ``iterkeys()``\n and ``itervalues()`` methods: ``pairs = zip(d.itervalues(),\n d.iterkeys())`` provides the same value for ``pairs``. Another\n way to create the same list is ``pairs = [(v, k) for (k, v) in\n d.iteritems()]``.\n\n iteritems()\n\n Return an iterator over the dictionary\'s ``(key, value)`` pairs.\n See the note for ``dict.items()``.\n\n Using ``iteritems()`` while adding or deleting entries in the\n dictionary may raise a ``RuntimeError`` or fail to iterate over\n all entries.\n\n New in version 2.2.\n\n iterkeys()\n\n Return an iterator over the dictionary\'s keys. See the note for\n ``dict.items()``.\n\n Using ``iterkeys()`` while adding or deleting entries in the\n dictionary may raise a ``RuntimeError`` or fail to iterate over\n all entries.\n\n New in version 2.2.\n\n itervalues()\n\n Return an iterator over the dictionary\'s values. See the note\n for ``dict.items()``.\n\n Using ``itervalues()`` while adding or deleting entries in the\n dictionary may raise a ``RuntimeError`` or fail to iterate over\n all entries.\n\n New in version 2.2.\n\n keys()\n\n Return a copy of the dictionary\'s list of keys. See the note\n for ``dict.items()``.\n\n pop(key[, default])\n\n If *key* is in the dictionary, remove it and return its value,\n else return *default*. If *default* is not given and *key* is\n not in the dictionary, a ``KeyError`` is raised.\n\n New in version 2.3.\n\n popitem()\n\n Remove and return an arbitrary ``(key, value)`` pair from the\n dictionary.\n\n ``popitem()`` is useful to destructively iterate over a\n dictionary, as often used in set algorithms. If the dictionary\n is empty, calling ``popitem()`` raises a ``KeyError``.\n\n setdefault(key[, default])\n\n If *key* is in the dictionary, return its value. If not, insert\n *key* with a value of *default* and return *default*. *default*\n defaults to ``None``.\n\n update([other])\n\n Update the dictionary with the key/value pairs from *other*,\n overwriting existing keys. Return ``None``.\n\n ``update()`` accepts either another dictionary object or an\n iterable of key/value pairs (as tuples or other iterables of\n length two). If keyword arguments are specified, the dictionary\n is then updated with those key/value pairs: ``d.update(red=1,\n blue=2)``.\n\n Changed in version 2.4: Allowed the argument to be an iterable\n of key/value pairs and allowed keyword arguments.\n\n values()\n\n Return a copy of the dictionary\'s list of values. See the note\n for ``dict.items()``.\n\n viewitems()\n\n Return a new view of the dictionary\'s items (``(key, value)``\n pairs). See below for documentation of view objects.\n\n New in version 2.7.\n\n viewkeys()\n\n Return a new view of the dictionary\'s keys. See below for\n documentation of view objects.\n\n New in version 2.7.\n\n viewvalues()\n\n Return a new view of the dictionary\'s values. See below for\n documentation of view objects.\n\n New in version 2.7.\n\n\nDictionary view objects\n=======================\n\nThe objects returned by ``dict.viewkeys()``, ``dict.viewvalues()`` and\n``dict.viewitems()`` are *view objects*. They provide a dynamic view\non the dictionary\'s entries, which means that when the dictionary\nchanges, the view reflects these changes.\n\nDictionary views can be iterated over to yield their respective data,\nand support membership tests:\n\nlen(dictview)\n\n Return the number of entries in the dictionary.\n\niter(dictview)\n\n Return an iterator over the keys, values or items (represented as\n tuples of ``(key, value)``) in the dictionary.\n\n Keys and values are iterated over in an arbitrary order which is\n non-random, varies across Python implementations, and depends on\n the dictionary\'s history of insertions and deletions. If keys,\n values and items views are iterated over with no intervening\n modifications to the dictionary, the order of items will directly\n correspond. This allows the creation of ``(value, key)`` pairs\n using ``zip()``: ``pairs = zip(d.values(), d.keys())``. Another\n way to create the same list is ``pairs = [(v, k) for (k, v) in\n d.items()]``.\n\n Iterating views while adding or deleting entries in the dictionary\n may raise a ``RuntimeError`` or fail to iterate over all entries.\n\nx in dictview\n\n Return ``True`` if *x* is in the underlying dictionary\'s keys,\n values or items (in the latter case, *x* should be a ``(key,\n value)`` tuple).\n\nKeys views are set-like since their entries are unique and hashable.\nIf all values are hashable, so that (key, value) pairs are unique and\nhashable, then the items view is also set-like. (Values views are not\ntreated as set-like since the entries are generally not unique.) Then\nthese set operations are available ("other" refers either to another\nview or a set):\n\ndictview & other\n\n Return the intersection of the dictview and the other object as a\n new set.\n\ndictview | other\n\n Return the union of the dictview and the other object as a new set.\n\ndictview - other\n\n Return the difference between the dictview and the other object\n (all elements in *dictview* that aren\'t in *other*) as a new set.\n\ndictview ^ other\n\n Return the symmetric difference (all elements either in *dictview*\n or *other*, but not in both) of the dictview and the other object\n as a new set.\n\nAn example of dictionary view usage:\n\n >>> dishes = {\'eggs\': 2, \'sausage\': 1, \'bacon\': 1, \'spam\': 500}\n >>> keys = dishes.viewkeys()\n >>> values = dishes.viewvalues()\n\n >>> # iteration\n >>> n = 0\n >>> for val in values:\n ... n += val\n >>> print(n)\n 504\n\n >>> # keys and values are iterated over in the same order\n >>> list(keys)\n [\'eggs\', \'bacon\', \'sausage\', \'spam\']\n >>> list(values)\n [2, 1, 1, 500]\n\n >>> # view objects are dynamic and reflect dict changes\n >>> del dishes[\'eggs\']\n >>> del dishes[\'sausage\']\n >>> list(keys)\n [\'spam\', \'bacon\']\n\n >>> # set operations\n >>> keys & {\'eggs\', \'bacon\', \'salad\'}\n {\'bacon\'}\n', 'typesmethods': '\nMethods\n*******\n\nMethods are functions that are called using the attribute notation.\nThere are two flavors: built-in methods (such as ``append()`` on\nlists) and class instance methods. Built-in methods are described\nwith the types that support them.\n\nThe implementation adds two special read-only attributes to class\ninstance methods: ``m.im_self`` is the object on which the method\noperates, and ``m.im_func`` is the function implementing the method.\nCalling ``m(arg-1, arg-2, ..., arg-n)`` is completely equivalent to\ncalling ``m.im_func(m.im_self, arg-1, arg-2, ..., arg-n)``.\n\nClass instance methods are either *bound* or *unbound*, referring to\nwhether the method was accessed through an instance or a class,\nrespectively. When a method is unbound, its ``im_self`` attribute\nwill be ``None`` and if called, an explicit ``self`` object must be\npassed as the first argument. In this case, ``self`` must be an\ninstance of the unbound method\'s class (or a subclass of that class),\notherwise a ``TypeError`` is raised.\n\nLike function objects, methods objects support getting arbitrary\nattributes. However, since method attributes are actually stored on\nthe underlying function object (``meth.im_func``), setting method\nattributes on either bound or unbound methods is disallowed.\nAttempting to set an attribute on a method results in an\n``AttributeError`` being raised. In order to set a method attribute,\nyou need to explicitly set it on the underlying function object:\n\n >>> class C:\n ... def method(self):\n ... pass\n ...\n >>> c = C()\n >>> c.method.whoami = \'my name is method\' # can\'t set on the method\n Traceback (most recent call last):\n File "", line 1, in \n AttributeError: \'instancemethod\' object has no attribute \'whoami\'\n >>> c.method.im_func.whoami = \'my name is method\'\n >>> c.method.whoami\n \'my name is method\'\n\nSee *The standard type hierarchy* for more information.\n', 'typesmodules': "\nModules\n*******\n\nThe only special operation on a module is attribute access:\n``m.name``, where *m* is a module and *name* accesses a name defined\nin *m*'s symbol table. Module attributes can be assigned to. (Note\nthat the ``import`` statement is not, strictly speaking, an operation\non a module object; ``import foo`` does not require a module object\nnamed *foo* to exist, rather it requires an (external) *definition*\nfor a module named *foo* somewhere.)\n\nA special attribute of every module is ``__dict__``. This is the\ndictionary containing the module's symbol table. Modifying this\ndictionary will actually change the module's symbol table, but direct\nassignment to the ``__dict__`` attribute is not possible (you can\nwrite ``m.__dict__['a'] = 1``, which defines ``m.a`` to be ``1``, but\nyou can't write ``m.__dict__ = {}``). Modifying ``__dict__`` directly\nis not recommended.\n\nModules built into the interpreter are written like this: ````. If loaded from a file, they are written as\n````.\n", 'typesseq': '\nSequence Types --- ``str``, ``unicode``, ``list``, ``tuple``, ``bytearray``, ``buffer``, ``xrange``\n***************************************************************************************************\n\nThere are seven sequence types: strings, Unicode strings, lists,\ntuples, bytearrays, buffers, and xrange objects.\n\nFor other containers see the built in ``dict`` and ``set`` classes,\nand the ``collections`` module.\n\nString literals are written in single or double quotes: ``\'xyzzy\'``,\n``"frobozz"``. See *String literals* for more about string literals.\nUnicode strings are much like strings, but are specified in the syntax\nusing a preceding ``\'u\'`` character: ``u\'abc\'``, ``u"def"``. In\naddition to the functionality described here, there are also string-\nspecific methods described in the *String Methods* section. Lists are\nconstructed with square brackets, separating items with commas: ``[a,\nb, c]``. Tuples are constructed by the comma operator (not within\nsquare brackets), with or without enclosing parentheses, but an empty\ntuple must have the enclosing parentheses, such as ``a, b, c`` or\n``()``. A single item tuple must have a trailing comma, such as\n``(d,)``.\n\nBytearray objects are created with the built-in function\n``bytearray()``.\n\nBuffer objects are not directly supported by Python syntax, but can be\ncreated by calling the built-in function ``buffer()``. They don\'t\nsupport concatenation or repetition.\n\nObjects of type xrange are similar to buffers in that there is no\nspecific syntax to create them, but they are created using the\n``xrange()`` function. They don\'t support slicing, concatenation or\nrepetition, and using ``in``, ``not in``, ``min()`` or ``max()`` on\nthem is inefficient.\n\nMost sequence types support the following operations. The ``in`` and\n``not in`` operations have the same priorities as the comparison\noperations. The ``+`` and ``*`` operations have the same priority as\nthe corresponding numeric operations. [3] Additional methods are\nprovided for *Mutable Sequence Types*.\n\nThis table lists the sequence operations sorted in ascending priority\n(operations in the same box have the same priority). In the table,\n*s* and *t* are sequences of the same type; *n*, *i* and *j* are\nintegers:\n\n+--------------------+----------------------------------+------------+\n| Operation | Result | Notes |\n+====================+==================================+============+\n| ``x in s`` | ``True`` if an item of *s* is | (1) |\n| | equal to *x*, else ``False`` | |\n+--------------------+----------------------------------+------------+\n| ``x not in s`` | ``False`` if an item of *s* is | (1) |\n| | equal to *x*, else ``True`` | |\n+--------------------+----------------------------------+------------+\n| ``s + t`` | the concatenation of *s* and *t* | (6) |\n+--------------------+----------------------------------+------------+\n| ``s * n, n * s`` | *n* shallow copies of *s* | (2) |\n| | concatenated | |\n+--------------------+----------------------------------+------------+\n| ``s[i]`` | *i*th item of *s*, origin 0 | (3) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j]`` | slice of *s* from *i* to *j* | (3)(4) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j:k]`` | slice of *s* from *i* to *j* | (3)(5) |\n| | with step *k* | |\n+--------------------+----------------------------------+------------+\n| ``len(s)`` | length of *s* | |\n+--------------------+----------------------------------+------------+\n| ``min(s)`` | smallest item of *s* | |\n+--------------------+----------------------------------+------------+\n| ``max(s)`` | largest item of *s* | |\n+--------------------+----------------------------------+------------+\n| ``s.index(x)`` | index of the first occurrence of | |\n| | *x* in *s* | |\n+--------------------+----------------------------------+------------+\n| ``s.count(x)`` | total number of occurrences of | |\n| | *x* in *s* | |\n+--------------------+----------------------------------+------------+\n\nSequence types also support comparisons. In particular, tuples and\nlists are compared lexicographically by comparing corresponding\nelements. This means that to compare equal, every element must compare\nequal and the two sequences must be of the same type and have the same\nlength. (For full details see *Comparisons* in the language\nreference.)\n\nNotes:\n\n1. When *s* is a string or Unicode string object the ``in`` and ``not\n in`` operations act like a substring test. In Python versions\n before 2.3, *x* had to be a string of length 1. In Python 2.3 and\n beyond, *x* may be a string of any length.\n\n2. Values of *n* less than ``0`` are treated as ``0`` (which yields an\n empty sequence of the same type as *s*). Note also that the copies\n are shallow; nested structures are not copied. This often haunts\n new Python programmers; consider:\n\n >>> lists = [[]] * 3\n >>> lists\n [[], [], []]\n >>> lists[0].append(3)\n >>> lists\n [[3], [3], [3]]\n\n What has happened is that ``[[]]`` is a one-element list containing\n an empty list, so all three elements of ``[[]] * 3`` are (pointers\n to) this single empty list. Modifying any of the elements of\n ``lists`` modifies this single list. You can create a list of\n different lists this way:\n\n >>> lists = [[] for i in range(3)]\n >>> lists[0].append(3)\n >>> lists[1].append(5)\n >>> lists[2].append(7)\n >>> lists\n [[3], [5], [7]]\n\n3. If *i* or *j* is negative, the index is relative to the end of the\n string: ``len(s) + i`` or ``len(s) + j`` is substituted. But note\n that ``-0`` is still ``0``.\n\n4. The slice of *s* from *i* to *j* is defined as the sequence of\n items with index *k* such that ``i <= k < j``. If *i* or *j* is\n greater than ``len(s)``, use ``len(s)``. If *i* is omitted or\n ``None``, use ``0``. If *j* is omitted or ``None``, use\n ``len(s)``. If *i* is greater than or equal to *j*, the slice is\n empty.\n\n5. The slice of *s* from *i* to *j* with step *k* is defined as the\n sequence of items with index ``x = i + n*k`` such that ``0 <= n <\n (j-i)/k``. In other words, the indices are ``i``, ``i+k``,\n ``i+2*k``, ``i+3*k`` and so on, stopping when *j* is reached (but\n never including *j*). If *i* or *j* is greater than ``len(s)``,\n use ``len(s)``. If *i* or *j* are omitted or ``None``, they become\n "end" values (which end depends on the sign of *k*). Note, *k*\n cannot be zero. If *k* is ``None``, it is treated like ``1``.\n\n6. **CPython implementation detail:** If *s* and *t* are both strings,\n some Python implementations such as CPython can usually perform an\n in-place optimization for assignments of the form ``s = s + t`` or\n ``s += t``. When applicable, this optimization makes quadratic\n run-time much less likely. This optimization is both version and\n implementation dependent. For performance sensitive code, it is\n preferable to use the ``str.join()`` method which assures\n consistent linear concatenation performance across versions and\n implementations.\n\n Changed in version 2.4: Formerly, string concatenation never\n occurred in-place.\n\n\nString Methods\n==============\n\nBelow are listed the string methods which both 8-bit strings and\nUnicode objects support. Some of them are also available on\n``bytearray`` objects.\n\nIn addition, Python\'s strings support the sequence type methods\ndescribed in the *Sequence Types --- str, unicode, list, tuple,\nbytearray, buffer, xrange* section. To output formatted strings use\ntemplate strings or the ``%`` operator described in the *String\nFormatting Operations* section. Also, see the ``re`` module for string\nfunctions based on regular expressions.\n\nstr.capitalize()\n\n Return a copy of the string with its first character capitalized\n and the rest lowercased.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.center(width[, fillchar])\n\n Return centered in a string of length *width*. Padding is done\n using the specified *fillchar* (default is a space).\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.count(sub[, start[, end]])\n\n Return the number of non-overlapping occurrences of substring *sub*\n in the range [*start*, *end*]. Optional arguments *start* and\n *end* are interpreted as in slice notation.\n\nstr.decode([encoding[, errors]])\n\n Decodes the string using the codec registered for *encoding*.\n *encoding* defaults to the default string encoding. *errors* may\n be given to set a different error handling scheme. The default is\n ``\'strict\'``, meaning that encoding errors raise ``UnicodeError``.\n Other possible values are ``\'ignore\'``, ``\'replace\'`` and any other\n name registered via ``codecs.register_error()``, see section *Codec\n Base Classes*.\n\n New in version 2.2.\n\n Changed in version 2.3: Support for other error handling schemes\n added.\n\n Changed in version 2.7: Support for keyword arguments added.\n\nstr.encode([encoding[, errors]])\n\n Return an encoded version of the string. Default encoding is the\n current default string encoding. *errors* may be given to set a\n different error handling scheme. The default for *errors* is\n ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'``, ``\'xmlcharrefreplace\'``, ``\'backslashreplace\'`` and\n any other name registered via ``codecs.register_error()``, see\n section *Codec Base Classes*. For a list of possible encodings, see\n section *Standard Encodings*.\n\n New in version 2.0.\n\n Changed in version 2.3: Support for ``\'xmlcharrefreplace\'`` and\n ``\'backslashreplace\'`` and other error handling schemes added.\n\n Changed in version 2.7: Support for keyword arguments added.\n\nstr.endswith(suffix[, start[, end]])\n\n Return ``True`` if the string ends with the specified *suffix*,\n otherwise return ``False``. *suffix* can also be a tuple of\n suffixes to look for. With optional *start*, test beginning at\n that position. With optional *end*, stop comparing at that\n position.\n\n Changed in version 2.5: Accept tuples as *suffix*.\n\nstr.expandtabs([tabsize])\n\n Return a copy of the string where all tab characters are replaced\n by one or more spaces, depending on the current column and the\n given tab size. Tab positions occur every *tabsize* characters\n (default is 8, giving tab positions at columns 0, 8, 16 and so on).\n To expand the string, the current column is set to zero and the\n string is examined character by character. If the character is a\n tab (``\\t``), one or more space characters are inserted in the\n result until the current column is equal to the next tab position.\n (The tab character itself is not copied.) If the character is a\n newline (``\\n``) or return (``\\r``), it is copied and the current\n column is reset to zero. Any other character is copied unchanged\n and the current column is incremented by one regardless of how the\n character is represented when printed.\n\n >>> \'01\\t012\\t0123\\t01234\'.expandtabs()\n \'01 012 0123 01234\'\n >>> \'01\\t012\\t0123\\t01234\'.expandtabs(4)\n \'01 012 0123 01234\'\n\nstr.find(sub[, start[, end]])\n\n Return the lowest index in the string where substring *sub* is\n found, such that *sub* is contained in the slice ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` if *sub* is not found.\n\n Note: The ``find()`` method should be used only if you need to know the\n position of *sub*. To check if *sub* is a substring or not, use\n the ``in`` operator:\n\n >>> \'Py\' in \'Python\'\n True\n\nstr.format(*args, **kwargs)\n\n Perform a string formatting operation. The string on which this\n method is called can contain literal text or replacement fields\n delimited by braces ``{}``. Each replacement field contains either\n the numeric index of a positional argument, or the name of a\n keyword argument. Returns a copy of the string where each\n replacement field is replaced with the string value of the\n corresponding argument.\n\n >>> "The sum of 1 + 2 is {0}".format(1+2)\n \'The sum of 1 + 2 is 3\'\n\n See *Format String Syntax* for a description of the various\n formatting options that can be specified in format strings.\n\n This method of string formatting is the new standard in Python 3,\n and should be preferred to the ``%`` formatting described in\n *String Formatting Operations* in new code.\n\n New in version 2.6.\n\nstr.index(sub[, start[, end]])\n\n Like ``find()``, but raise ``ValueError`` when the substring is not\n found.\n\nstr.isalnum()\n\n Return true if all characters in the string are alphanumeric and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isalpha()\n\n Return true if all characters in the string are alphabetic and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isdigit()\n\n Return true if all characters in the string are digits and there is\n at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.islower()\n\n Return true if all cased characters [4] in the string are lowercase\n and there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isspace()\n\n Return true if there are only whitespace characters in the string\n and there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.istitle()\n\n Return true if the string is a titlecased string and there is at\n least one character, for example uppercase characters may only\n follow uncased characters and lowercase characters only cased ones.\n Return false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isupper()\n\n Return true if all cased characters [4] in the string are uppercase\n and there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.join(iterable)\n\n Return a string which is the concatenation of the strings in the\n *iterable* *iterable*. The separator between elements is the\n string providing this method.\n\nstr.ljust(width[, fillchar])\n\n Return the string left justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than or\n equal to ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.lower()\n\n Return a copy of the string with all the cased characters [4]\n converted to lowercase.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.lstrip([chars])\n\n Return a copy of the string with leading characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a prefix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.lstrip()\n \'spacious \'\n >>> \'www.example.com\'.lstrip(\'cmowz.\')\n \'example.com\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.partition(sep)\n\n Split the string at the first occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing the string itself, followed by\n two empty strings.\n\n New in version 2.5.\n\nstr.replace(old, new[, count])\n\n Return a copy of the string with all occurrences of substring *old*\n replaced by *new*. If the optional argument *count* is given, only\n the first *count* occurrences are replaced.\n\nstr.rfind(sub[, start[, end]])\n\n Return the highest index in the string where substring *sub* is\n found, such that *sub* is contained within ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` on failure.\n\nstr.rindex(sub[, start[, end]])\n\n Like ``rfind()`` but raises ``ValueError`` when the substring *sub*\n is not found.\n\nstr.rjust(width[, fillchar])\n\n Return the string right justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than or\n equal to ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.rpartition(sep)\n\n Split the string at the last occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing two empty strings, followed by\n the string itself.\n\n New in version 2.5.\n\nstr.rsplit([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit* splits\n are done, the *rightmost* ones. If *sep* is not specified or\n ``None``, any whitespace string is a separator. Except for\n splitting from the right, ``rsplit()`` behaves like ``split()``\n which is described in detail below.\n\n New in version 2.4.\n\nstr.rstrip([chars])\n\n Return a copy of the string with trailing characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a suffix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.rstrip()\n \' spacious\'\n >>> \'mississippi\'.rstrip(\'ipz\')\n \'mississ\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.split([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit*\n splits are done (thus, the list will have at most ``maxsplit+1``\n elements). If *maxsplit* is not specified or ``-1``, then there is\n no limit on the number of splits (all possible splits are made).\n\n If *sep* is given, consecutive delimiters are not grouped together\n and are deemed to delimit empty strings (for example,\n ``\'1,,2\'.split(\',\')`` returns ``[\'1\', \'\', \'2\']``). The *sep*\n argument may consist of multiple characters (for example,\n ``\'1<>2<>3\'.split(\'<>\')`` returns ``[\'1\', \'2\', \'3\']``). Splitting\n an empty string with a specified separator returns ``[\'\']``.\n\n If *sep* is not specified or is ``None``, a different splitting\n algorithm is applied: runs of consecutive whitespace are regarded\n as a single separator, and the result will contain no empty strings\n at the start or end if the string has leading or trailing\n whitespace. Consequently, splitting an empty string or a string\n consisting of just whitespace with a ``None`` separator returns\n ``[]``.\n\n For example, ``\' 1 2 3 \'.split()`` returns ``[\'1\', \'2\', \'3\']``,\n and ``\' 1 2 3 \'.split(None, 1)`` returns ``[\'1\', \'2 3 \']``.\n\nstr.splitlines([keepends])\n\n Return a list of the lines in the string, breaking at line\n boundaries. This method uses the *universal newlines* approach to\n splitting lines. Line breaks are not included in the resulting list\n unless *keepends* is given and true.\n\n For example, ``\'ab c\\n\\nde fg\\rkl\\r\\n\'.splitlines()`` returns\n ``[\'ab c\', \'\', \'de fg\', \'kl\']``, while the same call with\n ``splitlines(True)`` returns ``[\'ab c\\n\', \'\\n\', \'de fg\\r\',\n \'kl\\r\\n\']``.\n\n Unlike ``split()`` when a delimiter string *sep* is given, this\n method returns an empty list for the empty string, and a terminal\n line break does not result in an extra line.\n\nstr.startswith(prefix[, start[, end]])\n\n Return ``True`` if string starts with the *prefix*, otherwise\n return ``False``. *prefix* can also be a tuple of prefixes to look\n for. With optional *start*, test string beginning at that\n position. With optional *end*, stop comparing string at that\n position.\n\n Changed in version 2.5: Accept tuples as *prefix*.\n\nstr.strip([chars])\n\n Return a copy of the string with the leading and trailing\n characters removed. The *chars* argument is a string specifying the\n set of characters to be removed. If omitted or ``None``, the\n *chars* argument defaults to removing whitespace. The *chars*\n argument is not a prefix or suffix; rather, all combinations of its\n values are stripped:\n\n >>> \' spacious \'.strip()\n \'spacious\'\n >>> \'www.example.com\'.strip(\'cmowz.\')\n \'example\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.swapcase()\n\n Return a copy of the string with uppercase characters converted to\n lowercase and vice versa.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.title()\n\n Return a titlecased version of the string where words start with an\n uppercase character and the remaining characters are lowercase.\n\n The algorithm uses a simple language-independent definition of a\n word as groups of consecutive letters. The definition works in\n many contexts but it means that apostrophes in contractions and\n possessives form word boundaries, which may not be the desired\n result:\n\n >>> "they\'re bill\'s friends from the UK".title()\n "They\'Re Bill\'S Friends From The Uk"\n\n A workaround for apostrophes can be constructed using regular\n expressions:\n\n >>> import re\n >>> def titlecase(s):\n ... return re.sub(r"[A-Za-z]+(\'[A-Za-z]+)?",\n ... lambda mo: mo.group(0)[0].upper() +\n ... mo.group(0)[1:].lower(),\n ... s)\n ...\n >>> titlecase("they\'re bill\'s friends.")\n "They\'re Bill\'s Friends."\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.translate(table[, deletechars])\n\n Return a copy of the string where all characters occurring in the\n optional argument *deletechars* are removed, and the remaining\n characters have been mapped through the given translation table,\n which must be a string of length 256.\n\n You can use the ``maketrans()`` helper function in the ``string``\n module to create a translation table. For string objects, set the\n *table* argument to ``None`` for translations that only delete\n characters:\n\n >>> \'read this short text\'.translate(None, \'aeiou\')\n \'rd ths shrt txt\'\n\n New in version 2.6: Support for a ``None`` *table* argument.\n\n For Unicode objects, the ``translate()`` method does not accept the\n optional *deletechars* argument. Instead, it returns a copy of the\n *s* where all characters have been mapped through the given\n translation table which must be a mapping of Unicode ordinals to\n Unicode ordinals, Unicode strings or ``None``. Unmapped characters\n are left untouched. Characters mapped to ``None`` are deleted.\n Note, a more flexible approach is to create a custom character\n mapping codec using the ``codecs`` module (see ``encodings.cp1251``\n for an example).\n\nstr.upper()\n\n Return a copy of the string with all the cased characters [4]\n converted to uppercase. Note that ``str.upper().isupper()`` might\n be ``False`` if ``s`` contains uncased characters or if the Unicode\n category of the resulting character(s) is not "Lu" (Letter,\n uppercase), but e.g. "Lt" (Letter, titlecase).\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.zfill(width)\n\n Return the numeric string left filled with zeros in a string of\n length *width*. A sign prefix is handled correctly. The original\n string is returned if *width* is less than or equal to ``len(s)``.\n\n New in version 2.2.2.\n\nThe following methods are present only on unicode objects:\n\nunicode.isnumeric()\n\n Return ``True`` if there are only numeric characters in S,\n ``False`` otherwise. Numeric characters include digit characters,\n and all characters that have the Unicode numeric value property,\n e.g. U+2155, VULGAR FRACTION ONE FIFTH.\n\nunicode.isdecimal()\n\n Return ``True`` if there are only decimal characters in S,\n ``False`` otherwise. Decimal characters include digit characters,\n and all characters that can be used to form decimal-radix numbers,\n e.g. U+0660, ARABIC-INDIC DIGIT ZERO.\n\n\nString Formatting Operations\n============================\n\nString and Unicode objects have one unique built-in operation: the\n``%`` operator (modulo). This is also known as the string\n*formatting* or *interpolation* operator. Given ``format % values``\n(where *format* is a string or Unicode object), ``%`` conversion\nspecifications in *format* are replaced with zero or more elements of\n*values*. The effect is similar to the using ``sprintf()`` in the C\nlanguage. If *format* is a Unicode object, or if any of the objects\nbeing converted using the ``%s`` conversion are Unicode objects, the\nresult will also be a Unicode object.\n\nIf *format* requires a single argument, *values* may be a single non-\ntuple object. [5] Otherwise, *values* must be a tuple with exactly\nthe number of items specified by the format string, or a single\nmapping object (for example, a dictionary).\n\nA conversion specifier contains two or more characters and has the\nfollowing components, which must occur in this order:\n\n1. The ``\'%\'`` character, which marks the start of the specifier.\n\n2. Mapping key (optional), consisting of a parenthesised sequence of\n characters (for example, ``(somename)``).\n\n3. Conversion flags (optional), which affect the result of some\n conversion types.\n\n4. Minimum field width (optional). If specified as an ``\'*\'``\n (asterisk), the actual width is read from the next element of the\n tuple in *values*, and the object to convert comes after the\n minimum field width and optional precision.\n\n5. Precision (optional), given as a ``\'.\'`` (dot) followed by the\n precision. If specified as ``\'*\'`` (an asterisk), the actual width\n is read from the next element of the tuple in *values*, and the\n value to convert comes after the precision.\n\n6. Length modifier (optional).\n\n7. Conversion type.\n\nWhen the right argument is a dictionary (or other mapping type), then\nthe formats in the string *must* include a parenthesised mapping key\ninto that dictionary inserted immediately after the ``\'%\'`` character.\nThe mapping key selects the value to be formatted from the mapping.\nFor example:\n\n>>> print \'%(language)s has %(number)03d quote types.\' % \\\n... {"language": "Python", "number": 2}\nPython has 002 quote types.\n\nIn this case no ``*`` specifiers may occur in a format (since they\nrequire a sequential parameter list).\n\nThe conversion flag characters are:\n\n+-----------+-----------------------------------------------------------------------+\n| Flag | Meaning |\n+===========+=======================================================================+\n| ``\'#\'`` | The value conversion will use the "alternate form" (where defined |\n| | below). |\n+-----------+-----------------------------------------------------------------------+\n| ``\'0\'`` | The conversion will be zero padded for numeric values. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'-\'`` | The converted value is left adjusted (overrides the ``\'0\'`` |\n| | conversion if both are given). |\n+-----------+-----------------------------------------------------------------------+\n| ``\' \'`` | (a space) A blank should be left before a positive number (or empty |\n| | string) produced by a signed conversion. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'+\'`` | A sign character (``\'+\'`` or ``\'-\'``) will precede the conversion |\n| | (overrides a "space" flag). |\n+-----------+-----------------------------------------------------------------------+\n\nA length modifier (``h``, ``l``, or ``L``) may be present, but is\nignored as it is not necessary for Python -- so e.g. ``%ld`` is\nidentical to ``%d``.\n\nThe conversion types are:\n\n+--------------+-------------------------------------------------------+---------+\n| Conversion | Meaning | Notes |\n+==============+=======================================================+=========+\n| ``\'d\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'i\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'o\'`` | Signed octal value. | (1) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'u\'`` | Obsolete type -- it is identical to ``\'d\'``. | (7) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'x\'`` | Signed hexadecimal (lowercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'X\'`` | Signed hexadecimal (uppercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'e\'`` | Floating point exponential format (lowercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'E\'`` | Floating point exponential format (uppercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'f\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'F\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'g\'`` | Floating point format. Uses lowercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'G\'`` | Floating point format. Uses uppercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'c\'`` | Single character (accepts integer or single character | |\n| | string). | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'r\'`` | String (converts any Python object using *repr()*). | (5) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'s\'`` | String (converts any Python object using ``str()``). | (6) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'%\'`` | No argument is converted, results in a ``\'%\'`` | |\n| | character in the result. | |\n+--------------+-------------------------------------------------------+---------+\n\nNotes:\n\n1. The alternate form causes a leading zero (``\'0\'``) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n2. The alternate form causes a leading ``\'0x\'`` or ``\'0X\'`` (depending\n on whether the ``\'x\'`` or ``\'X\'`` format was used) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n3. The alternate form causes the result to always contain a decimal\n point, even if no digits follow it.\n\n The precision determines the number of digits after the decimal\n point and defaults to 6.\n\n4. The alternate form causes the result to always contain a decimal\n point, and trailing zeroes are not removed as they would otherwise\n be.\n\n The precision determines the number of significant digits before\n and after the decimal point and defaults to 6.\n\n5. The ``%r`` conversion was added in Python 2.0.\n\n The precision determines the maximal number of characters used.\n\n6. If the object or format provided is a ``unicode`` string, the\n resulting string will also be ``unicode``.\n\n The precision determines the maximal number of characters used.\n\n7. See **PEP 237**.\n\nSince Python strings have an explicit length, ``%s`` conversions do\nnot assume that ``\'\\0\'`` is the end of the string.\n\nChanged in version 2.7: ``%f`` conversions for numbers whose absolute\nvalue is over 1e50 are no longer replaced by ``%g`` conversions.\n\nAdditional string operations are defined in standard modules\n``string`` and ``re``.\n\n\nXRange Type\n===========\n\nThe ``xrange`` type is an immutable sequence which is commonly used\nfor looping. The advantage of the ``xrange`` type is that an\n``xrange`` object will always take the same amount of memory, no\nmatter the size of the range it represents. There are no consistent\nperformance advantages.\n\nXRange objects have very little behavior: they only support indexing,\niteration, and the ``len()`` function.\n\n\nMutable Sequence Types\n======================\n\nList and ``bytearray`` objects support additional operations that\nallow in-place modification of the object. Other mutable sequence\ntypes (when added to the language) should also support these\noperations. Strings and tuples are immutable sequence types: such\nobjects cannot be modified once created. The following operations are\ndefined on mutable sequence types (where *x* is an arbitrary object):\n\n+--------------------------------+----------------------------------+-----------------------+\n| Operation | Result | Notes |\n+================================+==================================+=======================+\n| ``s[i] = x`` | item *i* of *s* is replaced by | |\n| | *x* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j] = t`` | slice of *s* from *i* to *j* is | |\n| | replaced by the contents of the | |\n| | iterable *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j]`` | same as ``s[i:j] = []`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j:k] = t`` | the elements of ``s[i:j:k]`` are | (1) |\n| | replaced by those of *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j:k]`` | removes the elements of | |\n| | ``s[i:j:k]`` from the list | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.append(x)`` | same as ``s[len(s):len(s)] = | (2) |\n| | [x]`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.extend(x)`` | same as ``s[len(s):len(s)] = x`` | (3) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.count(x)`` | return number of *i*\'s for which | |\n| | ``s[i] == x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.index(x[, i[, j]])`` | return smallest *k* such that | (4) |\n| | ``s[k] == x`` and ``i <= k < j`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.insert(i, x)`` | same as ``s[i:i] = [x]`` | (5) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.pop([i])`` | same as ``x = s[i]; del s[i]; | (6) |\n| | return x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.remove(x)`` | same as ``del s[s.index(x)]`` | (4) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.reverse()`` | reverses the items of *s* in | (7) |\n| | place | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.sort([cmp[, key[, | sort the items of *s* in place | (7)(8)(9)(10) |\n| reverse]]])`` | | |\n+--------------------------------+----------------------------------+-----------------------+\n\nNotes:\n\n1. *t* must have the same length as the slice it is replacing.\n\n2. The C implementation of Python has historically accepted multiple\n parameters and implicitly joined them into a tuple; this no longer\n works in Python 2.0. Use of this misfeature has been deprecated\n since Python 1.4.\n\n3. *x* can be any iterable object.\n\n4. Raises ``ValueError`` when *x* is not found in *s*. When a negative\n index is passed as the second or third parameter to the ``index()``\n method, the list length is added, as for slice indices. If it is\n still negative, it is truncated to zero, as for slice indices.\n\n Changed in version 2.3: Previously, ``index()`` didn\'t have\n arguments for specifying start and stop positions.\n\n5. When a negative index is passed as the first parameter to the\n ``insert()`` method, the list length is added, as for slice\n indices. If it is still negative, it is truncated to zero, as for\n slice indices.\n\n Changed in version 2.3: Previously, all negative indices were\n truncated to zero.\n\n6. The ``pop()`` method\'s optional argument *i* defaults to ``-1``, so\n that by default the last item is removed and returned.\n\n7. The ``sort()`` and ``reverse()`` methods modify the list in place\n for economy of space when sorting or reversing a large list. To\n remind you that they operate by side effect, they don\'t return the\n sorted or reversed list.\n\n8. The ``sort()`` method takes optional arguments for controlling the\n comparisons.\n\n *cmp* specifies a custom comparison function of two arguments (list\n items) which should return a negative, zero or positive number\n depending on whether the first argument is considered smaller than,\n equal to, or larger than the second argument: ``cmp=lambda x,y:\n cmp(x.lower(), y.lower())``. The default value is ``None``.\n\n *key* specifies a function of one argument that is used to extract\n a comparison key from each list element: ``key=str.lower``. The\n default value is ``None``.\n\n *reverse* is a boolean value. If set to ``True``, then the list\n elements are sorted as if each comparison were reversed.\n\n In general, the *key* and *reverse* conversion processes are much\n faster than specifying an equivalent *cmp* function. This is\n because *cmp* is called multiple times for each list element while\n *key* and *reverse* touch each element only once. Use\n ``functools.cmp_to_key()`` to convert an old-style *cmp* function\n to a *key* function.\n\n Changed in version 2.3: Support for ``None`` as an equivalent to\n omitting *cmp* was added.\n\n Changed in version 2.4: Support for *key* and *reverse* was added.\n\n9. Starting with Python 2.3, the ``sort()`` method is guaranteed to be\n stable. A sort is stable if it guarantees not to change the\n relative order of elements that compare equal --- this is helpful\n for sorting in multiple passes (for example, sort by department,\n then by salary grade).\n\n10. **CPython implementation detail:** While a list is being sorted,\n the effect of attempting to mutate, or even inspect, the list is\n undefined. The C implementation of Python 2.3 and newer makes the\n list appear empty for the duration, and raises ``ValueError`` if\n it can detect that the list has been mutated during a sort.\n', -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 04:03:12 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 30 Jun 2014 04:03:12 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogYnVtcCB0byAyLjcu?= =?utf-8?q?8?= Message-ID: <3h1sRh70wQz7Ljy@mail.python.org> http://hg.python.org/cpython/rev/ee879c0ffa11 changeset: 91474:ee879c0ffa11 branch: 2.7 tag: v2.7.8 user: Benjamin Peterson date: Sun Jun 29 18:58:16 2014 -0700 summary: bump to 2.7.8 files: Include/patchlevel.h | 4 ++-- Lib/distutils/__init__.py | 2 +- Lib/idlelib/idlever.py | 2 +- Misc/NEWS | 2 +- Misc/RPM/python-2.7.spec | 2 +- README | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -22,12 +22,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 2 #define PY_MINOR_VERSION 7 -#define PY_MICRO_VERSION 7 +#define PY_MICRO_VERSION 8 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "2.7.7+" +#define PY_VERSION "2.7.8" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository). Empty diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py --- a/Lib/distutils/__init__.py +++ b/Lib/distutils/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.7" +__version__ = "2.7.8" #--end constants-- diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1,1 +1,1 @@ -IDLE_VERSION = "2.7.7" +IDLE_VERSION = "2.7.8" diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -5,7 +5,7 @@ What's New in Python 2.7.8? =========================== -*Release date: XXXX-XX-XX* +*Release date: 2014-06-29* Core and Builtins ----------------- diff --git a/Misc/RPM/python-2.7.spec b/Misc/RPM/python-2.7.spec --- a/Misc/RPM/python-2.7.spec +++ b/Misc/RPM/python-2.7.spec @@ -39,7 +39,7 @@ %define name python #--start constants-- -%define version 2.7.7 +%define version 2.7.8 %define libvers 2.7 #--end constants-- %define release 1pydotorg diff --git a/README b/README --- a/README +++ b/README @@ -1,4 +1,4 @@ -This is Python version 2.7.7 +This is Python version 2.7.8 ============================ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 04:03:14 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 30 Jun 2014 04:03:14 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Added_tag_v2?= =?utf-8?q?=2E7=2E8_for_changeset_ee879c0ffa11?= Message-ID: <3h1sRk1bNpz7Ljy@mail.python.org> http://hg.python.org/cpython/rev/f4664567d904 changeset: 91475:f4664567d904 branch: 2.7 user: Benjamin Peterson date: Sun Jun 29 18:58:31 2014 -0700 summary: Added tag v2.7.8 for changeset ee879c0ffa11 files: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -165,3 +165,4 @@ 3a1db0d2747ec2d47a8693ed5650f3567161a200 v2.7.6 e32e3a9f390212463c22509d0f9aead8051cee63 v2.7.7rc1 f89216059edf77660ef1eb2a98e88352551da1d6 v2.7.7 +ee879c0ffa11caaa34bf01537e1c4411dd948552 v2.7.8 -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 04:03:15 2014 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 30 Jun 2014 04:03:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogYWZ0ZXIgMi43Ljg=?= Message-ID: <3h1sRl3HxCz7Lk0@mail.python.org> http://hg.python.org/cpython/rev/ab16cc90873a changeset: 91476:ab16cc90873a branch: 2.7 user: Benjamin Peterson date: Sun Jun 29 18:59:07 2014 -0700 summary: after 2.7.8 files: Include/patchlevel.h | 2 +- Misc/NEWS | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -27,7 +27,7 @@ #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "2.7.8" +#define PY_VERSION "2.7.8+" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository). Empty diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -2,6 +2,18 @@ Python News +++++++++++ +What's New in Python 2.7.9? +========================== + +*Release date: XXXX-XX-XX* + +Core and Builtins +----------------- + +Library +------- + + What's New in Python 2.7.8? =========================== -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 08:56:48 2014 From: python-checkins at python.org (ned.deily) Date: Mon, 30 Jun 2014 08:56:48 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxODEx?= =?utf-8?q?=3A_Add_Misc/NEWS_entry=2E?= Message-ID: <3h1zyS4sdZz7LjW@mail.python.org> http://hg.python.org/cpython/rev/53112afddae6 changeset: 91477:53112afddae6 branch: 2.7 user: Ned Deily date: Sun Jun 29 23:38:55 2014 -0700 summary: Issue #21811: Add Misc/NEWS entry. files: Misc/NEWS | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -152,6 +152,11 @@ - Issue #20635: Added tests for Tk geometry managers. +Build +----- + +- Issue #21811: Anticipated fixes to support OS X versions > 10.9. + Windows ------- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 08:56:49 2014 From: python-checkins at python.org (ned.deily) Date: Mon, 30 Jun 2014 08:56:49 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxODEx?= =?utf-8?q?=3A_Add_Misc/NEWS_entry=2E?= Message-ID: <3h1zyT6J8fz7LkJ@mail.python.org> http://hg.python.org/cpython/rev/ec27c85d3001 changeset: 91478:ec27c85d3001 branch: 3.4 parent: 91471:d943089af1c6 user: Ned Deily date: Sun Jun 29 23:51:55 2014 -0700 summary: Issue #21811: Add Misc/NEWS entry. files: Misc/NEWS | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -130,6 +130,8 @@ - Issue #17095: Fix Modules/Setup *shared* support. +- Issue #21811: Anticipated fixes to support OS X versions > 10.9. + IDLE ---- -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 08:56:51 2014 From: python-checkins at python.org (ned.deily) Date: Mon, 30 Jun 2014 08:56:51 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321811=3A_Add_Misc/NEWS_entry=2E?= Message-ID: <3h1zyW0YhMz7LkN@mail.python.org> http://hg.python.org/cpython/rev/1f59baf609a4 changeset: 91479:1f59baf609a4 parent: 91472:10a1e7780ee7 parent: 91478:ec27c85d3001 user: Ned Deily date: Sun Jun 29 23:54:38 2014 -0700 summary: Issue #21811: Add Misc/NEWS entry. files: Misc/NEWS | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -600,6 +600,8 @@ - Issue #17095: Fix Modules/Setup *shared* support. +- Issue #21811: Anticipated fixes to support OS X versions > 10.9. + C API ----- - Issue #20942: PyImport_ImportFrozenModuleObject() no longer sets __file__ to -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 10:29:59 2014 From: python-checkins at python.org (berker.peksag) Date: Mon, 30 Jun 2014 10:29:59 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fix_typo_in_so?= =?utf-8?q?cket=2Egetaddrinfo=28=29_docstring=2E?= Message-ID: <3h221z431Jz7LjS@mail.python.org> http://hg.python.org/cpython/rev/4c8fc852244a changeset: 91480:4c8fc852244a branch: 3.4 parent: 91478:ec27c85d3001 user: Berker Peksag date: Mon Jun 30 11:28:40 2014 +0300 summary: Fix typo in socket.getaddrinfo() docstring. Reported by Krishna Kumar Thakur on docs at . files: Modules/socketmodule.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -33,8 +33,8 @@ - socket.ntohl(32 bit value) --> new int object - socket.htons(16 bit value) --> new int object - socket.htonl(32 bit value) --> new int object -- socket.getaddrinfo(host, port [, family, socktype, proto, flags]) - --> List of (family, socktype, proto, canonname, sockaddr) +- socket.getaddrinfo(host, port [, family, type, proto, flags]) + --> List of (family, type, proto, canonname, sockaddr) - socket.getnameinfo(sockaddr, flags) --> (host, port) - socket.AF_INET, socket.SOCK_STREAM, etc.: constants from - socket.has_ipv6: boolean value indicating if IPv6 is supported @@ -5292,8 +5292,8 @@ } PyDoc_STRVAR(getaddrinfo_doc, -"getaddrinfo(host, port [, family, socktype, proto, flags])\n\ - -> list of (family, socktype, proto, canonname, sockaddr)\n\ +"getaddrinfo(host, port [, family, type, proto, flags])\n\ + -> list of (family, type, proto, canonname, sockaddr)\n\ \n\ Resolve host and port into addrinfo struct."); -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 10:30:00 2014 From: python-checkins at python.org (berker.peksag) Date: Mon, 30 Jun 2014 10:30:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Fix_typo_in_socket=2Egetaddrinfo=28=29_docstring=2E?= Message-ID: <3h22205hv1z7Ljy@mail.python.org> http://hg.python.org/cpython/rev/3086709101ff changeset: 91481:3086709101ff parent: 91479:1f59baf609a4 parent: 91480:4c8fc852244a user: Berker Peksag date: Mon Jun 30 11:30:00 2014 +0300 summary: Fix typo in socket.getaddrinfo() docstring. Reported by Krishna Kumar Thakur on docs at . files: Modules/socketmodule.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -33,8 +33,8 @@ - socket.ntohl(32 bit value) --> new int object - socket.htons(16 bit value) --> new int object - socket.htonl(32 bit value) --> new int object -- socket.getaddrinfo(host, port [, family, socktype, proto, flags]) - --> List of (family, socktype, proto, canonname, sockaddr) +- socket.getaddrinfo(host, port [, family, type, proto, flags]) + --> List of (family, type, proto, canonname, sockaddr) - socket.getnameinfo(sockaddr, flags) --> (host, port) - socket.AF_INET, socket.SOCK_STREAM, etc.: constants from - socket.has_ipv6: boolean value indicating if IPv6 is supported @@ -5290,8 +5290,8 @@ } PyDoc_STRVAR(getaddrinfo_doc, -"getaddrinfo(host, port [, family, socktype, proto, flags])\n\ - -> list of (family, socktype, proto, canonname, sockaddr)\n\ +"getaddrinfo(host, port [, family, type, proto, flags])\n\ + -> list of (family, type, proto, canonname, sockaddr)\n\ \n\ Resolve host and port into addrinfo struct."); -- Repository URL: http://hg.python.org/cpython From solipsis at pitrou.net Mon Jun 30 10:51:07 2014 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 30 Jun 2014 10:51:07 +0200 Subject: [Python-checkins] Daily reference leaks (10a1e7780ee7): sum=11 Message-ID: results for 10a1e7780ee7 on branch "default" -------------------------------------------- test_collections leaked [0, 2, 0] references, sum=2 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_io leaked [2, 2, 2] references, sum=6 test_site leaked [0, 2, -2] references, sum=0 test_site leaked [0, 2, -2] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogW8rsIQ', '-x'] From python-checkins at python.org Mon Jun 30 12:33:34 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 30 Jun 2014 12:33:34 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321645=3A_asyncio?= =?utf-8?q?=3A_add_a_watchdog_in_test=5Fread=5Fall=5Ffrom=5Fpipe=5Freader?= =?utf-8?b?KCkgZm9y?= Message-ID: <3h24mZ0sfhz7LjT@mail.python.org> http://hg.python.org/cpython/rev/69d474dab479 changeset: 91482:69d474dab479 user: Victor Stinner date: Mon Jun 30 12:32:59 2014 +0200 summary: Issue #21645: asyncio: add a watchdog in test_read_all_from_pipe_reader() for debug files: Lib/test/test_asyncio/test_streams.py | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) 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 @@ -596,6 +596,12 @@ code = """\ import os, sys +try: + import faulthandler +except ImportError: + pass +else: + faulthandler.dump_traceback_later(60, exit=True) fd = int(sys.argv[1]) os.write(fd, b'data') os.close(fd) -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 14:41:00 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 30 Jun 2014 14:41:00 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogc3lu?= =?utf-8?q?c_with_Tulip?= Message-ID: <3h27bc6391z7Lk1@mail.python.org> http://hg.python.org/cpython/rev/defd09a5339a changeset: 91483:defd09a5339a branch: 3.4 parent: 91480:4c8fc852244a user: Victor Stinner date: Mon Jun 30 14:39:11 2014 +0200 summary: asyncio: sync with Tulip - Sort imports - Simplify/optimize iscoroutine(). Inline inspect.isgenerator(obj): replace it with isinstance(obj, types.GeneratorType) - CoroWrapper: check at runtime if Python has the yield-from bug #21209. If Python has the bug, check if CoroWrapper.send() was called by yield-from to decide if parameters must be unpacked or not. - Fix "Task was destroyed but it is pending!" warning in test_task_source_traceback() files: Lib/asyncio/base_events.py | 4 +- Lib/asyncio/coroutines.py | 57 ++++++++++++++-- Lib/test/test_asyncio/test_tasks.py | 1 + 3 files changed, 52 insertions(+), 10 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 @@ -19,11 +19,11 @@ import heapq import inspect import logging +import os import socket import subprocess +import time import traceback -import time -import os import sys from . import coroutines diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -3,14 +3,20 @@ import functools import inspect +import opcode import os import sys import traceback +import types from . import events from . import futures from .log import logger + +# Opcode of "yield from" instruction +_YIELD_FROM = opcode.opmap['YIELD_FROM'] + # If you set _DEBUG to true, @coroutine will wrap the resulting # generator objects in a CoroWrapper instance (defined below). That # instance will log a message when the generator is never iterated @@ -25,6 +31,31 @@ _PY35 = (sys.version_info >= (3, 5)) + +# Check for CPython issue #21209 +def has_yield_from_bug(): + class MyGen: + def __init__(self): + self.send_args = None + def __iter__(self): + return self + def __next__(self): + return 42 + def send(self, *what): + self.send_args = what + return None + def yield_from_gen(gen): + yield from gen + value = (1, 2, 3) + gen = MyGen() + coro = yield_from_gen(gen) + next(coro) + coro.send(value) + return gen.send_args != (value,) +_YIELD_FROM_BUG = has_yield_from_bug() +del has_yield_from_bug + + class CoroWrapper: # Wrapper for coroutine in _DEBUG mode. @@ -40,13 +71,21 @@ def __next__(self): return next(self.gen) - def send(self, *value): - # We use `*value` because of a bug in CPythons prior - # to 3.4.1. See issue #21209 and test_yield_from_corowrapper - # for details. This workaround should be removed in 3.5.0. - if len(value) == 1: - value = value[0] - return self.gen.send(value) + if _YIELD_FROM_BUG: + # For for CPython issue #21209: using "yield from" and a custom + # generator, generator.send(tuple) unpacks the tuple instead of passing + # the tuple unchanged. Check if the caller is a generator using "yield + # from" to decide if the parameter should be unpacked or not. + def send(self, *value): + frame = sys._getframe() + caller = frame.f_back + assert caller.f_lasti >= 0 + if caller.f_code.co_code[caller.f_lasti] != _YIELD_FROM: + value = value[0] + return self.gen.send(value) + else: + def send(self, value): + return self.gen.send(value) def throw(self, exc): return self.gen.throw(exc) @@ -119,9 +158,11 @@ return getattr(func, '_is_coroutine', False) +_COROUTINE_TYPES = (CoroWrapper, types.GeneratorType) + def iscoroutine(obj): """Return True if obj is a coroutine object.""" - return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj) + return isinstance(obj, _COROUTINE_TYPES) def _format_coroutine(coro): 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 @@ -1621,6 +1621,7 @@ (__file__, lineno, 'test_task_source_traceback')) + self.loop.run_until_complete(task) class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 14:41:02 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 30 Jun 2014 14:41:02 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_asyncio=3A_sync_with_Tulip?= Message-ID: <3h27bf1Pjlz7Lkj@mail.python.org> http://hg.python.org/cpython/rev/8dc8c93e74c9 changeset: 91484:8dc8c93e74c9 parent: 91482:69d474dab479 parent: 91483:defd09a5339a user: Victor Stinner date: Mon Jun 30 14:39:47 2014 +0200 summary: asyncio: sync with Tulip - Sort imports - Simplify/optimize iscoroutine(). Inline inspect.isgenerator(obj): replace it with isinstance(obj, types.GeneratorType) - CoroWrapper: check at runtime if Python has the yield-from bug #21209. If Python has the bug, check if CoroWrapper.send() was called by yield-from to decide if parameters must be unpacked or not. - Fix "Task was destroyed but it is pending!" warning in test_task_source_traceback() files: Lib/asyncio/base_events.py | 4 +- Lib/asyncio/coroutines.py | 57 ++++++++++++++-- Lib/test/test_asyncio/test_tasks.py | 1 + 3 files changed, 52 insertions(+), 10 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 @@ -19,11 +19,11 @@ import heapq import inspect import logging +import os import socket import subprocess +import time import traceback -import time -import os import sys from . import coroutines diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -3,14 +3,20 @@ import functools import inspect +import opcode import os import sys import traceback +import types from . import events from . import futures from .log import logger + +# Opcode of "yield from" instruction +_YIELD_FROM = opcode.opmap['YIELD_FROM'] + # If you set _DEBUG to true, @coroutine will wrap the resulting # generator objects in a CoroWrapper instance (defined below). That # instance will log a message when the generator is never iterated @@ -25,6 +31,31 @@ _PY35 = (sys.version_info >= (3, 5)) + +# Check for CPython issue #21209 +def has_yield_from_bug(): + class MyGen: + def __init__(self): + self.send_args = None + def __iter__(self): + return self + def __next__(self): + return 42 + def send(self, *what): + self.send_args = what + return None + def yield_from_gen(gen): + yield from gen + value = (1, 2, 3) + gen = MyGen() + coro = yield_from_gen(gen) + next(coro) + coro.send(value) + return gen.send_args != (value,) +_YIELD_FROM_BUG = has_yield_from_bug() +del has_yield_from_bug + + class CoroWrapper: # Wrapper for coroutine in _DEBUG mode. @@ -40,13 +71,21 @@ def __next__(self): return next(self.gen) - def send(self, *value): - # We use `*value` because of a bug in CPythons prior - # to 3.4.1. See issue #21209 and test_yield_from_corowrapper - # for details. This workaround should be removed in 3.5.0. - if len(value) == 1: - value = value[0] - return self.gen.send(value) + if _YIELD_FROM_BUG: + # For for CPython issue #21209: using "yield from" and a custom + # generator, generator.send(tuple) unpacks the tuple instead of passing + # the tuple unchanged. Check if the caller is a generator using "yield + # from" to decide if the parameter should be unpacked or not. + def send(self, *value): + frame = sys._getframe() + caller = frame.f_back + assert caller.f_lasti >= 0 + if caller.f_code.co_code[caller.f_lasti] != _YIELD_FROM: + value = value[0] + return self.gen.send(value) + else: + def send(self, value): + return self.gen.send(value) def throw(self, exc): return self.gen.throw(exc) @@ -119,9 +158,11 @@ return getattr(func, '_is_coroutine', False) +_COROUTINE_TYPES = (CoroWrapper, types.GeneratorType) + def iscoroutine(obj): """Return True if obj is a coroutine object.""" - return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj) + return isinstance(obj, _COROUTINE_TYPES) def _format_coroutine(coro): 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 @@ -1621,6 +1621,7 @@ (__file__, lineno, 'test_task_source_traceback')) + self.loop.run_until_complete(task) class GatherTestsBase: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 14:53:53 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 30 Jun 2014 14:53:53 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxMTYz?= =?utf-8?q?=3A_BaseEventLoop=2Erun=5Funtil=5Fcomplete=28=29_and_test=5Futi?= =?utf-8?q?ls=2Erun=5Fbriefly=28=29?= Message-ID: <3h27tT5B47z7LkJ@mail.python.org> http://hg.python.org/cpython/rev/13e78b9cf290 changeset: 91485:13e78b9cf290 branch: 3.4 parent: 91483:defd09a5339a user: Victor Stinner date: Mon Jun 30 14:51:04 2014 +0200 summary: Issue #21163: BaseEventLoop.run_until_complete() and test_utils.run_briefly() don't log the "destroy pending task" message anymore. The log is redundant for run_until_complete() and useless in run_briefly(). files: Lib/asyncio/base_events.py | 7 +++++++ Lib/asyncio/tasks.py | 5 ++++- Lib/asyncio/test_utils.py | 3 +++ 3 files changed, 14 insertions(+), 1 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 @@ -227,7 +227,14 @@ Return the Future's result, or raise its exception. """ self._check_closed() + + new_task = not isinstance(future, futures.Future) future = tasks.async(future, loop=self) + if new_task: + # An exception is raised if the future didn't complete, so there + # is no need to log the "destroy pending task" message + future._log_destroy_pending = False + future.add_done_callback(_raise_stop_error) self.run_forever() future.remove_done_callback(_raise_stop_error) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -75,13 +75,16 @@ self._must_cancel = False self._loop.call_soon(self._step) self.__class__._all_tasks.add(self) + # If False, don't log a message if the task is destroyed whereas its + # status is still pending + self._log_destroy_pending = True # On Python 3.3 or older, objects with a destructor part of a reference # cycle are never destroyed. It's not more the case on Python 3.4 thanks to # the PEP 442. if _PY34: def __del__(self): - if self._state == futures._PENDING: + if self._state == futures._PENDING and self._log_destroy_pending: context = { 'task': self, 'message': 'Task was destroyed but it is pending!', 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 @@ -49,6 +49,9 @@ pass gen = once() t = tasks.Task(gen, loop=loop) + # Don't log a warning if the task is not done after run_until_complete(). + # It occurs if the loop is stopped or if a task raises a BaseException. + t._log_destroy_pending = False try: loop.run_until_complete(t) finally: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 14:53:54 2014 From: python-checkins at python.org (victor.stinner) Date: Mon, 30 Jun 2014 14:53:54 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_=28Merge_3=2E4=29_Issue_=2321163=3A_BaseEventLoop=2Erun?= =?utf-8?b?X3VudGlsX2NvbXBsZXRlKCkgYW5k?= Message-ID: <3h27tV6zyVz7Ll8@mail.python.org> http://hg.python.org/cpython/rev/2d0fa8f383c8 changeset: 91486:2d0fa8f383c8 parent: 91484:8dc8c93e74c9 parent: 91485:13e78b9cf290 user: Victor Stinner date: Mon Jun 30 14:51:24 2014 +0200 summary: (Merge 3.4) Issue #21163: BaseEventLoop.run_until_complete() and test_utils.run_briefly() don't log the "destroy pending task" message anymore. The log is redundant for run_until_complete() and useless in run_briefly(). files: Lib/asyncio/base_events.py | 7 +++++++ Lib/asyncio/tasks.py | 5 ++++- Lib/asyncio/test_utils.py | 3 +++ 3 files changed, 14 insertions(+), 1 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 @@ -227,7 +227,14 @@ Return the Future's result, or raise its exception. """ self._check_closed() + + new_task = not isinstance(future, futures.Future) future = tasks.async(future, loop=self) + if new_task: + # An exception is raised if the future didn't complete, so there + # is no need to log the "destroy pending task" message + future._log_destroy_pending = False + future.add_done_callback(_raise_stop_error) self.run_forever() future.remove_done_callback(_raise_stop_error) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -75,13 +75,16 @@ self._must_cancel = False self._loop.call_soon(self._step) self.__class__._all_tasks.add(self) + # If False, don't log a message if the task is destroyed whereas its + # status is still pending + self._log_destroy_pending = True # On Python 3.3 or older, objects with a destructor part of a reference # cycle are never destroyed. It's not more the case on Python 3.4 thanks to # the PEP 442. if _PY34: def __del__(self): - if self._state == futures._PENDING: + if self._state == futures._PENDING and self._log_destroy_pending: context = { 'task': self, 'message': 'Task was destroyed but it is pending!', 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 @@ -49,6 +49,9 @@ pass gen = once() t = tasks.Task(gen, loop=loop) + # Don't log a warning if the task is not done after run_until_complete(). + # It occurs if the loop is stopped or if a task raises a BaseException. + t._log_destroy_pending = False try: loop.run_until_complete(t) finally: -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 22:10:15 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 30 Jun 2014 22:10:15 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIxODgy?= =?utf-8?q?=3A_In_turtle_demos=2C_remove_module_scope_gui_and_sys_calls_by?= Message-ID: <3h2KYz1M1jz7LjW@mail.python.org> http://hg.python.org/cpython/rev/c173a34f20c0 changeset: 91487:c173a34f20c0 branch: 2.7 parent: 91477:53112afddae6 user: Terry Jan Reedy date: Mon Jun 30 16:09:16 2014 -0400 summary: Issue #21882: In turtle demos, remove module scope gui and sys calls by moving them to the module's main function. files: Demo/turtle/demohelp.txt | 3 + Demo/turtle/tdemo_clock.py | 8 +- Demo/turtle/tdemo_minimal_hanoi.py | 1 - Demo/turtle/tdemo_nim.py | 27 +++--- Demo/turtle/tdemo_two_canvases.py | 54 ++++++++++++++ Demo/turtle/turtledemo_two_canvases.py | 49 ------------ Lib/lib-tk/turtle.py | 2 +- 7 files changed, 73 insertions(+), 71 deletions(-) diff --git a/Demo/turtle/demohelp.txt b/Demo/turtle/demohelp.txt --- a/Demo/turtle/demohelp.txt +++ b/Demo/turtle/demohelp.txt @@ -52,6 +52,9 @@ (2) How to add your own demos to the demo repository + IMPORTANT! When imported, the demo should not modify the system + by calling functions in other modules, such as sys, tkinter, or + turtle. Global variables should be initialized in main(). - The script name must begin with tdemo_ , so it must have the form tdemo_.py diff --git a/Demo/turtle/tdemo_clock.py b/Demo/turtle/tdemo_clock.py --- a/Demo/turtle/tdemo_clock.py +++ b/Demo/turtle/tdemo_clock.py @@ -11,11 +11,8 @@ ------------------------------------ """ from turtle import * -from turtle import Terminator # not in __all__ from datetime import datetime -mode("logo") - def jump(distanz, winkel=0): penup() right(winkel) @@ -43,7 +40,6 @@ hand_form = get_poly() register_shape(name, hand_form) - def clockface(radius): reset() pensize(7) @@ -84,7 +80,6 @@ writer.pu() writer.bk(85) - def wochentag(t): wochentag = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] @@ -131,6 +126,7 @@ return "EVENTLOOP" if __name__ == "__main__": + mode("logo") msg = main() print msg - mainloop() + mainloop() # keep window open diff --git a/Demo/turtle/tdemo_minimal_hanoi.py b/Demo/turtle/tdemo_minimal_hanoi.py --- a/Demo/turtle/tdemo_minimal_hanoi.py +++ b/Demo/turtle/tdemo_minimal_hanoi.py @@ -18,7 +18,6 @@ --------------------------------------- """ from turtle import * -from turtle import Terminator # not in __all__ class Disc(Turtle): def __init__(self, n): diff --git a/Demo/turtle/tdemo_nim.py b/Demo/turtle/tdemo_nim.py --- a/Demo/turtle/tdemo_nim.py +++ b/Demo/turtle/tdemo_nim.py @@ -1,7 +1,7 @@ """ turtle-example-suite: tdemo_nim.py - + Play nim against the computer. The player who takes the last stick is the winner. @@ -41,7 +41,7 @@ return move def randommove(state): - m = max(state) + m = max(state) while True: z = random.randint(0,2) if state[z] > (m > 1): @@ -62,7 +62,7 @@ self.winner = None self.game.view.setup() self.game.state = Nim.RUNNING - + def move(self, row, col): maxspalte = self.sticks[row] self.sticks[row] = col @@ -76,7 +76,7 @@ row, col = computerzug(self.sticks) self.move(row, col) self.player = 0 - + def game_over(self): return self.sticks == [0, 0, 0] @@ -100,13 +100,13 @@ self.goto(x,y) self.color("white") self.showturtle() - + def coords(self, row, col): packet, remainder = divmod(col, 5) x = (3 + 11 * packet + 2 * remainder) * WUNIT y = (2 + 3 * row) * HUNIT return x - SCREENWIDTH // 2 + WUNIT // 2, SCREENHEIGHT // 2 - y - HUNIT // 2 - + def makemove(self, x, y): if self.game.state != Nim.RUNNING: return @@ -142,7 +142,7 @@ self.writer.pencolor("black") self.writer.write(msg1, align="center", font=("Courier",14,"bold")) self.screen.tracer(True) - + def setup(self): self.screen.tracer(False) @@ -181,6 +181,7 @@ if self.game.state == Nim.OVER: self.screen.clear() + class NimController(object): def __init__(self, game): @@ -200,28 +201,26 @@ self.BUSY = True self.game.model.notify_move(row, col) self.BUSY = False - + class Nim(object): CREATED = 0 RUNNING = 1 OVER = 2 def __init__(self, screen): - self.state = Nim.CREATED + self.state = Nim.CREATED self.screen = screen self.model = NimModel(self) self.view = NimView(self) self.controller = NimController(self) - -mainscreen = turtle.Screen() -mainscreen.mode("standard") -mainscreen.setup(SCREENWIDTH, SCREENHEIGHT) def main(): + mainscreen = turtle.Screen() + mainscreen.mode("standard") + mainscreen.setup(SCREENWIDTH, SCREENHEIGHT) nim = Nim(mainscreen) return "EVENTLOOP!" if __name__ == "__main__": main() turtle.mainloop() - diff --git a/Demo/turtle/tdemo_two_canvases.py b/Demo/turtle/tdemo_two_canvases.py new file mode 100644 --- /dev/null +++ b/Demo/turtle/tdemo_two_canvases.py @@ -0,0 +1,54 @@ +"""turtledemo.two_canvases + +Use TurtleScreen and RawTurtle to draw on two +distinct canvases in a separate windows. The +new window must be separately closed in +addition to pressing the STOP button. +""" + +from turtle import TurtleScreen, RawTurtle, TK + +def main(): + root = TK.Tk() + cv1 = TK.Canvas(root, width=300, height=200, bg="#ddffff") + cv2 = TK.Canvas(root, width=300, height=200, bg="#ffeeee") + cv1.pack() + cv2.pack() + + s1 = TurtleScreen(cv1) + s1.bgcolor(0.85, 0.85, 1) + s2 = TurtleScreen(cv2) + s2.bgcolor(1, 0.85, 0.85) + + p = RawTurtle(s1) + q = RawTurtle(s2) + + p.color("red", (1, 0.85, 0.85)) + p.width(3) + q.color("blue", (0.85, 0.85, 1)) + q.width(3) + + for t in p,q: + t.shape("turtle") + t.lt(36) + + q.lt(180) + + for t in p, q: + t.begin_fill() + for i in range(5): + for t in p, q: + t.fd(50) + t.lt(72) + for t in p,q: + t.end_fill() + t.lt(54) + t.pu() + t.bk(50) + + return "EVENTLOOP" + + +if __name__ == '__main__': + main() + TK.mainloop() # keep window open until user closes it diff --git a/Demo/turtle/turtledemo_two_canvases.py b/Demo/turtle/turtledemo_two_canvases.py deleted file mode 100755 --- a/Demo/turtle/turtledemo_two_canvases.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python -## DEMONSTRATES USE OF 2 CANVASES, SO CANNOT BE RUN IN DEMOVIEWER! -"""turtle example: Using TurtleScreen and RawTurtle -for drawing on two distinct canvases. -""" -from turtle import TurtleScreen, RawTurtle, TK - -root = TK.Tk() -cv1 = TK.Canvas(root, width=300, height=200, bg="#ddffff") -cv2 = TK.Canvas(root, width=300, height=200, bg="#ffeeee") -cv1.pack() -cv2.pack() - -s1 = TurtleScreen(cv1) -s1.bgcolor(0.85, 0.85, 1) -s2 = TurtleScreen(cv2) -s2.bgcolor(1, 0.85, 0.85) - -p = RawTurtle(s1) -q = RawTurtle(s2) - -p.color("red", "white") -p.width(3) -q.color("blue", "black") -q.width(3) - -for t in p,q: - t.shape("turtle") - t.lt(36) - -q.lt(180) - -for i in range(5): - for t in p, q: - t.fd(50) - t.lt(72) -for t in p,q: - t.lt(54) - t.pu() - t.bk(50) - -## Want to get some info? - -print s1, s2 -print p, q -print s1.turtles() -print s2.turtles() - -TK.mainloop() diff --git a/Lib/lib-tk/turtle.py b/Lib/lib-tk/turtle.py --- a/Lib/lib-tk/turtle.py +++ b/Lib/lib-tk/turtle.py @@ -142,7 +142,7 @@ 'log10', 'modf', 'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh'] __all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions + - _tg_utilities + _math_functions) + _tg_utilities + ['Terminator'] + _math_functions) _alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos', 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st', -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 22:10:16 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 30 Jun 2014 22:10:16 +0200 (CEST) Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxODgy?= =?utf-8?q?=3A_In_turtle_demos=2C_remove_module_scope_gui_and_sys_calls_by?= Message-ID: <3h2KZ03qFPz7Ljy@mail.python.org> http://hg.python.org/cpython/rev/fcfa9c5a00fd changeset: 91488:fcfa9c5a00fd branch: 3.4 parent: 91485:13e78b9cf290 user: Terry Jan Reedy date: Mon Jun 30 16:09:24 2014 -0400 summary: Issue #21882: In turtle demos, remove module scope gui and sys calls by either deleting or moving to the module's main function. files: Lib/turtle.py | 2 +- Lib/turtledemo/clock.py | 6 +- Lib/turtledemo/colormixer.py | 2 - Lib/turtledemo/demohelp.txt | 5 +- Lib/turtledemo/minimal_hanoi.py | 1 - Lib/turtledemo/nim.py | 10 +- Lib/turtledemo/two_canvases.py | 80 +++++++++----------- 7 files changed, 48 insertions(+), 58 deletions(-) diff --git a/Lib/turtle.py b/Lib/turtle.py --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -140,7 +140,7 @@ _tg_utilities = ['write_docstringdict', 'done'] __all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions + - _tg_utilities) # + _math_functions) + _tg_utilities + ['Terminator']) # + _math_functions) _alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos', 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st', diff --git a/Lib/turtledemo/clock.py b/Lib/turtledemo/clock.py --- a/Lib/turtledemo/clock.py +++ b/Lib/turtledemo/clock.py @@ -11,11 +11,8 @@ ------------------------------------ """ from turtle import * -from turtle import Terminator # not in __all__ from datetime import datetime -mode("logo") - def jump(distanz, winkel=0): penup() right(winkel) @@ -43,7 +40,6 @@ hand_form = get_poly() register_shape(name, hand_form) - def clockface(radius): reset() pensize(7) @@ -84,7 +80,6 @@ writer.pu() writer.bk(85) - def wochentag(t): wochentag = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] @@ -131,6 +126,7 @@ return "EVENTLOOP" if __name__ == "__main__": + mode("logo") msg = main() print(msg) mainloop() diff --git a/Lib/turtledemo/colormixer.py b/Lib/turtledemo/colormixer.py --- a/Lib/turtledemo/colormixer.py +++ b/Lib/turtledemo/colormixer.py @@ -1,8 +1,6 @@ # colormixer from turtle import Screen, Turtle, mainloop -import sys -sys.setrecursionlimit(20000) # overcomes, for now, an instability of Python 3.0 class ColorTurtle(Turtle): diff --git a/Lib/turtledemo/demohelp.txt b/Lib/turtledemo/demohelp.txt --- a/Lib/turtledemo/demohelp.txt +++ b/Lib/turtledemo/demohelp.txt @@ -54,6 +54,9 @@ (2) How to add your own demos to the demo repository - Place the file in the same directory as turtledemo/__main__.py + IMPORTANT! When imported, the demo should not modify the system + by calling functions in other modules, such as sys, tkinter, or + turtle. Global variables should be initialized in main(). - The code must contain a main() function which will be executed by the viewer (see provided example scripts). @@ -65,7 +68,7 @@ if __name__ == '__main__': main() - mainloop() # keep window + mainloop() # keep window open python -m turtledemo.mydemo # will then run it diff --git a/Lib/turtledemo/minimal_hanoi.py b/Lib/turtledemo/minimal_hanoi.py --- a/Lib/turtledemo/minimal_hanoi.py +++ b/Lib/turtledemo/minimal_hanoi.py @@ -18,7 +18,6 @@ --------------------------------------- """ from turtle import * -from turtle import Terminator # not in __all__ class Disc(Turtle): def __init__(self, n): diff --git a/Lib/turtledemo/nim.py b/Lib/turtledemo/nim.py --- a/Lib/turtledemo/nim.py +++ b/Lib/turtledemo/nim.py @@ -143,7 +143,6 @@ self.writer.write(msg1, align="center", font=("Courier",14,"bold")) self.screen.tracer(True) - def setup(self): self.screen.tracer(False) for row in range(3): @@ -181,6 +180,7 @@ if self.game.state == Nim.OVER: self.screen.clear() + class NimController(object): def __init__(self, game): @@ -201,6 +201,7 @@ self.game.model.notify_move(row, col) self.BUSY = False + class Nim(object): CREATED = 0 RUNNING = 1 @@ -213,11 +214,10 @@ self.controller = NimController(self) -mainscreen = turtle.Screen() -mainscreen.mode("standard") -mainscreen.setup(SCREENWIDTH, SCREENHEIGHT) - def main(): + mainscreen = turtle.Screen() + mainscreen.mode("standard") + mainscreen.setup(SCREENWIDTH, SCREENHEIGHT) nim = Nim(mainscreen) return "EVENTLOOP!" diff --git a/Lib/turtledemo/two_canvases.py b/Lib/turtledemo/two_canvases.py --- a/Lib/turtledemo/two_canvases.py +++ b/Lib/turtledemo/two_canvases.py @@ -1,60 +1,54 @@ """turtledemo.two_canvases Use TurtleScreen and RawTurtle to draw on two -distinct canvases. +distinct canvases in a separate windows. The +new window must be separately closed in +addition to pressing the STOP button. """ -#The final mainloop only serves to keep the window open. - -#TODO: This runs in its own two-canvas window when selected in the -#demoviewer examples menu but the text is not loaded and the previous -#example is left visible. If the ending mainloop is removed, the text -#Eis loaded, this run again in a third window, and if start is pressed, -#demoviewer raises an error because main is not found, and then freezes. from turtle import TurtleScreen, RawTurtle, TK -root = TK.Tk() -cv1 = TK.Canvas(root, width=300, height=200, bg="#ddffff") -cv2 = TK.Canvas(root, width=300, height=200, bg="#ffeeee") -cv1.pack() -cv2.pack() +def main(): + root = TK.Tk() + cv1 = TK.Canvas(root, width=300, height=200, bg="#ddffff") + cv2 = TK.Canvas(root, width=300, height=200, bg="#ffeeee") + cv1.pack() + cv2.pack() -s1 = TurtleScreen(cv1) -s1.bgcolor(0.85, 0.85, 1) -s2 = TurtleScreen(cv2) -s2.bgcolor(1, 0.85, 0.85) + s1 = TurtleScreen(cv1) + s1.bgcolor(0.85, 0.85, 1) + s2 = TurtleScreen(cv2) + s2.bgcolor(1, 0.85, 0.85) -p = RawTurtle(s1) -q = RawTurtle(s2) + p = RawTurtle(s1) + q = RawTurtle(s2) -p.color("red", (1, 0.85, 0.85)) -p.width(3) -q.color("blue", (0.85, 0.85, 1)) -q.width(3) + p.color("red", (1, 0.85, 0.85)) + p.width(3) + q.color("blue", (0.85, 0.85, 1)) + q.width(3) -for t in p,q: - t.shape("turtle") - t.lt(36) + for t in p,q: + t.shape("turtle") + t.lt(36) -q.lt(180) + q.lt(180) -for t in p, q: - t.begin_fill() -for i in range(5): for t in p, q: - t.fd(50) - t.lt(72) -for t in p,q: - t.end_fill() - t.lt(54) - t.pu() - t.bk(50) + t.begin_fill() + for i in range(5): + for t in p, q: + t.fd(50) + t.lt(72) + for t in p,q: + t.end_fill() + t.lt(54) + t.pu() + t.bk(50) -## Want to get some info? + return "EVENTLOOP" -#print(s1, s2) -#print(p, q) -#print(s1.turtles()) -#print(s2.turtles()) -TK.mainloop() +if __name__ == '__main__': + main() + TK.mainloop() # keep window open until user closes it -- Repository URL: http://hg.python.org/cpython From python-checkins at python.org Mon Jun 30 22:10:18 2014 From: python-checkins at python.org (terry.reedy) Date: Mon, 30 Jun 2014 22:10:18 +0200 (CEST) Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_with_3=2E4?= Message-ID: <3h2KZ21ddVz7LkR@mail.python.org> http://hg.python.org/cpython/rev/3134189655b1 changeset: 91489:3134189655b1 parent: 91486:2d0fa8f383c8 parent: 91488:fcfa9c5a00fd user: Terry Jan Reedy date: Mon Jun 30 16:09:44 2014 -0400 summary: Merge with 3.4 files: Lib/turtle.py | 2 +- Lib/turtledemo/clock.py | 6 +- Lib/turtledemo/colormixer.py | 2 - Lib/turtledemo/demohelp.txt | 5 +- Lib/turtledemo/minimal_hanoi.py | 1 - Lib/turtledemo/nim.py | 10 +- Lib/turtledemo/two_canvases.py | 80 +++++++++----------- 7 files changed, 48 insertions(+), 58 deletions(-) diff --git a/Lib/turtle.py b/Lib/turtle.py --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -140,7 +140,7 @@ _tg_utilities = ['write_docstringdict', 'done'] __all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions + - _tg_utilities) # + _math_functions) + _tg_utilities + ['Terminator']) # + _math_functions) _alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos', 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st', diff --git a/Lib/turtledemo/clock.py b/Lib/turtledemo/clock.py --- a/Lib/turtledemo/clock.py +++ b/Lib/turtledemo/clock.py @@ -11,11 +11,8 @@ ------------------------------------ """ from turtle import * -from turtle import Terminator # not in __all__ from datetime import datetime -mode("logo") - def jump(distanz, winkel=0): penup() right(winkel) @@ -43,7 +40,6 @@ hand_form = get_poly() register_shape(name, hand_form) - def clockface(radius): reset() pensize(7) @@ -84,7 +80,6 @@ writer.pu() writer.bk(85) - def wochentag(t): wochentag = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] @@ -131,6 +126,7 @@ return "EVENTLOOP" if __name__ == "__main__": + mode("logo") msg = main() print(msg) mainloop() diff --git a/Lib/turtledemo/colormixer.py b/Lib/turtledemo/colormixer.py --- a/Lib/turtledemo/colormixer.py +++ b/Lib/turtledemo/colormixer.py @@ -1,8 +1,6 @@ # colormixer from turtle import Screen, Turtle, mainloop -import sys -sys.setrecursionlimit(20000) # overcomes, for now, an instability of Python 3.0 class ColorTurtle(Turtle): diff --git a/Lib/turtledemo/demohelp.txt b/Lib/turtledemo/demohelp.txt --- a/Lib/turtledemo/demohelp.txt +++ b/Lib/turtledemo/demohelp.txt @@ -54,6 +54,9 @@ (2) How to add your own demos to the demo repository - Place the file in the same directory as turtledemo/__main__.py + IMPORTANT! When imported, the demo should not modify the system + by calling functions in other modules, such as sys, tkinter, or + turtle. Global variables should be initialized in main(). - The code must contain a main() function which will be executed by the viewer (see provided example scripts). @@ -65,7 +68,7 @@ if __name__ == '__main__': main() - mainloop() # keep window + mainloop() # keep window open python -m turtledemo.mydemo # will then run it diff --git a/Lib/turtledemo/minimal_hanoi.py b/Lib/turtledemo/minimal_hanoi.py --- a/Lib/turtledemo/minimal_hanoi.py +++ b/Lib/turtledemo/minimal_hanoi.py @@ -18,7 +18,6 @@ --------------------------------------- """ from turtle import * -from turtle import Terminator # not in __all__ class Disc(Turtle): def __init__(self, n): diff --git a/Lib/turtledemo/nim.py b/Lib/turtledemo/nim.py --- a/Lib/turtledemo/nim.py +++ b/Lib/turtledemo/nim.py @@ -143,7 +143,6 @@ self.writer.write(msg1, align="center", font=("Courier",14,"bold")) self.screen.tracer(True) - def setup(self): self.screen.tracer(False) for row in range(3): @@ -181,6 +180,7 @@ if self.game.state == Nim.OVER: self.screen.clear() + class NimController(object): def __init__(self, game): @@ -201,6 +201,7 @@ self.game.model.notify_move(row, col) self.BUSY = False + class Nim(object): CREATED = 0 RUNNING = 1 @@ -213,11 +214,10 @@ self.controller = NimController(self) -mainscreen = turtle.Screen() -mainscreen.mode("standard") -mainscreen.setup(SCREENWIDTH, SCREENHEIGHT) - def main(): + mainscreen = turtle.Screen() + mainscreen.mode("standard") + mainscreen.setup(SCREENWIDTH, SCREENHEIGHT) nim = Nim(mainscreen) return "EVENTLOOP!" diff --git a/Lib/turtledemo/two_canvases.py b/Lib/turtledemo/two_canvases.py --- a/Lib/turtledemo/two_canvases.py +++ b/Lib/turtledemo/two_canvases.py @@ -1,60 +1,54 @@ """turtledemo.two_canvases Use TurtleScreen and RawTurtle to draw on two -distinct canvases. +distinct canvases in a separate windows. The +new window must be separately closed in +addition to pressing the STOP button. """ -#The final mainloop only serves to keep the window open. - -#TODO: This runs in its own two-canvas window when selected in the -#demoviewer examples menu but the text is not loaded and the previous -#example is left visible. If the ending mainloop is removed, the text -#Eis loaded, this run again in a third window, and if start is pressed, -#demoviewer raises an error because main is not found, and then freezes. from turtle import TurtleScreen, RawTurtle, TK -root = TK.Tk() -cv1 = TK.Canvas(root, width=300, height=200, bg="#ddffff") -cv2 = TK.Canvas(root, width=300, height=200, bg="#ffeeee") -cv1.pack() -cv2.pack() +def main(): + root = TK.Tk() + cv1 = TK.Canvas(root, width=300, height=200, bg="#ddffff") + cv2 = TK.Canvas(root, width=300, height=200, bg="#ffeeee") + cv1.pack() + cv2.pack() -s1 = TurtleScreen(cv1) -s1.bgcolor(0.85, 0.85, 1) -s2 = TurtleScreen(cv2) -s2.bgcolor(1, 0.85, 0.85) + s1 = TurtleScreen(cv1) + s1.bgcolor(0.85, 0.85, 1) + s2 = TurtleScreen(cv2) + s2.bgcolor(1, 0.85, 0.85) -p = RawTurtle(s1) -q = RawTurtle(s2) + p = RawTurtle(s1) + q = RawTurtle(s2) -p.color("red", (1, 0.85, 0.85)) -p.width(3) -q.color("blue", (0.85, 0.85, 1)) -q.width(3) + p.color("red", (1, 0.85, 0.85)) + p.width(3) + q.color("blue", (0.85, 0.85, 1)) + q.width(3) -for t in p,q: - t.shape("turtle") - t.lt(36) + for t in p,q: + t.shape("turtle") + t.lt(36) -q.lt(180) + q.lt(180) -for t in p, q: - t.begin_fill() -for i in range(5): for t in p, q: - t.fd(50) - t.lt(72) -for t in p,q: - t.end_fill() - t.lt(54) - t.pu() - t.bk(50) + t.begin_fill() + for i in range(5): + for t in p, q: + t.fd(50) + t.lt(72) + for t in p,q: + t.end_fill() + t.lt(54) + t.pu() + t.bk(50) -## Want to get some info? + return "EVENTLOOP" -#print(s1, s2) -#print(p, q) -#print(s1.turtles()) -#print(s2.turtles()) -TK.mainloop() +if __name__ == '__main__': + main() + TK.mainloop() # keep window open until user closes it -- Repository URL: http://hg.python.org/cpython