From python-checkins at python.org Sun Mar 1 02:49:53 2015 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 01 Mar 2015 01:49:53 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Use_unsigned_division_and_?= =?utf-8?q?modulo_for_item_assignment_as_well=2E?= Message-ID: <20150301014953.15296.74210@psf.io> https://hg.python.org/cpython/rev/1d25edcbb477 changeset: 94791:1d25edcbb477 user: Raymond Hettinger date: Sat Feb 28 17:49:47 2015 -0800 summary: Use unsigned division and modulo for item assignment as well. files: Modules/_collectionsmodule.c | 9 +++++---- 1 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -780,7 +780,6 @@ b = deque->rightblock; } else { i += deque->leftindex; - assert(i >= 0); n = (Py_ssize_t)((unsigned) i / BLOCKLEN); i = (Py_ssize_t)((unsigned) i % BLOCKLEN); if (index < (Py_SIZE(deque) >> 1)) { @@ -840,14 +839,16 @@ return deque_del_item(deque, i); i += deque->leftindex; - n = i / BLOCKLEN; - i %= BLOCKLEN; + n = (Py_ssize_t)((unsigned) i / BLOCKLEN); + i = (Py_ssize_t)((unsigned) i % BLOCKLEN); if (index <= halflen) { b = deque->leftblock; while (n--) b = b->rightlink; } else { - n = (deque->leftindex + len - 1) / BLOCKLEN - n; + n = (Py_ssize_t)( + ((unsigned)(deque->leftindex + Py_SIZE(deque) - 1)) + / BLOCKLEN - n); b = deque->rightblock; while (n--) b = b->leftlink; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 04:54:26 2015 From: python-checkins at python.org (steve.dower) Date: Sun, 01 Mar 2015 03:54:26 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Updates_CRT_installation_f?= =?utf-8?q?or_Windows_installer=2E?= Message-ID: <20150301035426.10066.36495@psf.io> https://hg.python.org/cpython/rev/cfdba57d0070 changeset: 94792:cfdba57d0070 user: Steve Dower date: Sat Feb 28 19:53:50 2015 -0800 summary: Updates CRT installation for Windows installer. Bundling versions of the CRT prior to 14.0 is no longer supported. files: Tools/msi/bundle/Default.wxl | 5 + Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp | 88 +++++++++- Tools/msi/bundle/bundle.targets | 3 + Tools/msi/bundle/postinstall_en-US.wxl_template | 3 +- Tools/msi/bundle/packagegroups/crt.wxs | 43 ++++ Tools/msi/crt/crt.wixproj | 2 +- Tools/msi/crt/crt_files.12.0.wxs | 20 -- Tools/msi/crt/crt_files.14.0.wxs | 20 -- Tools/msi/msi.props | 13 +- Tools/msi/redist/README.txt | 15 + 10 files changed, 160 insertions(+), 52 deletions(-) diff --git a/Tools/msi/bundle/Default.wxl b/Tools/msi/bundle/Default.wxl --- a/Tools/msi/bundle/Default.wxl +++ b/Tools/msi/bundle/Default.wxl @@ -11,6 +11,11 @@ Removing Uninstall + You will be prompted for Administrator privileges to install a C Runtime Library update (KB2999226). + + +Continue? + &Cancel &Close Install [WixBundleName] diff --git a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp --- a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp +++ b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp @@ -10,8 +10,6 @@ #include "pch.h" -static const LPCWSTR WIXBUNDLE_VARIABLE_ELEVATED = L"WixBundleElevated"; - static const LPCWSTR PYBA_WINDOW_CLASS = L"PythonBA"; static const LPCWSTR PYBA_VARIABLE_LAUNCH_TARGET_PATH = L"LaunchTarget"; static const LPCWSTR PYBA_VARIABLE_LAUNCH_TARGET_ELEVATED_ID = L"LaunchTargetElevatedId"; @@ -232,7 +230,7 @@ void OnCommand(CONTROL_ID id) { LPWSTR defaultDir = nullptr; LPWSTR targetDir = nullptr; - LONGLONG elevated; + LONGLONG elevated, crtInstalled; BOOL checked; WCHAR wzPath[MAX_PATH] = { }; BROWSEINFOW browseInfo = { }; @@ -320,6 +318,10 @@ ReleaseStr(targetDir); BalExitOnFailure(hr, "Failed to set install target directory"); + if (!QueryElevateForCrtInstall()) { + break; + } + OnPlan(BOOTSTRAPPER_ACTION_INSTALL); break; @@ -352,6 +354,11 @@ ReleaseStr(targetDir); } + checked = ThemeIsControlChecked(_theme, ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX); + if (!checked && !QueryElevateForCrtInstall()) { + break; + } + OnPlan(_command.action); break; @@ -2311,6 +2318,75 @@ } } + BOOL IsCrtInstalled() { + if (_crtInstalledToken > 0) { + return TRUE; + } else if (_crtInstalledToken == 0) { + return FALSE; + } + + // Check whether at least CRT v10.0.9920.0 is available. + // It should only be installed as a Windows Update package, which means + // we don't need to worry about 32-bit/64-bit. + // However, since the WU package does not include vcruntime140.dll, we + // still install that ourselves. + LPCWSTR crtFile = L"api-ms-win-crt-runtime-l1-1-0.dll"; + + DWORD cbVer = GetFileVersionInfoSizeW(crtFile, nullptr); + if (!cbVer) { + _crtInstalledToken = 0; + return FALSE; + } + + void *pData = malloc(cbVer); + if (!pData) { + _crtInstalledToken = 0; + return FALSE; + } + + if (!GetFileVersionInfoW(crtFile, 0, cbVer, pData)) { + free(pData); + _crtInstalledToken = 0; + return FALSE; + } + + VS_FIXEDFILEINFO *ffi; + UINT cb; + BOOL result = FALSE; + + if (VerQueryValueW(pData, L"\\", (LPVOID*)&ffi, &cb) && + ffi->dwFileVersionMS == 0x000A0000 && ffi->dwFileVersionLS >= 0x26C00000) { + result = TRUE; + } + + free(pData); + _crtInstalledToken = result ? 1 : 0; + return result; + } + + BOOL QueryElevateForCrtInstall() { + // Called to prompt the user that even though they think they won't need + // to elevate, they actually will because of the CRT install. + if (IsCrtInstalled()) { + // CRT is already installed - no need to prompt + return TRUE; + } + + LONGLONG elevated; + HRESULT hr = BalGetNumericVariable(L"WixBundleElevated", &elevated); + if (SUCCEEDED(hr) && elevated) { + // Already elevated - no need to prompt + return TRUE; + } + + LOC_STRING *locStr; + hr = LocGetString(_wixLoc, L"#(loc.ElevateForCRTInstall)", &locStr); + if (FAILED(hr)) { + BalLogError(hr, "Failed to get ElevateForCRTInstall string"); + return FALSE; + } + return ::MessageBoxW(_hWnd, locStr->wzText, _theme->sczCaption, MB_YESNO) != IDNO; + } HRESULT EvaluateConditions() { HRESULT hr = S_OK; @@ -2498,6 +2574,8 @@ } } + pEngine->SetVariableNumeric(L"CRTInstalled", IsCrtInstalled() ? 1 : 0); + _wixLoc = nullptr; memset(&_bundle, 0, sizeof(_bundle)); memset(&_conditions, 0, sizeof(_conditions)); @@ -2525,6 +2603,8 @@ _suppressRepair = FALSE; _modifying = FALSE; + _crtInstalledToken = -1; + _overridableVariables = nullptr; _taskbarList = nullptr; _taskbarButtonCreatedMessage = UINT_MAX; @@ -2606,6 +2686,8 @@ BOOL _suppressRepair; BOOL _modifying; + int _crtInstalledToken; + STRINGDICT_HANDLE _overridableVariables; ITaskbarList3* _taskbarList; diff --git a/Tools/msi/bundle/bundle.targets b/Tools/msi/bundle/bundle.targets --- a/Tools/msi/bundle/bundle.targets +++ b/Tools/msi/bundle/bundle.targets @@ -10,6 +10,8 @@ $(OutputName)-$(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber).$(RevisionNumber) $(OutputName)-amd64 $(OutputName)-$(OutputSuffix) + $(OutputName)-d + $(OutputName) $(OutputPath)en-us\ $(OutputPath) @@ -43,6 +45,7 @@ + diff --git a/Tools/msi/bundle/postinstall_en-US.wxl_template b/Tools/msi/bundle/bundle.wxl rename from Tools/msi/bundle/postinstall_en-US.wxl_template rename to Tools/msi/bundle/bundle.wxl --- a/Tools/msi/bundle/postinstall_en-US.wxl_template +++ b/Tools/msi/bundle/bundle.wxl @@ -1,4 +1,5 @@ - Precompiling standard library + C Runtime Update (KB2999226) + Precompiling standard library diff --git a/Tools/msi/bundle/packagegroups/crt.wxs b/Tools/msi/bundle/packagegroups/crt.wxs --- a/Tools/msi/bundle/packagegroups/crt.wxs +++ b/Tools/msi/bundle/packagegroups/crt.wxs @@ -2,6 +2,11 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tools/msi/crt/crt.wixproj b/Tools/msi/crt/crt.wixproj --- a/Tools/msi/crt/crt.wixproj +++ b/Tools/msi/crt/crt.wixproj @@ -10,7 +10,7 @@ - + diff --git a/Tools/msi/crt/crt_files.12.0.wxs b/Tools/msi/crt/crt_files.12.0.wxs deleted file mode 100644 --- a/Tools/msi/crt/crt_files.12.0.wxs +++ /dev/null @@ -1,20 +0,0 @@ -? - - - - - - - - - - ALLUSERS=1 - - - - NOT ALLUSERS=1 - - - - - diff --git a/Tools/msi/crt/crt_files.14.0.wxs b/Tools/msi/crt/crt_files.wxs rename from Tools/msi/crt/crt_files.14.0.wxs rename to Tools/msi/crt/crt_files.wxs --- a/Tools/msi/crt/crt_files.14.0.wxs +++ b/Tools/msi/crt/crt_files.wxs @@ -2,35 +2,15 @@ - - - - - - ALLUSERS=1 - - - - ALLUSERS=1 - - ALLUSERS=1 - - NOT ALLUSERS=1 - - - - NOT ALLUSERS=1 - - NOT ALLUSERS=1 diff --git a/Tools/msi/msi.props b/Tools/msi/msi.props --- a/Tools/msi/msi.props +++ b/Tools/msi/msi.props @@ -1,6 +1,7 @@ + $(OutputName) false false $(SuppressIces);ICE03;ICE57;ICE61 @@ -48,10 +49,8 @@ $(OutputPath)\ $(OutputPath) true - $(CommonProgramFiles)\Merge Modules\Microsoft_VC120_CRT_$(Platform).msm - $(CommonProgramFiles)\Merge Modules\Microsoft_VC140_CRT_$(Platform).msm - $([System.IO.Path]::GetFullPath(`$(VS120COMNTOOLS)\..\..\VC\redist\$(Platform)\Microsoft.VC120.CRT`)) - $([System.IO.Path]::GetFullPath(`$(VS140COMNTOOLS)\..\..\VC\redist\$(Platform)\Microsoft.VC140.CRT`)) + $([System.IO.Path]::GetFullPath(`$(VS140COMNTOOLS)\..\..\VC\redist\$(Platform)\Microsoft.VC140.CRT`)) + $(MSBuildThisFileDirectory)\redist\$(Platform) $(ReleaseLevelNumber) @@ -72,9 +71,6 @@ NextMajorVersionNumber=$(MajorVersionNumber).$([msbuild]::Add($(MinorVersionNumber), 1)).0.0; PyDebugExt=$(PyDebugExt); - - $(DefineConstants);CRTModule=$(CRTModule); - $(DefineConstants);CRTRedist=$(CRTRedist); @@ -115,6 +111,9 @@ crt + + redist + diff --git a/Tools/msi/redist/README.txt b/Tools/msi/redist/README.txt new file mode 100644 --- /dev/null +++ b/Tools/msi/redist/README.txt @@ -0,0 +1,15 @@ +This folder is intentianally left empty in the repository. + +The following dependencies may be copied here if they cannot be detected +automatically by the build scripts: + +redist\Windows6.0-KB2999226-x64.msu +redist\Windows6.0-KB2999226-x86.msu +redist\Windows6.1-KB2999226-x64.msu +redist\Windows6.1-KB2999226-x86.msu +redist\Windows8.1-KB2999226-x64.msu +redist\Windows8.1-KB2999226-x86.msu +redist\Windows8-RT-KB2999226-x64.msu +redist\Windows8-RT-KB2999226-x86.msu +redist\x64\vcruntime140.dll +redist\x86\vcruntime140.dll -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 08:29:23 2015 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 01 Mar 2015 07:29:23 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Need_a_=28size=5Ft=29_cast?= =?utf-8?q?_instead_of_=28unsigned=29_to_be_big_enough_for_a_Py=5Fssize=5F?= =?utf-8?q?t=2E?= Message-ID: <20150301072923.10072.40024@psf.io> https://hg.python.org/cpython/rev/570d78391343 changeset: 94793:570d78391343 user: Raymond Hettinger date: Sat Feb 28 23:29:16 2015 -0800 summary: Need a (size_t) cast instead of (unsigned) to be big enough for a Py_ssize_t. files: Modules/_collectionsmodule.c | 18 ++++++++++++------ 1 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1,6 +1,12 @@ #include "Python.h" #include "structmember.h" +#ifdef STDC_HEADERS +#include +#else +#include /* For size_t */ +#endif + /* collections module implementation of a deque() datatype Written and maintained by Raymond D. Hettinger Copyright (c) 2004-2015 Python Software Foundation. @@ -780,15 +786,15 @@ b = deque->rightblock; } else { i += deque->leftindex; - n = (Py_ssize_t)((unsigned) i / BLOCKLEN); - i = (Py_ssize_t)((unsigned) i % BLOCKLEN); + n = (Py_ssize_t)((size_t) i / BLOCKLEN); + i = (Py_ssize_t)((size_t) i % BLOCKLEN); if (index < (Py_SIZE(deque) >> 1)) { b = deque->leftblock; while (n--) b = b->rightlink; } else { n = (Py_ssize_t)( - ((unsigned)(deque->leftindex + Py_SIZE(deque) - 1)) + ((size_t)(deque->leftindex + Py_SIZE(deque) - 1)) / BLOCKLEN - n); b = deque->rightblock; while (n--) @@ -839,15 +845,15 @@ return deque_del_item(deque, i); i += deque->leftindex; - n = (Py_ssize_t)((unsigned) i / BLOCKLEN); - i = (Py_ssize_t)((unsigned) i % BLOCKLEN); + n = (Py_ssize_t)((size_t) i / BLOCKLEN); + i = (Py_ssize_t)((size_t) i % BLOCKLEN); if (index <= halflen) { b = deque->leftblock; while (n--) b = b->rightlink; } else { n = (Py_ssize_t)( - ((unsigned)(deque->leftindex + Py_SIZE(deque) - 1)) + ((size_t)(deque->leftindex + Py_SIZE(deque) - 1)) / BLOCKLEN - n); b = deque->rightblock; while (n--) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 09:04:27 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 01 Mar 2015 08:04:27 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIwMjA0?= =?utf-8?q?=3A_Added_the_=5F=5Fmodule=5F=5F_attribute_to_=5Ftkinter_classe?= =?utf-8?q?s=2E?= Message-ID: <20150301080427.16426.62287@psf.io> https://hg.python.org/cpython/rev/a192cc5a63be changeset: 94794:a192cc5a63be branch: 3.4 parent: 94787:8fe15bf68522 user: Serhiy Storchaka date: Sun Mar 01 09:06:29 2015 +0200 summary: Issue #20204: Added the __module__ attribute to _tkinter classes. files: Misc/NEWS | 3 +++ Modules/_tkinter.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ Library ------- + +- Issue #20204: Added the __module__ attribute to _tkinter classes. + - Issue #23521: Corrected pure python implementation of timedelta division. * Eliminated OverflowError from timedelta * float for some floats; diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -2388,7 +2388,7 @@ }; static PyType_Spec Tktt_Type_spec = { - "tktimertoken", + "_tkinter.tktimertoken", sizeof(TkttObject), 0, Py_TPFLAGS_DEFAULT, @@ -2692,7 +2692,7 @@ static PyType_Spec Tkapp_Type_spec = { - "tkapp", + "_tkinter.tkapp", sizeof(TkappObject), 0, Py_TPFLAGS_DEFAULT, -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 09:04:28 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 01 Mar 2015 08:04:28 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_Merge_heads?= Message-ID: <20150301080428.34492.1830@psf.io> https://hg.python.org/cpython/rev/1aafce84a865 changeset: 94797:1aafce84a865 parent: 94796:d6dff5a5290a parent: 94793:570d78391343 user: Serhiy Storchaka date: Sun Mar 01 10:03:46 2015 +0200 summary: Merge heads files: Modules/_collectionsmodule.c | 18 ++++++++++++------ 1 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1,6 +1,12 @@ #include "Python.h" #include "structmember.h" +#ifdef STDC_HEADERS +#include +#else +#include /* For size_t */ +#endif + /* collections module implementation of a deque() datatype Written and maintained by Raymond D. Hettinger Copyright (c) 2004-2015 Python Software Foundation. @@ -780,15 +786,15 @@ b = deque->rightblock; } else { i += deque->leftindex; - n = (Py_ssize_t)((unsigned) i / BLOCKLEN); - i = (Py_ssize_t)((unsigned) i % BLOCKLEN); + n = (Py_ssize_t)((size_t) i / BLOCKLEN); + i = (Py_ssize_t)((size_t) i % BLOCKLEN); if (index < (Py_SIZE(deque) >> 1)) { b = deque->leftblock; while (n--) b = b->rightlink; } else { n = (Py_ssize_t)( - ((unsigned)(deque->leftindex + Py_SIZE(deque) - 1)) + ((size_t)(deque->leftindex + Py_SIZE(deque) - 1)) / BLOCKLEN - n); b = deque->rightblock; while (n--) @@ -839,15 +845,15 @@ return deque_del_item(deque, i); i += deque->leftindex; - n = (Py_ssize_t)((unsigned) i / BLOCKLEN); - i = (Py_ssize_t)((unsigned) i % BLOCKLEN); + n = (Py_ssize_t)((size_t) i / BLOCKLEN); + i = (Py_ssize_t)((size_t) i % BLOCKLEN); if (index <= halflen) { b = deque->leftblock; while (n--) b = b->rightlink; } else { n = (Py_ssize_t)( - ((unsigned)(deque->leftindex + Py_SIZE(deque) - 1)) + ((size_t)(deque->leftindex + Py_SIZE(deque) - 1)) / BLOCKLEN - n); b = deque->rightblock; while (n--) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 09:04:28 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 01 Mar 2015 08:04:28 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320204=3A_Added_the_=5F=5Fmodule=5F=5F_attribute?= =?utf-8?q?_to_=5Ftkinter_classes=2E?= Message-ID: <20150301080427.53139.39246@psf.io> https://hg.python.org/cpython/rev/3244142eeafb changeset: 94795:3244142eeafb parent: 94792:cfdba57d0070 parent: 94794:a192cc5a63be user: Serhiy Storchaka date: Sun Mar 01 09:07:10 2015 +0200 summary: Issue #20204: Added the __module__ attribute to _tkinter classes. files: Misc/NEWS | 2 ++ Modules/_tkinter.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,8 @@ Library ------- +- Issue #20204: Added the __module__ attribute to _tkinter classes. + - Issue #19980: Improved help() for non-recognized strings. help('') now shows the help on str. help('help') now shows the help on help(). Original patch by Mark Lawrence. diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -2409,7 +2409,7 @@ }; static PyType_Spec Tktt_Type_spec = { - "tktimertoken", + "_tkinter.tktimertoken", sizeof(TkttObject), 0, Py_TPFLAGS_DEFAULT, @@ -2713,7 +2713,7 @@ static PyType_Spec Tkapp_Type_spec = { - "tkapp", + "_tkinter.tkapp", sizeof(TkappObject), 0, Py_TPFLAGS_DEFAULT, -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 09:04:28 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 01 Mar 2015 08:04:28 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2320204=3A_Deprecat?= =?utf-8?q?ion_warning_is_now_raised_for_builtin_type_without_the?= Message-ID: <20150301080427.53139.139@psf.io> https://hg.python.org/cpython/rev/d6dff5a5290a changeset: 94796:d6dff5a5290a user: Serhiy Storchaka date: Sun Mar 01 10:03:02 2015 +0200 summary: Issue #20204: Deprecation warning is now raised for builtin type without the __module__ attribute. files: Doc/whatsnew/3.5.rst | 5 +++++ Misc/NEWS | 6 ++++++ Objects/typeobject.c | 6 ++++++ 3 files changed, 17 insertions(+), 0 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 @@ -561,3 +561,8 @@ * Removed non-documented macro :c:macro:`PyObject_REPR` which leaked references. Use format character ``%R`` in :c:func:`PyUnicode_FromFormat`-like functions to format the :func:`repr` of the object. + +* Because the lack of the :attr:`__module__` attribute breaks pickling and + introspection, a deprecation warning now is raised for builtin type without + the :attr:`__module__` attribute. Would be an AttributeError in future. + (:issue:`20204`) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -88,6 +88,12 @@ - Issue #23445: pydebug builds now use "gcc -Og" where possible, to make the resulting executable faster. +C API +----- + +- Issue #20204: Deprecation warning is now raised for builtin type without the + __module__ attribute. + Windows ------- diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2808,6 +2808,12 @@ _PyDict_SetItemId(type->tp_dict, &PyId___module__, PyUnicode_FromStringAndSize( spec->name, (Py_ssize_t)(s - spec->name))); + else { + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "builtin type %.200s has no the __module__ attribute", + spec->name)) + goto fail; + } return (PyObject*)res; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 09:38:06 2015 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 01 Mar 2015 08:38:06 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323553=3A__Use_an_?= =?utf-8?q?unsigned_cast_to_tighten-up_the_bounds_checking_logic=2E?= Message-ID: <20150301083806.15320.74741@psf.io> https://hg.python.org/cpython/rev/1e89094998b2 changeset: 94798:1e89094998b2 user: Raymond Hettinger date: Sun Mar 01 00:38:00 2015 -0800 summary: Issue #23553: Use an unsigned cast to tighten-up the bounds checking logic. files: Modules/_collectionsmodule.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -772,7 +772,7 @@ PyObject *item; Py_ssize_t n, index=i; - if (i < 0 || i >= Py_SIZE(deque)) { + if ((size_t)i >= (size_t)Py_SIZE(deque)) { PyErr_SetString(PyExc_IndexError, "deque index out of range"); return NULL; @@ -836,7 +836,7 @@ block *b; Py_ssize_t n, len=Py_SIZE(deque), halflen=(len+1)>>1, index=i; - if (i < 0 || i >= len) { + if ((size_t)i >= (size_t)len) { PyErr_SetString(PyExc_IndexError, "deque index out of range"); return -1; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 13:39:53 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 01 Mar 2015 12:39:53 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fixed_a_typo=2E?= Message-ID: <20150301123952.87097.11025@psf.io> https://hg.python.org/cpython/rev/8dca41e6e86b changeset: 94799:8dca41e6e86b user: Serhiy Storchaka date: Sun Mar 01 14:39:20 2015 +0200 summary: Fixed a typo. files: Objects/typeobject.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2810,7 +2810,7 @@ spec->name, (Py_ssize_t)(s - spec->name))); else { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "builtin type %.200s has no the __module__ attribute", + "builtin type %.200s has no __module__ attribute", spec->name)) goto fail; } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 14:33:18 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 01 Mar 2015 13:33:18 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Fixed_pydoc_tests_when_run_with_-OO=2E?= Message-ID: <20150301133257.77546.96229@psf.io> https://hg.python.org/cpython/rev/94edd0ef4c2a changeset: 94802:94edd0ef4c2a parent: 94799:8dca41e6e86b parent: 94801:d7b3e722152d user: Serhiy Storchaka date: Sun Mar 01 15:32:17 2015 +0200 summary: Fixed pydoc tests when run with -OO. files: Lib/test/test_pydoc.py | 4 ++++ 1 files changed, 4 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 @@ -467,6 +467,8 @@ self.assertEqual(expected, result, "documentation for missing module found") + @unittest.skipIf(sys.flags.optimize >= 2, + 'Docstrings are omitted with -OO and above') def test_not_ascii(self): result = run_pydoc('test.test_pydoc.nonascii', PYTHONIOENCODING='ascii') encoded = nonascii.__doc__.encode('ascii', 'backslashreplace') @@ -551,6 +553,8 @@ synopsis = pydoc.synopsis(TESTFN, {}) self.assertEqual(synopsis, 'line 1: h\xe9') + @unittest.skipIf(sys.flags.optimize >= 2, + 'Docstrings are omitted with -OO and above') def test_synopsis_sourceless(self): expected = os.__doc__.splitlines()[0] filename = os.__cached__ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 14:33:18 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 01 Mar 2015 13:33:18 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fixed_pydoc_te?= =?utf-8?q?sts_when_run_with_-OO=2E?= Message-ID: <20150301133257.87107.44912@psf.io> https://hg.python.org/cpython/rev/d7b3e722152d changeset: 94801:d7b3e722152d branch: 3.4 parent: 94794:a192cc5a63be user: Serhiy Storchaka date: Sun Mar 01 15:31:36 2015 +0200 summary: Fixed pydoc tests when run with -OO. files: Lib/test/test_pydoc.py | 4 ++++ 1 files changed, 4 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 @@ -478,6 +478,8 @@ self.assertEqual(expected, result, "documentation for missing module found") + @unittest.skipIf(sys.flags.optimize >= 2, + 'Docstrings are omitted with -OO and above') def test_not_ascii(self): result = run_pydoc('test.test_pydoc.nonascii', PYTHONIOENCODING='ascii') encoded = nonascii.__doc__.encode('ascii', 'backslashreplace') @@ -562,6 +564,8 @@ synopsis = pydoc.synopsis(TESTFN, {}) self.assertEqual(synopsis, 'line 1: h\xe9') + @unittest.skipIf(sys.flags.optimize >= 2, + 'Docstrings are omitted with -OO and above') def test_synopsis_sourceless(self): expected = os.__doc__.splitlines()[0] filename = os.__cached__ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 14:33:18 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 01 Mar 2015 13:33:18 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Fixed_pydoc_te?= =?utf-8?q?sts_when_run_with_-OO=2E?= Message-ID: <20150301133257.77542.22924@psf.io> https://hg.python.org/cpython/rev/663a83c1d42d changeset: 94800:663a83c1d42d branch: 2.7 parent: 94744:325aec842e3e user: Serhiy Storchaka date: Sun Mar 01 15:31:21 2015 +0200 summary: Fixed pydoc tests when run with -OO. files: Lib/test/test_pydoc.py | 2 ++ 1 files changed, 2 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 @@ -399,6 +399,8 @@ synopsis = pydoc.synopsis(init_path, {}) self.assertEqual(synopsis, 'my doc') + @unittest.skipIf(sys.flags.optimize >= 2, + 'Docstrings are omitted with -OO and above') def test_synopsis_sourceless_empty_doc(self): with test.test_support.temp_cwd() as test_dir: init_path = os.path.join(test_dir, 'foomod42.py') -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 20:52:15 2015 From: python-checkins at python.org (alexander.belopolsky) Date: Sun, 01 Mar 2015 19:52:15 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Closes_issue_=2322791=3A_I?= =?utf-8?q?mproved_datetime_from_timestamp_methods_documentation=2E?= Message-ID: <20150301195215.20285.51785@psf.io> https://hg.python.org/cpython/rev/4a590c66070d changeset: 94803:4a590c66070d user: Alexander Belopolsky date: Sun Mar 01 14:52:07 2015 -0500 summary: Closes issue #22791: Improved datetime from timestamp methods documentation. Original patch by Akira Li. files: Doc/library/datetime.rst | 20 +++++++++++++------- Lib/datetime.py | 2 +- Modules/_datetimemodule.c | 3 +-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -759,13 +759,19 @@ :attr:`tzinfo` ``None``. This may raise :exc:`OverflowError`, if the timestamp is out of the range of values supported by the platform C :c:func:`gmtime` function, and :exc:`OSError` on :c:func:`gmtime` failure. - It's common for this to be restricted to years in 1970 through 2038. See also - :meth:`fromtimestamp`. - - On the POSIX compliant platforms, ``utcfromtimestamp(timestamp)`` - is equivalent to the following expression:: - - datetime(1970, 1, 1) + timedelta(seconds=timestamp) + It's common for this to be restricted to years in 1970 through 2038. + + To get an aware :class:`.datetime` object, call :meth:`fromtimestamp`:: + + datetime.fromtimestamp(timestamp, timezone.utc) + + On the POSIX compliant platforms, it is equivalent to the following + expression:: + + datetime(1970, 1, 1, tzinfo=timezone.utc) + timedelta(seconds=timestamp) + + except the latter formula always supports the full years range: between + :const:`MINYEAR` and :const:`MAXYEAR` inclusive. .. versionchanged:: 3.3 Raise :exc:`OverflowError` instead of :exc:`ValueError` if the timestamp diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1393,7 +1393,7 @@ @classmethod def utcfromtimestamp(cls, t): - "Construct a UTC datetime from a POSIX timestamp (like time.time())." + """Construct a naive UTC datetime from a POSIX timestamp.""" t, frac = divmod(t, 1.0) us = int(frac * 1e6) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -5020,8 +5020,7 @@ {"utcfromtimestamp", (PyCFunction)datetime_utcfromtimestamp, METH_VARARGS | METH_CLASS, - PyDoc_STR("timestamp -> UTC datetime from a POSIX timestamp " - "(like time.time()).")}, + PyDoc_STR("Construct a naive UTC datetime from a POSIX timestamp.")}, {"strptime", (PyCFunction)datetime_strptime, METH_VARARGS | METH_CLASS, -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 1 21:08:28 2015 From: python-checkins at python.org (alexander.belopolsky) Date: Sun, 01 Mar 2015 20:08:28 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=237830=3A_Flatten_n?= =?utf-8?q?ested_functools=2Epartial=2E?= Message-ID: <20150301200828.87117.37553@psf.io> https://hg.python.org/cpython/rev/7839681ca931 changeset: 94804:7839681ca931 user: Alexander Belopolsky date: Sun Mar 01 15:08:17 2015 -0500 summary: Issue #7830: Flatten nested functools.partial. files: Lib/functools.py | 8 +++ Lib/test/test_functools.py | 10 ++++ Misc/NEWS | 2 + Modules/_functoolsmodule.c | 55 ++++++++++++++++++++++--- 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py --- a/Lib/functools.py +++ b/Lib/functools.py @@ -241,6 +241,14 @@ """New function with partial application of the given arguments and keywords. """ + if hasattr(func, 'func'): + args = func.args + args + tmpkw = func.keywords.copy() + tmpkw.update(keywords) + keywords = tmpkw + del tmpkw + func = func.func + def newfunc(*fargs, **fkeywords): newkeywords = keywords.copy() newkeywords.update(fkeywords) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -131,6 +131,16 @@ join = self.partial(''.join) self.assertEqual(join(data), '0123456789') + def test_nested_optimization(self): + partial = self.partial + # Only "true" partial is optimized + if partial.__name__ != 'partial': + return + inner = partial(signature, 'asdf') + nested = partial(inner, bar=True) + flat = partial(signature, 'asdf', bar=True) + self.assertEqual(signature(nested), signature(flat)) + @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestPartialC(TestPartial, unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,8 @@ Library ------- +- Issue #7830: Flatten nested functools.partial. + - Issue #20204: Added the __module__ attribute to _tkinter classes. - Issue #19980: Improved help() for non-recognized strings. help('') now diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -25,7 +25,7 @@ static PyObject * partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - PyObject *func; + PyObject *func, *pargs, *nargs, *pkw; partialobject *pto; if (PyTuple_GET_SIZE(args) < 1) { @@ -34,7 +34,16 @@ return NULL; } + pargs = pkw = Py_None; func = PyTuple_GET_ITEM(args, 0); + if (Py_TYPE(func) == &partial_type && type == &partial_type) { + partialobject *part = (partialobject *)func; + if (part->dict == NULL) { + pargs = part->args; + pkw = part->kw; + func = part->fn; + } + } if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, "the first argument must be callable"); @@ -48,21 +57,53 @@ pto->fn = func; Py_INCREF(func); - pto->args = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX); - if (pto->args == NULL) { + + nargs = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX); + if (nargs == NULL) { + pto->args = NULL; pto->kw = NULL; Py_DECREF(pto); return NULL; } + if (pargs == Py_None || PyTuple_GET_SIZE(pargs) == 0) { + pto->args = nargs; + Py_INCREF(nargs); + } + else if (PyTuple_GET_SIZE(nargs) == 0) { + pto->args = pargs; + Py_INCREF(pargs); + } + else { + pto->args = PySequence_Concat(pargs, nargs); + if (pto->args == NULL) { + pto->kw = NULL; + Py_DECREF(pto); + return NULL; + } + } + Py_DECREF(nargs); + if (kw != NULL) { - pto->kw = PyDict_Copy(kw); + if (pkw == Py_None) { + pto->kw = PyDict_Copy(kw); + } + else { + pto->kw = PyDict_Copy(pkw); + if (pto->kw != NULL) { + if (PyDict_Merge(pto->kw, kw, 1) != 0) { + Py_DECREF(pto); + return NULL; + } + } + } if (pto->kw == NULL) { Py_DECREF(pto); return NULL; } - } else { - pto->kw = Py_None; - Py_INCREF(Py_None); + } + else { + pto->kw = pkw; + Py_INCREF(pkw); } pto->weakreflist = NULL; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 02:59:45 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 01:59:45 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_remove_mention?= =?utf-8?q?_of_Python_2=2E2_and_2=2E3?= Message-ID: <20150302015945.16428.59904@psf.io> https://hg.python.org/cpython/rev/26c7a9224e38 changeset: 94806:26c7a9224e38 branch: 2.7 parent: 94800:663a83c1d42d user: Benjamin Peterson date: Sun Mar 01 20:59:22 2015 -0500 summary: remove mention of Python 2.2 and 2.3 files: Misc/SpecialBuilds.txt | 11 +++++------ 1 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Misc/SpecialBuilds.txt b/Misc/SpecialBuilds.txt --- a/Misc/SpecialBuilds.txt +++ b/Misc/SpecialBuilds.txt @@ -212,12 +212,11 @@ Compile in support for Low Level TRACE-ing of the main interpreter loop. -When this preprocessor symbol is defined, before PyEval_EvalFrame (eval_frame in -2.3 and 2.2, eval_code2 before that) executes a frame's code it checks the -frame's global namespace for a variable "__lltrace__". If such a variable is -found, mounds of information about what the interpreter is doing are sprayed to -stdout, such as every opcode and opcode argument and values pushed onto and -popped off the value stack. +When this preprocessor symbol is defined, before PyEval_EvalFrame executes a +frame's code it checks the frame's global namespace for a variable +"__lltrace__". If such a variable is found, mounds of information about what +the interpreter is doing are sprayed to stdout, such as every opcode and opcode +argument and values pushed onto and popped off the value stack. Not useful very often, but very useful when needed. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 02:59:45 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 01:59:45 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_remove_mention?= =?utf-8?q?_of_Python_2=2E2_and_2=2E3?= Message-ID: <20150302015945.34508.36574@psf.io> https://hg.python.org/cpython/rev/6e801f60aba9 changeset: 94805:6e801f60aba9 branch: 3.4 parent: 94801:d7b3e722152d user: Benjamin Peterson date: Sun Mar 01 20:59:22 2015 -0500 summary: remove mention of Python 2.2 and 2.3 files: Misc/SpecialBuilds.txt | 11 +++++------ 1 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Misc/SpecialBuilds.txt b/Misc/SpecialBuilds.txt --- a/Misc/SpecialBuilds.txt +++ b/Misc/SpecialBuilds.txt @@ -216,12 +216,11 @@ Compile in support for Low Level TRACE-ing of the main interpreter loop. -When this preprocessor symbol is defined, before PyEval_EvalFrame (eval_frame in -2.3 and 2.2, eval_code2 before that) executes a frame's code it checks the -frame's global namespace for a variable "__lltrace__". If such a variable is -found, mounds of information about what the interpreter is doing are sprayed to -stdout, such as every opcode and opcode argument and values pushed onto and -popped off the value stack. +When this preprocessor symbol is defined, before PyEval_EvalFrame executes a +frame's code it checks the frame's global namespace for a variable +"__lltrace__". If such a variable is found, mounds of information about what +the interpreter is doing are sprayed to stdout, such as every opcode and opcode +argument and values pushed onto and popped off the value stack. Not useful very often, but very useful when needed. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 03:10:16 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 02:10:16 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <20150302015945.87101.90135@psf.io> https://hg.python.org/cpython/rev/6729446a5c55 changeset: 94807:6729446a5c55 parent: 94804:7839681ca931 parent: 94805:6e801f60aba9 user: Benjamin Peterson date: Sun Mar 01 20:59:41 2015 -0500 summary: merge 3.4 files: Misc/SpecialBuilds.txt | 11 +++++------ 1 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Misc/SpecialBuilds.txt b/Misc/SpecialBuilds.txt --- a/Misc/SpecialBuilds.txt +++ b/Misc/SpecialBuilds.txt @@ -216,12 +216,11 @@ Compile in support for Low Level TRACE-ing of the main interpreter loop. -When this preprocessor symbol is defined, before PyEval_EvalFrame (eval_frame in -2.3 and 2.2, eval_code2 before that) executes a frame's code it checks the -frame's global namespace for a variable "__lltrace__". If such a variable is -found, mounds of information about what the interpreter is doing are sprayed to -stdout, such as every opcode and opcode argument and values pushed onto and -popped off the value stack. +When this preprocessor symbol is defined, before PyEval_EvalFrame executes a +frame's code it checks the frame's global namespace for a variable +"__lltrace__". If such a variable is found, mounds of information about what +the interpreter is doing are sprayed to stdout, such as every opcode and opcode +argument and values pushed onto and popped off the value stack. Not useful very often, but very useful when needed. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 04:36:08 2015 From: python-checkins at python.org (berker.peksag) Date: Mon, 02 Mar 2015 03:36:08 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Add_a_versionadded_directi?= =?utf-8?q?ve_for_PEP_486=2E?= Message-ID: <20150302033607.39492.61614@psf.io> https://hg.python.org/cpython/rev/dbce76b93c8b changeset: 94808:dbce76b93c8b user: Berker Peksag date: Mon Mar 02 05:36:19 2015 +0200 summary: Add a versionadded directive for PEP 486. files: Doc/using/windows.rst | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -407,6 +407,8 @@ Virtual environments ^^^^^^^^^^^^^^^^^^^^ +.. versionadded:: 3.5 + If the launcher is run with no explicit Python version specification, and a virtual environment (created with the standard library :mod:`venv` module or the external ``virtualenv`` tool) active, the launcher will run the virtual -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 05:01:26 2015 From: python-checkins at python.org (berker.peksag) Date: Mon, 02 Mar 2015 04:01:26 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzMzg3?= =?utf-8?q?=3A_Skip_test=5Fissue16464_if_it_raises_an_5xx_error=2E?= Message-ID: <20150302040126.19146.54308@psf.io> https://hg.python.org/cpython/rev/7e783289bc12 changeset: 94809:7e783289bc12 branch: 3.4 parent: 94805:6e801f60aba9 user: Berker Peksag date: Mon Mar 02 06:01:01 2015 +0200 summary: Issue #23387: Skip test_issue16464 if it raises an 5xx error. Also, remove support.run_doctest() since there is no doctests in test_urllib2 and urllib.request. files: Lib/test/support/__init__.py | 2 + Lib/test/test_urllib2.py | 32 ++++++++--------------- 2 files changed, 13 insertions(+), 21 deletions(-) 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 @@ -1324,6 +1324,8 @@ n = getattr(err, 'errno', None) if (isinstance(err, socket.timeout) or (isinstance(err, socket.gaierror) and n in gai_errnos) or + (isinstance(err, urllib.error.HTTPError) and + 500 <= err.code <= 599) or (isinstance(err, urllib.error.URLError) and "ConnectionRefusedError" in err.reason) or n in captured_errnos): diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1469,17 +1469,18 @@ @unittest.skipUnless(support.is_resource_enabled('network'), 'test requires network access') def test_issue16464(self): - opener = urllib.request.build_opener() - request = urllib.request.Request("http://www.example.com/") - self.assertEqual(None, request.data) + with support.transient_internet("http://www.example.com/"): + opener = urllib.request.build_opener() + request = urllib.request.Request("http://www.example.com/") + self.assertEqual(None, request.data) - opener.open(request, "1".encode("us-ascii")) - self.assertEqual(b"1", request.data) - self.assertEqual("1", request.get_header("Content-length")) + opener.open(request, "1".encode("us-ascii")) + self.assertEqual(b"1", request.data) + self.assertEqual("1", request.get_header("Content-length")) - opener.open(request, "1234567890".encode("us-ascii")) - self.assertEqual(b"1234567890", request.data) - self.assertEqual("10", request.get_header("Content-length")) + opener.open(request, "1234567890".encode("us-ascii")) + self.assertEqual(b"1234567890", request.data) + self.assertEqual("10", request.get_header("Content-length")) def test_HTTPError_interface(self): """ @@ -1630,17 +1631,6 @@ req = Request(url) self.assertEqual(req.get_full_url(), req.full_url) -def test_main(verbose=None): - from test import test_urllib2 - support.run_doctest(test_urllib2, verbose) - support.run_doctest(urllib.request, verbose) - tests = (TrivialTests, - OpenerDirectorTests, - HandlerTests, - MiscTests, - RequestTests, - RequestHdrsTests) - support.run_unittest(*tests) if __name__ == "__main__": - test_main(verbose=True) + unittest.main() -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 05:01:29 2015 From: python-checkins at python.org (berker.peksag) Date: Mon, 02 Mar 2015 04:01:29 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323387=3A_Skip_test=5Fissue16464_if_it_raises_an?= =?utf-8?q?_5xx_error=2E?= Message-ID: <20150302040126.53141.49809@psf.io> https://hg.python.org/cpython/rev/4cadc2c65609 changeset: 94810:4cadc2c65609 parent: 94808:dbce76b93c8b parent: 94809:7e783289bc12 user: Berker Peksag date: Mon Mar 02 06:01:37 2015 +0200 summary: Issue #23387: Skip test_issue16464 if it raises an 5xx error. Also, remove support.run_doctest() since there is no doctests in test_urllib2 and urllib.request. files: Lib/test/support/__init__.py | 2 + Lib/test/test_urllib2.py | 32 ++++++++--------------- 2 files changed, 13 insertions(+), 21 deletions(-) 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 @@ -1325,6 +1325,8 @@ n = getattr(err, 'errno', None) if (isinstance(err, socket.timeout) or (isinstance(err, socket.gaierror) and n in gai_errnos) or + (isinstance(err, urllib.error.HTTPError) and + 500 <= err.code <= 599) or (isinstance(err, urllib.error.URLError) and "ConnectionRefusedError" in err.reason) or n in captured_errnos): diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1484,17 +1484,18 @@ @unittest.skipUnless(support.is_resource_enabled('network'), 'test requires network access') def test_issue16464(self): - opener = urllib.request.build_opener() - request = urllib.request.Request("http://www.example.com/") - self.assertEqual(None, request.data) + with support.transient_internet("http://www.example.com/"): + opener = urllib.request.build_opener() + request = urllib.request.Request("http://www.example.com/") + self.assertEqual(None, request.data) - opener.open(request, "1".encode("us-ascii")) - self.assertEqual(b"1", request.data) - self.assertEqual("1", request.get_header("Content-length")) + opener.open(request, "1".encode("us-ascii")) + self.assertEqual(b"1", request.data) + self.assertEqual("1", request.get_header("Content-length")) - opener.open(request, "1234567890".encode("us-ascii")) - self.assertEqual(b"1234567890", request.data) - self.assertEqual("10", request.get_header("Content-length")) + opener.open(request, "1234567890".encode("us-ascii")) + self.assertEqual(b"1234567890", request.data) + self.assertEqual("10", request.get_header("Content-length")) def test_HTTPError_interface(self): """ @@ -1645,17 +1646,6 @@ req = Request(url) self.assertEqual(req.get_full_url(), req.full_url) -def test_main(verbose=None): - from test import test_urllib2 - support.run_doctest(test_urllib2, verbose) - support.run_doctest(urllib.request, verbose) - tests = (TrivialTests, - OpenerDirectorTests, - HandlerTests, - MiscTests, - RequestTests, - RequestHdrsTests) - support.run_unittest(*tests) if __name__ == "__main__": - test_main(verbose=True) + unittest.main() -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 05:33:50 2015 From: python-checkins at python.org (berker.peksag) Date: Mon, 02 Mar 2015 04:33:50 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320521=3A_Change_=60=60TOS=60=60_to_TOS_in_dis_d?= =?utf-8?q?ocumentation=2E?= Message-ID: <20150302043350.87093.39502@psf.io> https://hg.python.org/cpython/rev/a331d71bdc0a changeset: 94812:a331d71bdc0a parent: 94810:4cadc2c65609 parent: 94811:617feb5d8af2 user: Berker Peksag date: Mon Mar 02 06:34:00 2015 +0200 summary: Issue #20521: Change ``TOS`` to TOS in dis documentation. TOS is an abbreviation of top-of-stack. Patch by Sven Berkvens-Matthijsse. files: Doc/library/dis.rst | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -554,12 +554,12 @@ .. opcode:: YIELD_VALUE - Pops ``TOS`` and yields it from a :term:`generator`. + Pops TOS and yields it from a :term:`generator`. .. opcode:: YIELD_FROM - Pops ``TOS`` and delegates to it as a subiterator from a :term:`generator`. + Pops TOS and delegates to it as a subiterator from a :term:`generator`. .. versionadded:: 3.3 @@ -780,9 +780,9 @@ .. opcode:: FOR_ITER (delta) - ``TOS`` is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. + TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. If this yields a new value, push it on the stack (leaving the iterator below - it). If the iterator indicates it is exhausted ``TOS`` is popped, and the + it). If the iterator indicates it is exhausted TOS is popped, and the byte code counter is incremented by *delta*. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 05:33:50 2015 From: python-checkins at python.org (berker.peksag) Date: Mon, 02 Mar 2015 04:33:50 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIwNTIx?= =?utf-8?q?=3A_Change_=60=60TOS=60=60_to_TOS_in_dis_documentation=2E?= Message-ID: <20150302043350.39447.88813@psf.io> https://hg.python.org/cpython/rev/617feb5d8af2 changeset: 94811:617feb5d8af2 branch: 3.4 parent: 94809:7e783289bc12 user: Berker Peksag date: Mon Mar 02 06:33:30 2015 +0200 summary: Issue #20521: Change ``TOS`` to TOS in dis documentation. TOS is an abbreviation of top-of-stack. Patch by Sven Berkvens-Matthijsse. files: Doc/library/dis.rst | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -544,12 +544,12 @@ .. opcode:: YIELD_VALUE - Pops ``TOS`` and yields it from a :term:`generator`. + Pops TOS and yields it from a :term:`generator`. .. opcode:: YIELD_FROM - Pops ``TOS`` and delegates to it as a subiterator from a :term:`generator`. + Pops TOS and delegates to it as a subiterator from a :term:`generator`. .. versionadded:: 3.3 @@ -770,9 +770,9 @@ .. opcode:: FOR_ITER (delta) - ``TOS`` is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. + TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. If this yields a new value, push it on the stack (leaving the iterator below - it). If the iterator indicates it is exhausted ``TOS`` is popped, and the + it). If the iterator indicates it is exhausted TOS is popped, and the byte code counter is incremented by *delta*. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 05:54:21 2015 From: python-checkins at python.org (berker.peksag) Date: Mon, 02 Mar 2015 04:54:21 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323477=3A_Improve_test_coverage_of_wsgiref=2Esim?= =?utf-8?q?ple=5Fserver=2E?= Message-ID: <20150302045421.19164.28688@psf.io> https://hg.python.org/cpython/rev/0c786d1fb372 changeset: 94814:0c786d1fb372 parent: 94812:a331d71bdc0a parent: 94813:9f2ef8654bf8 user: Berker Peksag date: Mon Mar 02 06:54:27 2015 +0200 summary: Issue #23477: Improve test coverage of wsgiref.simple_server. The test checks that the environ argument contains correct headers, querystring and path information. Patch by Alex Shkop. files: Lib/test/test_wsgiref.py | 25 +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -48,6 +48,18 @@ ]) return [b"Hello, world!"] + +def header_app(environ, start_response): + start_response("200 OK", [ + ('Content-Type', 'text/plain'), + ('Date', 'Mon, 05 Jun 2006 18:49:54 GMT') + ]) + return [';'.join([ + environ['HTTP_X_TEST_HEADER'], environ['QUERY_STRING'], + environ['PATH_INFO'] + ]).encode('iso-8859-1')] + + def run_amock(app=hello_app, data=b"GET / HTTP/1.0\n\n"): server = make_server("", 80, app, MockServer, MockHandler) inp = BufferedReader(BytesIO(data)) @@ -118,6 +130,19 @@ out, err = run_amock() self.check_hello(out) + def test_environ(self): + request = ( + b"GET /p%61th/?query=test HTTP/1.0\n" + b"X-Test-Header: Python test \n" + b"X-Test-Header: Python test 2\n" + b"Content-Length: 0\n\n" + ) + out, err = run_amock(header_app, request) + self.assertEqual( + out.splitlines()[-1], + b"Python test,Python test 2;query=test;/path/" + ) + def test_request_length(self): out, err = run_amock(data=b"GET " + (b"x" * 65537) + b" HTTP/1.0\n\n") self.assertEqual(out.splitlines()[0], -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 05:54:21 2015 From: python-checkins at python.org (berker.peksag) Date: Mon, 02 Mar 2015 04:54:21 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNDc3?= =?utf-8?q?=3A_Improve_test_coverage_of_wsgiref=2Esimple=5Fserver=2E?= Message-ID: <20150302045421.19162.36812@psf.io> https://hg.python.org/cpython/rev/9f2ef8654bf8 changeset: 94813:9f2ef8654bf8 branch: 3.4 parent: 94811:617feb5d8af2 user: Berker Peksag date: Mon Mar 02 06:53:33 2015 +0200 summary: Issue #23477: Improve test coverage of wsgiref.simple_server. The test checks that the environ argument contains correct headers, querystring and path information. Patch by Alex Shkop. files: Lib/test/test_wsgiref.py | 25 +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -48,6 +48,18 @@ ]) return [b"Hello, world!"] + +def header_app(environ, start_response): + start_response("200 OK", [ + ('Content-Type', 'text/plain'), + ('Date', 'Mon, 05 Jun 2006 18:49:54 GMT') + ]) + return [';'.join([ + environ['HTTP_X_TEST_HEADER'], environ['QUERY_STRING'], + environ['PATH_INFO'] + ]).encode('iso-8859-1')] + + def run_amock(app=hello_app, data=b"GET / HTTP/1.0\n\n"): server = make_server("", 80, app, MockServer, MockHandler) inp = BufferedReader(BytesIO(data)) @@ -118,6 +130,19 @@ out, err = run_amock() self.check_hello(out) + def test_environ(self): + request = ( + b"GET /p%61th/?query=test HTTP/1.0\n" + b"X-Test-Header: Python test \n" + b"X-Test-Header: Python test 2\n" + b"Content-Length: 0\n\n" + ) + out, err = run_amock(header_app, request) + self.assertEqual( + out.splitlines()[-1], + b"Python test,Python test 2;query=test;/path/" + ) + def test_request_length(self): out, err = run_amock(data=b"GET " + (b"x" * 65537) + b" HTTP/1.0\n\n") self.assertEqual(out.splitlines()[0], -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 06:40:50 2015 From: python-checkins at python.org (berker.peksag) Date: Mon, 02 Mar 2015 05:40:50 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNTI3?= =?utf-8?q?=3A_Update_Gmail_port_number_for_STARTTLS_to_587=2E?= Message-ID: <20150302054049.10072.7797@psf.io> https://hg.python.org/cpython/rev/06d69688ea06 changeset: 94815:06d69688ea06 branch: 3.4 parent: 94813:9f2ef8654bf8 user: Berker Peksag date: Mon Mar 02 07:40:36 2015 +0200 summary: Issue #23527: Update Gmail port number for STARTTLS to 587. Patch by Alex Shkop. files: Lib/test/test_smtpnet.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_smtpnet.py b/Lib/test/test_smtpnet.py --- a/Lib/test/test_smtpnet.py +++ b/Lib/test/test_smtpnet.py @@ -21,7 +21,7 @@ class SmtpTest(unittest.TestCase): testServer = 'smtp.gmail.com' - remotePort = 25 + remotePort = 587 def test_connect_starttls(self): support.get_attribute(smtplib, 'SMTP_SSL') -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 06:40:50 2015 From: python-checkins at python.org (berker.peksag) Date: Mon, 02 Mar 2015 05:40:50 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323527=3A_Update_Gmail_port_number_for_STARTTLS_?= =?utf-8?q?to_587=2E?= Message-ID: <20150302054049.15298.66452@psf.io> https://hg.python.org/cpython/rev/9ff477cd79da changeset: 94816:9ff477cd79da parent: 94814:0c786d1fb372 parent: 94815:06d69688ea06 user: Berker Peksag date: Mon Mar 02 07:41:00 2015 +0200 summary: Issue #23527: Update Gmail port number for STARTTLS to 587. Patch by Alex Shkop. files: Lib/test/test_smtpnet.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_smtpnet.py b/Lib/test/test_smtpnet.py --- a/Lib/test/test_smtpnet.py +++ b/Lib/test/test_smtpnet.py @@ -21,7 +21,7 @@ class SmtpTest(unittest.TestCase): testServer = 'smtp.gmail.com' - remotePort = 25 + remotePort = 587 def test_connect_starttls(self): support.get_attribute(smtplib, 'SMTP_SSL') -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 13:32:44 2015 From: python-checkins at python.org (nick.coghlan) Date: Mon, 02 Mar 2015 12:32:44 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_Withdraw_PEP_422_in_favour_of?= =?utf-8?q?_PEP_487?= Message-ID: <20150302123238.77532.50469@psf.io> https://hg.python.org/peps/rev/4db0c1a3e363 changeset: 5717:4db0c1a3e363 user: Nick Coghlan date: Mon Mar 02 22:32:31 2015 +1000 summary: Withdraw PEP 422 in favour of PEP 487 files: pep-0422.txt | 17 +++++++---------- 1 files changed, 7 insertions(+), 10 deletions(-) diff --git a/pep-0422.txt b/pep-0422.txt --- a/pep-0422.txt +++ b/pep-0422.txt @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: Nick Coghlan , Daniel Urban -Status: Deferred +Status: Withdrawn Type: Standards Track Content-Type: text/x-rst Created: 5-Jun-2012 @@ -28,16 +28,13 @@ introduction to the full power Python's metaclass machinery. -PEP Deferral -============ +PEP Withdrawal +============== -Currently deferred pending updates to address feedback received prior to the -Python 3.4 release (see TODO note below). - -Note that I'd be open to relinquishing the PEP to a new champion if anyone -would like to propose it for Python 3.5 (while I've made a few updates to -the design recently it doesn't currently look like I will be in a position -to push it forward myself in the 3.5 time frame). +This proposal has been withdrawn in favour of Martin Teichmann's proposal +in PEP 487, which achieves the same goals through a simpler, easier to use +``__init_subclass__`` hook that simply isn't invoked for the base class +that defines the hook. Background -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Mon Mar 2 15:33:54 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 02 Mar 2015 14:33:54 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Added_more_tes?= =?utf-8?q?ts_for_urllib_utility_functions=2E?= Message-ID: <20150302143354.16430.91586@psf.io> https://hg.python.org/cpython/rev/5d3a5557f2d5 changeset: 94817:5d3a5557f2d5 branch: 2.7 parent: 94806:26c7a9224e38 user: Serhiy Storchaka date: Mon Mar 02 16:31:57 2015 +0200 summary: Added more tests for urllib utility functions. These functions are not documented but used in third-party code. files: Lib/test/test_urllib.py | 116 ++++++++++++++++++++++++--- 1 files changed, 103 insertions(+), 13 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 @@ -773,21 +773,55 @@ class Utility_Tests(unittest.TestCase): """Testcase to test the various utility functions in the urllib.""" + # In Python 3 this test class is moved to test_urlparse. + + def test_splittype(self): + splittype = urllib.splittype + self.assertEqual(splittype('type:opaquestring'), ('type', 'opaquestring')) + self.assertEqual(splittype('opaquestring'), (None, 'opaquestring')) + self.assertEqual(splittype(':opaquestring'), (None, ':opaquestring')) + self.assertEqual(splittype('type:'), ('type', '')) + self.assertEqual(splittype('type:opaque:string'), ('type', 'opaque:string')) + + def test_splithost(self): + splithost = urllib.splithost + self.assertEqual(splithost('//www.example.org:80/foo/bar/baz.html'), + ('www.example.org:80', '/foo/bar/baz.html')) + self.assertEqual(splithost('//www.example.org:80'), + ('www.example.org:80', '')) + self.assertEqual(splithost('/foo/bar/baz.html'), + (None, '/foo/bar/baz.html')) + + def test_splituser(self): + splituser = urllib.splituser + self.assertEqual(splituser('User:Pass at www.python.org:080'), + ('User:Pass', 'www.python.org:080')) + self.assertEqual(splituser('@www.python.org:080'), + ('', 'www.python.org:080')) + self.assertEqual(splituser('www.python.org:080'), + (None, 'www.python.org:080')) + self.assertEqual(splituser('User:Pass@'), + ('User:Pass', '')) + self.assertEqual(splituser('User at example.com:Pass at www.python.org:080'), + ('User at example.com:Pass', 'www.python.org:080')) def test_splitpasswd(self): - """Some of the password examples are not sensible, but it is added to - confirming to RFC2617 and addressing issue4675. - """ - self.assertEqual(('user', 'ab'),urllib.splitpasswd('user:ab')) - self.assertEqual(('user', 'a\nb'),urllib.splitpasswd('user:a\nb')) - self.assertEqual(('user', 'a\tb'),urllib.splitpasswd('user:a\tb')) - self.assertEqual(('user', 'a\rb'),urllib.splitpasswd('user:a\rb')) - self.assertEqual(('user', 'a\fb'),urllib.splitpasswd('user:a\fb')) - self.assertEqual(('user', 'a\vb'),urllib.splitpasswd('user:a\vb')) - self.assertEqual(('user', 'a:b'),urllib.splitpasswd('user:a:b')) - self.assertEqual(('user', 'a b'),urllib.splitpasswd('user:a b')) - self.assertEqual(('user 2', 'ab'),urllib.splitpasswd('user 2:ab')) - self.assertEqual(('user+1', 'a+b'),urllib.splitpasswd('user+1:a+b')) + # Some of the password examples are not sensible, but it is added to + # confirming to RFC2617 and addressing issue4675. + splitpasswd = urllib.splitpasswd + self.assertEqual(splitpasswd('user:ab'), ('user', 'ab')) + self.assertEqual(splitpasswd('user:a\nb'), ('user', 'a\nb')) + self.assertEqual(splitpasswd('user:a\tb'), ('user', 'a\tb')) + self.assertEqual(splitpasswd('user:a\rb'), ('user', 'a\rb')) + self.assertEqual(splitpasswd('user:a\fb'), ('user', 'a\fb')) + self.assertEqual(splitpasswd('user:a\vb'), ('user', 'a\vb')) + self.assertEqual(splitpasswd('user:a:b'), ('user', 'a:b')) + self.assertEqual(splitpasswd('user:a b'), ('user', 'a b')) + self.assertEqual(splitpasswd('user 2:ab'), ('user 2', 'ab')) + self.assertEqual(splitpasswd('user+1:a+b'), ('user+1', 'a+b')) + self.assertEqual(splitpasswd('user:'), ('user', '')) + self.assertEqual(splitpasswd('user'), ('user', None)) + self.assertEqual(splitpasswd(':ab'), ('', 'ab')) def test_splitport(self): splitport = urllib.splitport @@ -796,6 +830,9 @@ self.assertEqual(splitport('parrot:'), ('parrot', None)) self.assertEqual(splitport('127.0.0.1'), ('127.0.0.1', None)) self.assertEqual(splitport('parrot:cheese'), ('parrot:cheese', None)) + self.assertEqual(splitport('[::1]:88'), ('[::1]', '88')) + self.assertEqual(splitport('[::1]'), ('[::1]', None)) + self.assertEqual(splitport(':88'), ('', '88')) def test_splitnport(self): splitnport = urllib.splitnport @@ -809,6 +846,59 @@ self.assertEqual(splitnport('parrot:cheese'), ('parrot', None)) self.assertEqual(splitnport('parrot:cheese', 55), ('parrot', None)) + def test_splitquery(self): + # Normal cases are exercised by other tests; ensure that we also + # catch cases with no port specified (testcase ensuring coverage) + splitquery = urllib.splitquery + self.assertEqual(splitquery('http://python.org/fake?foo=bar'), + ('http://python.org/fake', 'foo=bar')) + self.assertEqual(splitquery('http://python.org/fake?foo=bar?'), + ('http://python.org/fake?foo=bar', '')) + self.assertEqual(splitquery('http://python.org/fake'), + ('http://python.org/fake', None)) + self.assertEqual(splitquery('?foo=bar'), ('', 'foo=bar')) + + def test_splittag(self): + splittag = urllib.splittag + self.assertEqual(splittag('http://example.com?foo=bar#baz'), + ('http://example.com?foo=bar', 'baz')) + self.assertEqual(splittag('http://example.com?foo=bar#'), + ('http://example.com?foo=bar', '')) + self.assertEqual(splittag('#baz'), ('', 'baz')) + self.assertEqual(splittag('http://example.com?foo=bar'), + ('http://example.com?foo=bar', None)) + self.assertEqual(splittag('http://example.com?foo=bar#baz#boo'), + ('http://example.com?foo=bar#baz', 'boo')) + + def test_splitattr(self): + splitattr = urllib.splitattr + self.assertEqual(splitattr('/path;attr1=value1;attr2=value2'), + ('/path', ['attr1=value1', 'attr2=value2'])) + self.assertEqual(splitattr('/path;'), ('/path', [''])) + self.assertEqual(splitattr(';attr1=value1;attr2=value2'), + ('', ['attr1=value1', 'attr2=value2'])) + self.assertEqual(splitattr('/path'), ('/path', [])) + + def test_splitvalue(self): + # Normal cases are exercised by other tests; test pathological cases + # with no key/value pairs. (testcase ensuring coverage) + splitvalue = urllib.splitvalue + self.assertEqual(splitvalue('foo=bar'), ('foo', 'bar')) + self.assertEqual(splitvalue('foo='), ('foo', '')) + self.assertEqual(splitvalue('=bar'), ('', 'bar')) + self.assertEqual(splitvalue('foobar'), ('foobar', None)) + self.assertEqual(splitvalue('foo=bar=baz'), ('foo', 'bar=baz')) + + def test_toBytes(self): + result = urllib.toBytes(u'http://www.python.org') + self.assertEqual(result, 'http://www.python.org') + self.assertRaises(UnicodeError, urllib.toBytes, + test_support.u(r'http://www.python.org/medi\u00e6val')) + + def test_unwrap(self): + url = urllib.unwrap('') + self.assertEqual(url, 'type://host/path') + class URLopener_Tests(unittest.TestCase): """Testcase to test the open method of URLopener class.""" -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 15:33:54 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 02 Mar 2015 14:33:54 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Added_more_tes?= =?utf-8?q?ts_for_urllib=2Eparse_utility_functions=2E?= Message-ID: <20150302143354.10066.89060@psf.io> https://hg.python.org/cpython/rev/a08759df248f changeset: 94818:a08759df248f branch: 3.4 parent: 94815:06d69688ea06 user: Serhiy Storchaka date: Mon Mar 02 16:32:29 2015 +0200 summary: Added more tests for urllib.parse utility functions. These functions are not documented but used in third-party code. files: Lib/test/test_urllib.py | 15 -- Lib/test/test_urlparse.py | 185 ++++++++++++++++++------- 2 files changed, 130 insertions(+), 70 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 @@ -1294,21 +1294,6 @@ class Utility_Tests(unittest.TestCase): """Testcase to test the various utility functions in the urllib.""" - def test_splitpasswd(self): - """Some of password examples are not sensible, but it is added to - confirming to RFC2617 and addressing issue4675. - """ - self.assertEqual(('user', 'ab'),urllib.parse.splitpasswd('user:ab')) - self.assertEqual(('user', 'a\nb'),urllib.parse.splitpasswd('user:a\nb')) - self.assertEqual(('user', 'a\tb'),urllib.parse.splitpasswd('user:a\tb')) - self.assertEqual(('user', 'a\rb'),urllib.parse.splitpasswd('user:a\rb')) - self.assertEqual(('user', 'a\fb'),urllib.parse.splitpasswd('user:a\fb')) - self.assertEqual(('user', 'a\vb'),urllib.parse.splitpasswd('user:a\vb')) - self.assertEqual(('user', 'a:b'),urllib.parse.splitpasswd('user:a:b')) - self.assertEqual(('user', 'a b'),urllib.parse.splitpasswd('user:a b')) - self.assertEqual(('user 2', 'ab'),urllib.parse.splitpasswd('user 2:ab')) - self.assertEqual(('user+1', 'a+b'),urllib.parse.splitpasswd('user+1:a+b')) - def test_thishost(self): """Test the urllib.request.thishost utility function returns a tuple""" self.assertIsInstance(urllib.request.thishost(), tuple) diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -1,4 +1,3 @@ -from test import support import unittest import urllib.parse @@ -749,52 +748,6 @@ errors="ignore") self.assertEqual(result, [('key', '\u0141-')]) - def test_splitport(self): - splitport = urllib.parse.splitport - self.assertEqual(splitport('parrot:88'), ('parrot', '88')) - self.assertEqual(splitport('parrot'), ('parrot', None)) - self.assertEqual(splitport('parrot:'), ('parrot', None)) - self.assertEqual(splitport('127.0.0.1'), ('127.0.0.1', None)) - self.assertEqual(splitport('parrot:cheese'), ('parrot:cheese', None)) - - def test_splitnport(self): - splitnport = urllib.parse.splitnport - self.assertEqual(splitnport('parrot:88'), ('parrot', 88)) - self.assertEqual(splitnport('parrot'), ('parrot', -1)) - self.assertEqual(splitnport('parrot', 55), ('parrot', 55)) - self.assertEqual(splitnport('parrot:'), ('parrot', -1)) - self.assertEqual(splitnport('parrot:', 55), ('parrot', 55)) - self.assertEqual(splitnport('127.0.0.1'), ('127.0.0.1', -1)) - self.assertEqual(splitnport('127.0.0.1', 55), ('127.0.0.1', 55)) - self.assertEqual(splitnport('parrot:cheese'), ('parrot', None)) - self.assertEqual(splitnport('parrot:cheese', 55), ('parrot', None)) - - def test_splitquery(self): - # Normal cases are exercised by other tests; ensure that we also - # catch cases with no port specified (testcase ensuring coverage) - result = urllib.parse.splitquery('http://python.org/fake?foo=bar') - self.assertEqual(result, ('http://python.org/fake', 'foo=bar')) - result = urllib.parse.splitquery('http://python.org/fake?foo=bar?') - self.assertEqual(result, ('http://python.org/fake?foo=bar', '')) - result = urllib.parse.splitquery('http://python.org/fake') - self.assertEqual(result, ('http://python.org/fake', None)) - - def test_splitvalue(self): - # Normal cases are exercised by other tests; test pathological cases - # with no key/value pairs. (testcase ensuring coverage) - result = urllib.parse.splitvalue('foo=bar') - self.assertEqual(result, ('foo', 'bar')) - result = urllib.parse.splitvalue('foo=') - self.assertEqual(result, ('foo', '')) - result = urllib.parse.splitvalue('foobar') - self.assertEqual(result, ('foobar', None)) - - def test_to_bytes(self): - result = urllib.parse.to_bytes('http://www.python.org') - self.assertEqual(result, 'http://www.python.org') - self.assertRaises(UnicodeError, urllib.parse.to_bytes, - 'http://www.python.org/medi\u00e6val') - def test_urlencode_sequences(self): # Other tests incidentally urlencode things; test non-covered cases: # Sequence and object values. @@ -863,17 +816,139 @@ self.assertEqual(p1.path, '863-1234') self.assertEqual(p1.params, 'phone-context=+1-914-555') + def test_Quoter_repr(self): + quoter = urllib.parse.Quoter(urllib.parse._ALWAYS_SAFE) + self.assertIn('Quoter', repr(quoter)) + + +class Utility_Tests(unittest.TestCase): + """Testcase to test the various utility functions in the urllib.""" + # In Python 2 this test class was in test_urllib. + + def test_splittype(self): + splittype = urllib.parse.splittype + self.assertEqual(splittype('type:opaquestring'), ('type', 'opaquestring')) + self.assertEqual(splittype('opaquestring'), (None, 'opaquestring')) + self.assertEqual(splittype(':opaquestring'), (None, ':opaquestring')) + self.assertEqual(splittype('type:'), ('type', '')) + self.assertEqual(splittype('type:opaque:string'), ('type', 'opaque:string')) + + def test_splithost(self): + splithost = urllib.parse.splithost + self.assertEqual(splithost('//www.example.org:80/foo/bar/baz.html'), + ('www.example.org:80', '/foo/bar/baz.html')) + self.assertEqual(splithost('//www.example.org:80'), + ('www.example.org:80', '')) + self.assertEqual(splithost('/foo/bar/baz.html'), + (None, '/foo/bar/baz.html')) + + def test_splituser(self): + splituser = urllib.parse.splituser + self.assertEqual(splituser('User:Pass at www.python.org:080'), + ('User:Pass', 'www.python.org:080')) + self.assertEqual(splituser('@www.python.org:080'), + ('', 'www.python.org:080')) + self.assertEqual(splituser('www.python.org:080'), + (None, 'www.python.org:080')) + self.assertEqual(splituser('User:Pass@'), + ('User:Pass', '')) + self.assertEqual(splituser('User at example.com:Pass at www.python.org:080'), + ('User at example.com:Pass', 'www.python.org:080')) + + def test_splitpasswd(self): + # Some of the password examples are not sensible, but it is added to + # confirming to RFC2617 and addressing issue4675. + splitpasswd = urllib.parse.splitpasswd + self.assertEqual(splitpasswd('user:ab'), ('user', 'ab')) + self.assertEqual(splitpasswd('user:a\nb'), ('user', 'a\nb')) + self.assertEqual(splitpasswd('user:a\tb'), ('user', 'a\tb')) + self.assertEqual(splitpasswd('user:a\rb'), ('user', 'a\rb')) + self.assertEqual(splitpasswd('user:a\fb'), ('user', 'a\fb')) + self.assertEqual(splitpasswd('user:a\vb'), ('user', 'a\vb')) + self.assertEqual(splitpasswd('user:a:b'), ('user', 'a:b')) + self.assertEqual(splitpasswd('user:a b'), ('user', 'a b')) + self.assertEqual(splitpasswd('user 2:ab'), ('user 2', 'ab')) + self.assertEqual(splitpasswd('user+1:a+b'), ('user+1', 'a+b')) + self.assertEqual(splitpasswd('user:'), ('user', '')) + self.assertEqual(splitpasswd('user'), ('user', None)) + self.assertEqual(splitpasswd(':ab'), ('', 'ab')) + + def test_splitport(self): + splitport = urllib.parse.splitport + self.assertEqual(splitport('parrot:88'), ('parrot', '88')) + self.assertEqual(splitport('parrot'), ('parrot', None)) + self.assertEqual(splitport('parrot:'), ('parrot', None)) + self.assertEqual(splitport('127.0.0.1'), ('127.0.0.1', None)) + self.assertEqual(splitport('parrot:cheese'), ('parrot:cheese', None)) + self.assertEqual(splitport('[::1]:88'), ('[::1]', '88')) + self.assertEqual(splitport('[::1]'), ('[::1]', None)) + self.assertEqual(splitport(':88'), ('', '88')) + + def test_splitnport(self): + splitnport = urllib.parse.splitnport + self.assertEqual(splitnport('parrot:88'), ('parrot', 88)) + self.assertEqual(splitnport('parrot'), ('parrot', -1)) + self.assertEqual(splitnport('parrot', 55), ('parrot', 55)) + self.assertEqual(splitnport('parrot:'), ('parrot', -1)) + self.assertEqual(splitnport('parrot:', 55), ('parrot', 55)) + self.assertEqual(splitnport('127.0.0.1'), ('127.0.0.1', -1)) + self.assertEqual(splitnport('127.0.0.1', 55), ('127.0.0.1', 55)) + self.assertEqual(splitnport('parrot:cheese'), ('parrot', None)) + self.assertEqual(splitnport('parrot:cheese', 55), ('parrot', None)) + + def test_splitquery(self): + # Normal cases are exercised by other tests; ensure that we also + # catch cases with no port specified (testcase ensuring coverage) + splitquery = urllib.parse.splitquery + self.assertEqual(splitquery('http://python.org/fake?foo=bar'), + ('http://python.org/fake', 'foo=bar')) + self.assertEqual(splitquery('http://python.org/fake?foo=bar?'), + ('http://python.org/fake?foo=bar', '')) + self.assertEqual(splitquery('http://python.org/fake'), + ('http://python.org/fake', None)) + self.assertEqual(splitquery('?foo=bar'), ('', 'foo=bar')) + + def test_splittag(self): + splittag = urllib.parse.splittag + self.assertEqual(splittag('http://example.com?foo=bar#baz'), + ('http://example.com?foo=bar', 'baz')) + self.assertEqual(splittag('http://example.com?foo=bar#'), + ('http://example.com?foo=bar', '')) + self.assertEqual(splittag('#baz'), ('', 'baz')) + self.assertEqual(splittag('http://example.com?foo=bar'), + ('http://example.com?foo=bar', None)) + self.assertEqual(splittag('http://example.com?foo=bar#baz#boo'), + ('http://example.com?foo=bar#baz', 'boo')) + + def test_splitattr(self): + splitattr = urllib.parse.splitattr + self.assertEqual(splitattr('/path;attr1=value1;attr2=value2'), + ('/path', ['attr1=value1', 'attr2=value2'])) + self.assertEqual(splitattr('/path;'), ('/path', [''])) + self.assertEqual(splitattr(';attr1=value1;attr2=value2'), + ('', ['attr1=value1', 'attr2=value2'])) + self.assertEqual(splitattr('/path'), ('/path', [])) + + def test_splitvalue(self): + # Normal cases are exercised by other tests; test pathological cases + # with no key/value pairs. (testcase ensuring coverage) + splitvalue = urllib.parse.splitvalue + self.assertEqual(splitvalue('foo=bar'), ('foo', 'bar')) + self.assertEqual(splitvalue('foo='), ('foo', '')) + self.assertEqual(splitvalue('=bar'), ('', 'bar')) + self.assertEqual(splitvalue('foobar'), ('foobar', None)) + self.assertEqual(splitvalue('foo=bar=baz'), ('foo', 'bar=baz')) + + def test_to_bytes(self): + result = urllib.parse.to_bytes('http://www.python.org') + self.assertEqual(result, 'http://www.python.org') + self.assertRaises(UnicodeError, urllib.parse.to_bytes, + 'http://www.python.org/medi\u00e6val') + def test_unwrap(self): url = urllib.parse.unwrap('') self.assertEqual(url, 'type://host/path') - def test_Quoter_repr(self): - quoter = urllib.parse.Quoter(urllib.parse._ALWAYS_SAFE) - self.assertIn('Quoter', repr(quoter)) - - -def test_main(): - support.run_unittest(UrlParseTestCase) if __name__ == "__main__": - test_main() + unittest.main() -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 15:33:54 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Mon, 02 Mar 2015 14:33:54 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Added_more_tests_for_urllib=2Eparse_utility_functions=2E?= Message-ID: <20150302143354.15304.17232@psf.io> https://hg.python.org/cpython/rev/84ac7e437cdc changeset: 94819:84ac7e437cdc parent: 94816:9ff477cd79da parent: 94818:a08759df248f user: Serhiy Storchaka date: Mon Mar 02 16:33:08 2015 +0200 summary: Added more tests for urllib.parse utility functions. These functions are not documented but used in third-party code. files: Lib/test/test_urllib.py | 15 -- Lib/test/test_urlparse.py | 185 ++++++++++++++++++------- 2 files changed, 130 insertions(+), 70 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 @@ -1298,21 +1298,6 @@ class Utility_Tests(unittest.TestCase): """Testcase to test the various utility functions in the urllib.""" - def test_splitpasswd(self): - """Some of password examples are not sensible, but it is added to - confirming to RFC2617 and addressing issue4675. - """ - self.assertEqual(('user', 'ab'),urllib.parse.splitpasswd('user:ab')) - self.assertEqual(('user', 'a\nb'),urllib.parse.splitpasswd('user:a\nb')) - self.assertEqual(('user', 'a\tb'),urllib.parse.splitpasswd('user:a\tb')) - self.assertEqual(('user', 'a\rb'),urllib.parse.splitpasswd('user:a\rb')) - self.assertEqual(('user', 'a\fb'),urllib.parse.splitpasswd('user:a\fb')) - self.assertEqual(('user', 'a\vb'),urllib.parse.splitpasswd('user:a\vb')) - self.assertEqual(('user', 'a:b'),urllib.parse.splitpasswd('user:a:b')) - self.assertEqual(('user', 'a b'),urllib.parse.splitpasswd('user:a b')) - self.assertEqual(('user 2', 'ab'),urllib.parse.splitpasswd('user 2:ab')) - self.assertEqual(('user+1', 'a+b'),urllib.parse.splitpasswd('user+1:a+b')) - def test_thishost(self): """Test the urllib.request.thishost utility function returns a tuple""" self.assertIsInstance(urllib.request.thishost(), tuple) diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -1,4 +1,3 @@ -from test import support import unittest import urllib.parse @@ -769,52 +768,6 @@ errors="ignore") self.assertEqual(result, [('key', '\u0141-')]) - def test_splitport(self): - splitport = urllib.parse.splitport - self.assertEqual(splitport('parrot:88'), ('parrot', '88')) - self.assertEqual(splitport('parrot'), ('parrot', None)) - self.assertEqual(splitport('parrot:'), ('parrot', None)) - self.assertEqual(splitport('127.0.0.1'), ('127.0.0.1', None)) - self.assertEqual(splitport('parrot:cheese'), ('parrot:cheese', None)) - - def test_splitnport(self): - splitnport = urllib.parse.splitnport - self.assertEqual(splitnport('parrot:88'), ('parrot', 88)) - self.assertEqual(splitnport('parrot'), ('parrot', -1)) - self.assertEqual(splitnport('parrot', 55), ('parrot', 55)) - self.assertEqual(splitnport('parrot:'), ('parrot', -1)) - self.assertEqual(splitnport('parrot:', 55), ('parrot', 55)) - self.assertEqual(splitnport('127.0.0.1'), ('127.0.0.1', -1)) - self.assertEqual(splitnport('127.0.0.1', 55), ('127.0.0.1', 55)) - self.assertEqual(splitnport('parrot:cheese'), ('parrot', None)) - self.assertEqual(splitnport('parrot:cheese', 55), ('parrot', None)) - - def test_splitquery(self): - # Normal cases are exercised by other tests; ensure that we also - # catch cases with no port specified (testcase ensuring coverage) - result = urllib.parse.splitquery('http://python.org/fake?foo=bar') - self.assertEqual(result, ('http://python.org/fake', 'foo=bar')) - result = urllib.parse.splitquery('http://python.org/fake?foo=bar?') - self.assertEqual(result, ('http://python.org/fake?foo=bar', '')) - result = urllib.parse.splitquery('http://python.org/fake') - self.assertEqual(result, ('http://python.org/fake', None)) - - def test_splitvalue(self): - # Normal cases are exercised by other tests; test pathological cases - # with no key/value pairs. (testcase ensuring coverage) - result = urllib.parse.splitvalue('foo=bar') - self.assertEqual(result, ('foo', 'bar')) - result = urllib.parse.splitvalue('foo=') - self.assertEqual(result, ('foo', '')) - result = urllib.parse.splitvalue('foobar') - self.assertEqual(result, ('foobar', None)) - - def test_to_bytes(self): - result = urllib.parse.to_bytes('http://www.python.org') - self.assertEqual(result, 'http://www.python.org') - self.assertRaises(UnicodeError, urllib.parse.to_bytes, - 'http://www.python.org/medi\u00e6val') - def test_urlencode_sequences(self): # Other tests incidentally urlencode things; test non-covered cases: # Sequence and object values. @@ -883,17 +836,139 @@ self.assertEqual(p1.path, '863-1234') self.assertEqual(p1.params, 'phone-context=+1-914-555') + def test_Quoter_repr(self): + quoter = urllib.parse.Quoter(urllib.parse._ALWAYS_SAFE) + self.assertIn('Quoter', repr(quoter)) + + +class Utility_Tests(unittest.TestCase): + """Testcase to test the various utility functions in the urllib.""" + # In Python 2 this test class was in test_urllib. + + def test_splittype(self): + splittype = urllib.parse.splittype + self.assertEqual(splittype('type:opaquestring'), ('type', 'opaquestring')) + self.assertEqual(splittype('opaquestring'), (None, 'opaquestring')) + self.assertEqual(splittype(':opaquestring'), (None, ':opaquestring')) + self.assertEqual(splittype('type:'), ('type', '')) + self.assertEqual(splittype('type:opaque:string'), ('type', 'opaque:string')) + + def test_splithost(self): + splithost = urllib.parse.splithost + self.assertEqual(splithost('//www.example.org:80/foo/bar/baz.html'), + ('www.example.org:80', '/foo/bar/baz.html')) + self.assertEqual(splithost('//www.example.org:80'), + ('www.example.org:80', '')) + self.assertEqual(splithost('/foo/bar/baz.html'), + (None, '/foo/bar/baz.html')) + + def test_splituser(self): + splituser = urllib.parse.splituser + self.assertEqual(splituser('User:Pass at www.python.org:080'), + ('User:Pass', 'www.python.org:080')) + self.assertEqual(splituser('@www.python.org:080'), + ('', 'www.python.org:080')) + self.assertEqual(splituser('www.python.org:080'), + (None, 'www.python.org:080')) + self.assertEqual(splituser('User:Pass@'), + ('User:Pass', '')) + self.assertEqual(splituser('User at example.com:Pass at www.python.org:080'), + ('User at example.com:Pass', 'www.python.org:080')) + + def test_splitpasswd(self): + # Some of the password examples are not sensible, but it is added to + # confirming to RFC2617 and addressing issue4675. + splitpasswd = urllib.parse.splitpasswd + self.assertEqual(splitpasswd('user:ab'), ('user', 'ab')) + self.assertEqual(splitpasswd('user:a\nb'), ('user', 'a\nb')) + self.assertEqual(splitpasswd('user:a\tb'), ('user', 'a\tb')) + self.assertEqual(splitpasswd('user:a\rb'), ('user', 'a\rb')) + self.assertEqual(splitpasswd('user:a\fb'), ('user', 'a\fb')) + self.assertEqual(splitpasswd('user:a\vb'), ('user', 'a\vb')) + self.assertEqual(splitpasswd('user:a:b'), ('user', 'a:b')) + self.assertEqual(splitpasswd('user:a b'), ('user', 'a b')) + self.assertEqual(splitpasswd('user 2:ab'), ('user 2', 'ab')) + self.assertEqual(splitpasswd('user+1:a+b'), ('user+1', 'a+b')) + self.assertEqual(splitpasswd('user:'), ('user', '')) + self.assertEqual(splitpasswd('user'), ('user', None)) + self.assertEqual(splitpasswd(':ab'), ('', 'ab')) + + def test_splitport(self): + splitport = urllib.parse.splitport + self.assertEqual(splitport('parrot:88'), ('parrot', '88')) + self.assertEqual(splitport('parrot'), ('parrot', None)) + self.assertEqual(splitport('parrot:'), ('parrot', None)) + self.assertEqual(splitport('127.0.0.1'), ('127.0.0.1', None)) + self.assertEqual(splitport('parrot:cheese'), ('parrot:cheese', None)) + self.assertEqual(splitport('[::1]:88'), ('[::1]', '88')) + self.assertEqual(splitport('[::1]'), ('[::1]', None)) + self.assertEqual(splitport(':88'), ('', '88')) + + def test_splitnport(self): + splitnport = urllib.parse.splitnport + self.assertEqual(splitnport('parrot:88'), ('parrot', 88)) + self.assertEqual(splitnport('parrot'), ('parrot', -1)) + self.assertEqual(splitnport('parrot', 55), ('parrot', 55)) + self.assertEqual(splitnport('parrot:'), ('parrot', -1)) + self.assertEqual(splitnport('parrot:', 55), ('parrot', 55)) + self.assertEqual(splitnport('127.0.0.1'), ('127.0.0.1', -1)) + self.assertEqual(splitnport('127.0.0.1', 55), ('127.0.0.1', 55)) + self.assertEqual(splitnport('parrot:cheese'), ('parrot', None)) + self.assertEqual(splitnport('parrot:cheese', 55), ('parrot', None)) + + def test_splitquery(self): + # Normal cases are exercised by other tests; ensure that we also + # catch cases with no port specified (testcase ensuring coverage) + splitquery = urllib.parse.splitquery + self.assertEqual(splitquery('http://python.org/fake?foo=bar'), + ('http://python.org/fake', 'foo=bar')) + self.assertEqual(splitquery('http://python.org/fake?foo=bar?'), + ('http://python.org/fake?foo=bar', '')) + self.assertEqual(splitquery('http://python.org/fake'), + ('http://python.org/fake', None)) + self.assertEqual(splitquery('?foo=bar'), ('', 'foo=bar')) + + def test_splittag(self): + splittag = urllib.parse.splittag + self.assertEqual(splittag('http://example.com?foo=bar#baz'), + ('http://example.com?foo=bar', 'baz')) + self.assertEqual(splittag('http://example.com?foo=bar#'), + ('http://example.com?foo=bar', '')) + self.assertEqual(splittag('#baz'), ('', 'baz')) + self.assertEqual(splittag('http://example.com?foo=bar'), + ('http://example.com?foo=bar', None)) + self.assertEqual(splittag('http://example.com?foo=bar#baz#boo'), + ('http://example.com?foo=bar#baz', 'boo')) + + def test_splitattr(self): + splitattr = urllib.parse.splitattr + self.assertEqual(splitattr('/path;attr1=value1;attr2=value2'), + ('/path', ['attr1=value1', 'attr2=value2'])) + self.assertEqual(splitattr('/path;'), ('/path', [''])) + self.assertEqual(splitattr(';attr1=value1;attr2=value2'), + ('', ['attr1=value1', 'attr2=value2'])) + self.assertEqual(splitattr('/path'), ('/path', [])) + + def test_splitvalue(self): + # Normal cases are exercised by other tests; test pathological cases + # with no key/value pairs. (testcase ensuring coverage) + splitvalue = urllib.parse.splitvalue + self.assertEqual(splitvalue('foo=bar'), ('foo', 'bar')) + self.assertEqual(splitvalue('foo='), ('foo', '')) + self.assertEqual(splitvalue('=bar'), ('', 'bar')) + self.assertEqual(splitvalue('foobar'), ('foobar', None)) + self.assertEqual(splitvalue('foo=bar=baz'), ('foo', 'bar=baz')) + + def test_to_bytes(self): + result = urllib.parse.to_bytes('http://www.python.org') + self.assertEqual(result, 'http://www.python.org') + self.assertRaises(UnicodeError, urllib.parse.to_bytes, + 'http://www.python.org/medi\u00e6val') + def test_unwrap(self): url = urllib.parse.unwrap('') self.assertEqual(url, 'type://host/path') - def test_Quoter_repr(self): - quoter = urllib.parse.Quoter(urllib.parse._ALWAYS_SAFE) - self.assertIn('Quoter', repr(quoter)) - - -def test_main(): - support.run_unittest(UrlParseTestCase) if __name__ == "__main__": - test_main() + unittest.main() -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 15:37:09 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 14:37:09 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_link_to_the_co?= =?utf-8?q?rrect_dis_method_or_function_=28closes_=2323561=29?= Message-ID: <20150302143707.34512.12742@psf.io> https://hg.python.org/cpython/rev/10b563c32f9d changeset: 94820:10b563c32f9d branch: 3.4 parent: 94818:a08759df248f user: Benjamin Peterson date: Mon Mar 02 09:27:43 2015 -0500 summary: link to the correct dis method or function (closes #23561) files: Doc/library/dis.rst | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -62,7 +62,7 @@ the disassembled code object. If *current_offset* is not None, it refers to an instruction offset - in the disassembled code. Setting this means :meth:`dis` will display + in the disassembled code. Setting this means :meth:`.dis` will display a "current instruction" marker against the specified opcode. .. classmethod:: from_traceback(tb) @@ -81,8 +81,8 @@ .. method:: dis() - Return a formatted view of the bytecode operations (the same as - printed by :func:`dis`, but returned as a multi-line string). + Return a formatted view of the bytecode operations (the same as printed by + :func:`dis.dis`, but returned as a multi-line string). .. method:: info() -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 15:37:15 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 14:37:15 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjM1NjEp?= Message-ID: <20150302143713.10084.81709@psf.io> https://hg.python.org/cpython/rev/760f222103c7 changeset: 94823:760f222103c7 parent: 94819:84ac7e437cdc parent: 94821:1a6f5c18e317 user: Benjamin Peterson date: Mon Mar 02 09:36:48 2015 -0500 summary: merge 3.4 (#23561) files: Doc/library/dis.rst | 200 ++++++++++++++++---------------- 1 files changed, 100 insertions(+), 100 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -9,9 +9,9 @@ -------------- The :mod:`dis` module supports the analysis of CPython :term:`bytecode` by -disassembling it. The CPython bytecode which this module takes as an -input is defined in the file :file:`Include/opcode.h` and used by the compiler -and the interpreter. +disassembling it. The CPython bytecode which this module takes as an input is +defined in the file :file:`Include/opcode.h` and used by the compiler and the +interpreter. .. impl-detail:: @@ -43,33 +43,32 @@ .. versionadded:: 3.4 The bytecode analysis API allows pieces of Python code to be wrapped in a -:class:`Bytecode` object that provides easy access to details of the -compiled code. +:class:`Bytecode` object that provides easy access to details of the compiled +code. .. class:: Bytecode(x, *, first_line=None, current_offset=None) - Analyse the bytecode corresponding to a function, generator, method, - string of source code, or a code object (as returned by :func:`compile`). - This is a convenience wrapper around many of the functions listed below, - most notably :func:`get_instructions`, as iterating over a - :class:`Bytecode` instance yields the bytecode operations as - :class:`Instruction` instances. + Analyse the bytecode corresponding to a function, generator, method, string + of source code, or a code object (as returned by :func:`compile`). - If *first_line* is not None, it indicates the line number that should - be reported for the first source line in the disassembled code. - Otherwise, the source line information (if any) is taken directly from - the disassembled code object. + This is a convenience wrapper around many of the functions listed below, most + notably :func:`get_instructions`, as iterating over a :class:`Bytecode` + instance yields the bytecode operations as :class:`Instruction` instances. - If *current_offset* is not None, it refers to an instruction offset - in the disassembled code. Setting this means :meth:`dis` will display - a "current instruction" marker against the specified opcode. + If *first_line* is not None, it indicates the line number that should be + reported for the first source line in the disassembled code. Otherwise, the + source line information (if any) is taken directly from the disassembled code + object. + + If *current_offset* is not None, it refers to an instruction offset in the + disassembled code. Setting this means :meth:`.dis` will display a "current + instruction" marker against the specified opcode. .. classmethod:: from_traceback(tb) - Construct a :class:`Bytecode` instance from the given traceback, - setting *current_offset* to the instruction responsible for the - exception. + Construct a :class:`Bytecode` instance from the given traceback, setting + *current_offset* to the instruction responsible for the exception. .. data:: codeobj @@ -81,8 +80,8 @@ .. method:: dis() - Return a formatted view of the bytecode operations (the same as - printed by :func:`dis`, but returned as a multi-line string). + Return a formatted view of the bytecode operations (the same as printed by + :func:`dis.dis`, but returned as a multi-line string). .. method:: info() @@ -104,10 +103,9 @@ Analysis functions ------------------ -The :mod:`dis` module also defines the following analysis functions that -convert the input directly to the desired output. They can be useful if -only a single operation is being performed, so the intermediate analysis -object isn't useful: +The :mod:`dis` module also defines the following analysis functions that convert +the input directly to the desired output. They can be useful if only a single +operation is being performed, so the intermediate analysis object isn't useful: .. function:: code_info(x) @@ -196,13 +194,13 @@ Return an iterator over the instructions in the supplied function, method, source code string or code object. - The iterator generates a series of :class:`Instruction` named tuples - giving the details of each operation in the supplied code. + The iterator generates a series of :class:`Instruction` named tuples giving + the details of each operation in the supplied code. - If *first_line* is not None, it indicates the line number that should - be reported for the first source line in the disassembled code. - Otherwise, the source line information (if any) is taken directly from - the disassembled code object. + If *first_line* is not None, it indicates the line number that should be + reported for the first source line in the disassembled code. Otherwise, the + source line information (if any) is taken directly from the disassembled code + object. .. versionadded:: 3.4 @@ -511,8 +509,8 @@ .. opcode:: PRINT_EXPR Implements the expression statement for the interactive mode. TOS is removed - from the stack and printed. In non-interactive mode, an expression statement is - terminated with :opcode:`POP_TOP`. + from the stack and printed. In non-interactive mode, an expression statement + is terminated with :opcode:`POP_TOP`. .. opcode:: BREAK_LOOP @@ -542,9 +540,9 @@ comprehensions. For all of the :opcode:`SET_ADD`, :opcode:`LIST_APPEND` and :opcode:`MAP_ADD` -instructions, while the -added value or key/value pair is popped off, the container object remains on -the stack so that it is available for further iterations of the loop. +instructions, while the added value or key/value pair is popped off, the +container object remains on the stack so that it is available for further +iterations of the loop. .. opcode:: RETURN_VALUE @@ -566,23 +564,23 @@ .. opcode:: IMPORT_STAR - Loads all symbols not starting with ``'_'`` directly from the module TOS to the - local namespace. The module is popped after loading all names. This opcode - implements ``from module import *``. + Loads all symbols not starting with ``'_'`` directly from the module TOS to + the local namespace. The module is popped after loading all names. This + opcode implements ``from module import *``. .. opcode:: POP_BLOCK - Removes one block from the block stack. Per frame, there is a stack of blocks, - denoting nested loops, try statements, and such. + Removes one block from the block stack. Per frame, there is a stack of + blocks, denoting nested loops, try statements, and such. .. opcode:: POP_EXCEPT Removes one block from the block stack. The popped block must be an exception - handler block, as implicitly created when entering an except handler. - In addition to popping extraneous values from the frame stack, the - last three popped values are used to restore the exception state. + handler block, as implicitly created when entering an except handler. In + addition to popping extraneous values from the frame stack, the last three + popped values are used to restore the exception state. .. opcode:: END_FINALLY @@ -612,9 +610,9 @@ .. opcode:: WITH_CLEANUP - Cleans up the stack when a :keyword:`with` statement block exits. TOS is - the context manager's :meth:`__exit__` bound method. Below TOS are 1--3 - values indicating how/why the finally clause was entered: + Cleans up the stack when a :keyword:`with` statement block exits. TOS is the + context manager's :meth:`__exit__` bound method. Below TOS are 1--3 values + indicating how/why the finally clause was entered: * SECOND = ``None`` * (SECOND, THIRD) = (``WHY_{RETURN,CONTINUE}``), retval @@ -624,10 +622,10 @@ In the last case, ``TOS(SECOND, THIRD, FOURTH)`` is called, otherwise ``TOS(None, None, None)``. In addition, TOS is removed from the stack. - If the stack represents an exception, *and* the function call returns - a 'true' value, this information is "zapped" and replaced with a single - ``WHY_SILENCED`` to prevent :opcode:`END_FINALLY` from re-raising the exception. - (But non-local gotos will still be resumed.) + If the stack represents an exception, *and* the function call returns a + 'true' value, this information is "zapped" and replaced with a single + ``WHY_SILENCED`` to prevent :opcode:`END_FINALLY` from re-raising the + exception. (But non-local gotos will still be resumed.) .. XXX explain the WHY stuff! @@ -638,8 +636,8 @@ .. opcode:: STORE_NAME (namei) Implements ``name = TOS``. *namei* is the index of *name* in the attribute - :attr:`co_names` of the code object. The compiler tries to use :opcode:`STORE_FAST` - or :opcode:`STORE_GLOBAL` if possible. + :attr:`co_names` of the code object. The compiler tries to use + :opcode:`STORE_FAST` or :opcode:`STORE_GLOBAL` if possible. .. opcode:: DELETE_NAME (namei) @@ -699,8 +697,8 @@ .. opcode:: BUILD_TUPLE (count) - Creates a tuple consuming *count* items from the stack, and pushes the resulting - tuple onto the stack. + Creates a tuple consuming *count* items from the stack, and pushes the + resulting tuple onto the stack. .. opcode:: BUILD_LIST (count) @@ -734,8 +732,8 @@ Imports the module ``co_names[namei]``. TOS and TOS1 are popped and provide the *fromlist* and *level* arguments of :func:`__import__`. The module - object is pushed onto the stack. The current namespace is not affected: - for a proper import statement, a subsequent :opcode:`STORE_FAST` instruction + object is pushed onto the stack. The current namespace is not affected: for + a proper import statement, a subsequent :opcode:`STORE_FAST` instruction modifies the namespace. @@ -763,14 +761,14 @@ .. opcode:: JUMP_IF_TRUE_OR_POP (target) - If TOS is true, sets the bytecode counter to *target* and leaves TOS - on the stack. Otherwise (TOS is false), TOS is popped. + If TOS is true, sets the bytecode counter to *target* and leaves TOS on the + stack. Otherwise (TOS is false), TOS is popped. .. opcode:: JUMP_IF_FALSE_OR_POP (target) - If TOS is false, sets the bytecode counter to *target* and leaves - TOS on the stack. Otherwise (TOS is true), TOS is popped. + If TOS is false, sets the bytecode counter to *target* and leaves TOS on the + stack. Otherwise (TOS is true), TOS is popped. .. opcode:: JUMP_ABSOLUTE (target) @@ -780,10 +778,10 @@ .. opcode:: FOR_ITER (delta) - TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. - If this yields a new value, push it on the stack (leaving the iterator below - it). If the iterator indicates it is exhausted TOS is popped, and the - byte code counter is incremented by *delta*. + TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. If + this yields a new value, push it on the stack (leaving the iterator below + it). If the iterator indicates it is exhausted TOS is popped, and the byte + code counter is incremented by *delta*. .. opcode:: LOAD_GLOBAL (namei) @@ -799,19 +797,19 @@ .. opcode:: SETUP_EXCEPT (delta) - Pushes a try block from a try-except clause onto the block stack. *delta* points - to the first except block. + Pushes a try block from a try-except clause onto the block stack. *delta* + points to the first except block. .. opcode:: SETUP_FINALLY (delta) - Pushes a try block from a try-except clause onto the block stack. *delta* points - to the finally block. + Pushes a try block from a try-except clause onto the block stack. *delta* + points to the finally block. .. opcode:: STORE_MAP - Store a key and value pair in a dictionary. Pops the key and value while leaving - the dictionary on the stack. + Store a key and value pair in a dictionary. Pops the key and value while + leaving the dictionary on the stack. .. opcode:: LOAD_FAST (var_num) @@ -831,8 +829,8 @@ .. opcode:: LOAD_CLOSURE (i) Pushes a reference to the cell contained in slot *i* of the cell and free - variable storage. The name of the variable is ``co_cellvars[i]`` if *i* is - less than the length of *co_cellvars*. Otherwise it is ``co_freevars[i - + variable storage. The name of the variable is ``co_cellvars[i]`` if *i* is + less than the length of *co_cellvars*. Otherwise it is ``co_freevars[i - len(co_cellvars)]``. @@ -872,11 +870,12 @@ Calls a function. The low byte of *argc* indicates the number of positional parameters, the high byte the number of keyword parameters. On the stack, the - opcode finds the keyword parameters first. For each keyword argument, the value - is on top of the key. Below the keyword parameters, the positional parameters - are on the stack, with the right-most parameter on top. Below the parameters, - the function object to call is on the stack. Pops all function arguments, and - the function itself off the stack, and pushes the return value. + opcode finds the keyword parameters first. For each keyword argument, the + value is on top of the key. Below the keyword parameters, the positional + parameters are on the stack, with the right-most parameter on top. Below the + parameters, the function object to call is on the stack. Pops all function + arguments, and the function itself off the stack, and pushes the return + value. .. opcode:: MAKE_FUNCTION (argc) @@ -899,8 +898,8 @@ Creates a new function object, sets its *__closure__* slot, and pushes it on the stack. TOS is the :term:`qualified name` of the function, TOS1 is the code associated with the function, and TOS2 is the tuple containing cells for - the closure's free variables. The function also has *argc* default parameters, - which are found below the cells. + the closure's free variables. The function also has *argc* default + parameters, which are found below the cells. .. opcode:: BUILD_SLICE (argc) @@ -916,36 +915,37 @@ Prefixes any opcode which has an argument too big to fit into the default two bytes. *ext* holds two additional bytes which, taken together with the - subsequent opcode's argument, comprise a four-byte argument, *ext* being the two - most-significant bytes. + subsequent opcode's argument, comprise a four-byte argument, *ext* being the + two most-significant bytes. .. opcode:: CALL_FUNCTION_VAR (argc) - Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The top element - on the stack contains the variable argument list, followed by keyword and - positional arguments. + Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The + top element on the stack contains the variable argument list, followed by + keyword and positional arguments. .. opcode:: CALL_FUNCTION_KW (argc) - Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The top element - on the stack contains the keyword arguments dictionary, followed by explicit - keyword and positional arguments. + Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The + top element on the stack contains the keyword arguments dictionary, followed + by explicit keyword and positional arguments. .. opcode:: CALL_FUNCTION_VAR_KW (argc) - Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The top - element on the stack contains the keyword arguments dictionary, followed by the - variable-arguments tuple, followed by explicit keyword and positional arguments. + Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The + top element on the stack contains the keyword arguments dictionary, followed + by the variable-arguments tuple, followed by explicit keyword and positional + arguments. .. opcode:: HAVE_ARGUMENT - This is not really an opcode. It identifies the dividing line between opcodes - which don't take arguments ``< HAVE_ARGUMENT`` and those which do ``>= - HAVE_ARGUMENT``. + This is not really an opcode. It identifies the dividing line between + opcodes which don't take arguments ``< HAVE_ARGUMENT`` and those which do + ``>= HAVE_ARGUMENT``. .. _opcode_collections: @@ -977,10 +977,10 @@ .. data:: hasfree - Sequence of bytecodes that access a free variable (note that 'free' in - this context refers to names in the current scope that are referenced by - inner scopes or names in outer scopes that are referenced from this scope. - It does *not* include references to global or builtin scopes). + Sequence of bytecodes that access a free variable (note that 'free' in this + context refers to names in the current scope that are referenced by inner + scopes or names in outer scopes that are referenced from this scope. It does + *not* include references to global or builtin scopes). .. data:: hasname -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 15:37:15 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 14:37:15 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_wrap_everythin?= =?utf-8?q?g_at_80_chars?= Message-ID: <20150302143707.16406.92204@psf.io> https://hg.python.org/cpython/rev/1a6f5c18e317 changeset: 94821:1a6f5c18e317 branch: 3.4 user: Benjamin Peterson date: Mon Mar 02 09:31:40 2015 -0500 summary: wrap everything at 80 chars files: Doc/library/dis.rst | 195 +++++++++++++++---------------- 1 files changed, 97 insertions(+), 98 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -9,9 +9,9 @@ -------------- The :mod:`dis` module supports the analysis of CPython :term:`bytecode` by -disassembling it. The CPython bytecode which this module takes as an -input is defined in the file :file:`Include/opcode.h` and used by the compiler -and the interpreter. +disassembling it. The CPython bytecode which this module takes as an input is +defined in the file :file:`Include/opcode.h` and used by the compiler and the +interpreter. .. impl-detail:: @@ -43,33 +43,31 @@ .. versionadded:: 3.4 The bytecode analysis API allows pieces of Python code to be wrapped in a -:class:`Bytecode` object that provides easy access to details of the -compiled code. +:class:`Bytecode` object that provides easy access to details of the compiled +code. .. class:: Bytecode(x, *, first_line=None, current_offset=None) - Analyse the bytecode corresponding to a function, method, string of - source code, or a code object (as returned by :func:`compile`). + Analyse the bytecode corresponding to a function, method, string of source + code, or a code object (as returned by :func:`compile`). - This is a convenience wrapper around many of the functions listed below, - most notably :func:`get_instructions`, as iterating over a - :class:`Bytecode` instance yields the bytecode operations as - :class:`Instruction` instances. + This is a convenience wrapper around many of the functions listed below, most + notably :func:`get_instructions`, as iterating over a :class:`Bytecode` + instance yields the bytecode operations as :class:`Instruction` instances. - If *first_line* is not None, it indicates the line number that should - be reported for the first source line in the disassembled code. - Otherwise, the source line information (if any) is taken directly from - the disassembled code object. + If *first_line* is not None, it indicates the line number that should be + reported for the first source line in the disassembled code. Otherwise, the + source line information (if any) is taken directly from the disassembled code + object. - If *current_offset* is not None, it refers to an instruction offset - in the disassembled code. Setting this means :meth:`.dis` will display - a "current instruction" marker against the specified opcode. + If *current_offset* is not None, it refers to an instruction offset in the + disassembled code. Setting this means :meth:`.dis` will display a "current + instruction" marker against the specified opcode. .. classmethod:: from_traceback(tb) - Construct a :class:`Bytecode` instance from the given traceback, - setting *current_offset* to the instruction responsible for the - exception. + Construct a :class:`Bytecode` instance from the given traceback, setting + *current_offset* to the instruction responsible for the exception. .. data:: codeobj @@ -104,10 +102,9 @@ Analysis functions ------------------ -The :mod:`dis` module also defines the following analysis functions that -convert the input directly to the desired output. They can be useful if -only a single operation is being performed, so the intermediate analysis -object isn't useful: +The :mod:`dis` module also defines the following analysis functions that convert +the input directly to the desired output. They can be useful if only a single +operation is being performed, so the intermediate analysis object isn't useful: .. function:: code_info(x) @@ -196,13 +193,13 @@ Return an iterator over the instructions in the supplied function, method, source code string or code object. - The iterator generates a series of :class:`Instruction` named tuples - giving the details of each operation in the supplied code. + The iterator generates a series of :class:`Instruction` named tuples giving + the details of each operation in the supplied code. - If *first_line* is not None, it indicates the line number that should - be reported for the first source line in the disassembled code. - Otherwise, the source line information (if any) is taken directly from - the disassembled code object. + If *first_line* is not None, it indicates the line number that should be + reported for the first source line in the disassembled code. Otherwise, the + source line information (if any) is taken directly from the disassembled code + object. .. versionadded:: 3.4 @@ -501,8 +498,8 @@ .. opcode:: PRINT_EXPR Implements the expression statement for the interactive mode. TOS is removed - from the stack and printed. In non-interactive mode, an expression statement is - terminated with :opcode:`POP_TOP`. + from the stack and printed. In non-interactive mode, an expression statement + is terminated with :opcode:`POP_TOP`. .. opcode:: BREAK_LOOP @@ -532,9 +529,9 @@ comprehensions. For all of the :opcode:`SET_ADD`, :opcode:`LIST_APPEND` and :opcode:`MAP_ADD` -instructions, while the -added value or key/value pair is popped off, the container object remains on -the stack so that it is available for further iterations of the loop. +instructions, while the added value or key/value pair is popped off, the +container object remains on the stack so that it is available for further +iterations of the loop. .. opcode:: RETURN_VALUE @@ -556,23 +553,23 @@ .. opcode:: IMPORT_STAR - Loads all symbols not starting with ``'_'`` directly from the module TOS to the - local namespace. The module is popped after loading all names. This opcode - implements ``from module import *``. + Loads all symbols not starting with ``'_'`` directly from the module TOS to + the local namespace. The module is popped after loading all names. This + opcode implements ``from module import *``. .. opcode:: POP_BLOCK - Removes one block from the block stack. Per frame, there is a stack of blocks, - denoting nested loops, try statements, and such. + Removes one block from the block stack. Per frame, there is a stack of + blocks, denoting nested loops, try statements, and such. .. opcode:: POP_EXCEPT Removes one block from the block stack. The popped block must be an exception - handler block, as implicitly created when entering an except handler. - In addition to popping extraneous values from the frame stack, the - last three popped values are used to restore the exception state. + handler block, as implicitly created when entering an except handler. In + addition to popping extraneous values from the frame stack, the last three + popped values are used to restore the exception state. .. opcode:: END_FINALLY @@ -602,9 +599,9 @@ .. opcode:: WITH_CLEANUP - Cleans up the stack when a :keyword:`with` statement block exits. TOS is - the context manager's :meth:`__exit__` bound method. Below TOS are 1--3 - values indicating how/why the finally clause was entered: + Cleans up the stack when a :keyword:`with` statement block exits. TOS is the + context manager's :meth:`__exit__` bound method. Below TOS are 1--3 values + indicating how/why the finally clause was entered: * SECOND = ``None`` * (SECOND, THIRD) = (``WHY_{RETURN,CONTINUE}``), retval @@ -614,10 +611,10 @@ In the last case, ``TOS(SECOND, THIRD, FOURTH)`` is called, otherwise ``TOS(None, None, None)``. In addition, TOS is removed from the stack. - If the stack represents an exception, *and* the function call returns - a 'true' value, this information is "zapped" and replaced with a single - ``WHY_SILENCED`` to prevent :opcode:`END_FINALLY` from re-raising the exception. - (But non-local gotos will still be resumed.) + If the stack represents an exception, *and* the function call returns a + 'true' value, this information is "zapped" and replaced with a single + ``WHY_SILENCED`` to prevent :opcode:`END_FINALLY` from re-raising the + exception. (But non-local gotos will still be resumed.) .. XXX explain the WHY stuff! @@ -628,8 +625,8 @@ .. opcode:: STORE_NAME (namei) Implements ``name = TOS``. *namei* is the index of *name* in the attribute - :attr:`co_names` of the code object. The compiler tries to use :opcode:`STORE_FAST` - or :opcode:`STORE_GLOBAL` if possible. + :attr:`co_names` of the code object. The compiler tries to use + :opcode:`STORE_FAST` or :opcode:`STORE_GLOBAL` if possible. .. opcode:: DELETE_NAME (namei) @@ -689,8 +686,8 @@ .. opcode:: BUILD_TUPLE (count) - Creates a tuple consuming *count* items from the stack, and pushes the resulting - tuple onto the stack. + Creates a tuple consuming *count* items from the stack, and pushes the + resulting tuple onto the stack. .. opcode:: BUILD_LIST (count) @@ -724,8 +721,8 @@ Imports the module ``co_names[namei]``. TOS and TOS1 are popped and provide the *fromlist* and *level* arguments of :func:`__import__`. The module - object is pushed onto the stack. The current namespace is not affected: - for a proper import statement, a subsequent :opcode:`STORE_FAST` instruction + object is pushed onto the stack. The current namespace is not affected: for + a proper import statement, a subsequent :opcode:`STORE_FAST` instruction modifies the namespace. @@ -753,14 +750,14 @@ .. opcode:: JUMP_IF_TRUE_OR_POP (target) - If TOS is true, sets the bytecode counter to *target* and leaves TOS - on the stack. Otherwise (TOS is false), TOS is popped. + If TOS is true, sets the bytecode counter to *target* and leaves TOS on the + stack. Otherwise (TOS is false), TOS is popped. .. opcode:: JUMP_IF_FALSE_OR_POP (target) - If TOS is false, sets the bytecode counter to *target* and leaves - TOS on the stack. Otherwise (TOS is true), TOS is popped. + If TOS is false, sets the bytecode counter to *target* and leaves TOS on the + stack. Otherwise (TOS is true), TOS is popped. .. opcode:: JUMP_ABSOLUTE (target) @@ -770,10 +767,10 @@ .. opcode:: FOR_ITER (delta) - TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. - If this yields a new value, push it on the stack (leaving the iterator below - it). If the iterator indicates it is exhausted TOS is popped, and the - byte code counter is incremented by *delta*. + TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. If + this yields a new value, push it on the stack (leaving the iterator below + it). If the iterator indicates it is exhausted TOS is popped, and the byte + code counter is incremented by *delta*. .. opcode:: LOAD_GLOBAL (namei) @@ -789,19 +786,19 @@ .. opcode:: SETUP_EXCEPT (delta) - Pushes a try block from a try-except clause onto the block stack. *delta* points - to the first except block. + Pushes a try block from a try-except clause onto the block stack. *delta* + points to the first except block. .. opcode:: SETUP_FINALLY (delta) - Pushes a try block from a try-except clause onto the block stack. *delta* points - to the finally block. + Pushes a try block from a try-except clause onto the block stack. *delta* + points to the finally block. .. opcode:: STORE_MAP - Store a key and value pair in a dictionary. Pops the key and value while leaving - the dictionary on the stack. + Store a key and value pair in a dictionary. Pops the key and value while + leaving the dictionary on the stack. .. opcode:: LOAD_FAST (var_num) @@ -821,8 +818,8 @@ .. opcode:: LOAD_CLOSURE (i) Pushes a reference to the cell contained in slot *i* of the cell and free - variable storage. The name of the variable is ``co_cellvars[i]`` if *i* is - less than the length of *co_cellvars*. Otherwise it is ``co_freevars[i - + variable storage. The name of the variable is ``co_cellvars[i]`` if *i* is + less than the length of *co_cellvars*. Otherwise it is ``co_freevars[i - len(co_cellvars)]``. @@ -862,11 +859,12 @@ Calls a function. The low byte of *argc* indicates the number of positional parameters, the high byte the number of keyword parameters. On the stack, the - opcode finds the keyword parameters first. For each keyword argument, the value - is on top of the key. Below the keyword parameters, the positional parameters - are on the stack, with the right-most parameter on top. Below the parameters, - the function object to call is on the stack. Pops all function arguments, and - the function itself off the stack, and pushes the return value. + opcode finds the keyword parameters first. For each keyword argument, the + value is on top of the key. Below the keyword parameters, the positional + parameters are on the stack, with the right-most parameter on top. Below the + parameters, the function object to call is on the stack. Pops all function + arguments, and the function itself off the stack, and pushes the return + value. .. opcode:: MAKE_FUNCTION (argc) @@ -889,8 +887,8 @@ Creates a new function object, sets its *__closure__* slot, and pushes it on the stack. TOS is the :term:`qualified name` of the function, TOS1 is the code associated with the function, and TOS2 is the tuple containing cells for - the closure's free variables. The function also has *argc* default parameters, - which are found below the cells. + the closure's free variables. The function also has *argc* default + parameters, which are found below the cells. .. opcode:: BUILD_SLICE (argc) @@ -906,36 +904,37 @@ Prefixes any opcode which has an argument too big to fit into the default two bytes. *ext* holds two additional bytes which, taken together with the - subsequent opcode's argument, comprise a four-byte argument, *ext* being the two - most-significant bytes. + subsequent opcode's argument, comprise a four-byte argument, *ext* being the + two most-significant bytes. .. opcode:: CALL_FUNCTION_VAR (argc) - Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The top element - on the stack contains the variable argument list, followed by keyword and - positional arguments. + Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The + top element on the stack contains the variable argument list, followed by + keyword and positional arguments. .. opcode:: CALL_FUNCTION_KW (argc) - Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The top element - on the stack contains the keyword arguments dictionary, followed by explicit - keyword and positional arguments. + Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The + top element on the stack contains the keyword arguments dictionary, followed + by explicit keyword and positional arguments. .. opcode:: CALL_FUNCTION_VAR_KW (argc) - Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The top - element on the stack contains the keyword arguments dictionary, followed by the - variable-arguments tuple, followed by explicit keyword and positional arguments. + Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The + top element on the stack contains the keyword arguments dictionary, followed + by the variable-arguments tuple, followed by explicit keyword and positional + arguments. .. opcode:: HAVE_ARGUMENT - This is not really an opcode. It identifies the dividing line between opcodes - which don't take arguments ``< HAVE_ARGUMENT`` and those which do ``>= - HAVE_ARGUMENT``. + This is not really an opcode. It identifies the dividing line between + opcodes which don't take arguments ``< HAVE_ARGUMENT`` and those which do + ``>= HAVE_ARGUMENT``. .. _opcode_collections: @@ -967,10 +966,10 @@ .. data:: hasfree - Sequence of bytecodes that access a free variable (note that 'free' in - this context refers to names in the current scope that are referenced by - inner scopes or names in outer scopes that are referenced from this scope. - It does *not* include references to global or builtin scopes). + Sequence of bytecodes that access a free variable (note that 'free' in this + context refers to names in the current scope that are referenced by inner + scopes or names in outer scopes that are referenced from this scope. It does + *not* include references to global or builtin scopes). .. data:: hasname -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 15:37:45 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 14:37:45 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_wrap_everythin?= =?utf-8?q?g_at_80_chars?= Message-ID: <20150302143712.39517.5405@psf.io> https://hg.python.org/cpython/rev/f57af1b337ca changeset: 94822:f57af1b337ca branch: 2.7 parent: 94817:5d3a5557f2d5 user: Benjamin Peterson date: Mon Mar 02 09:34:31 2015 -0500 summary: wrap everything at 80 chars files: Doc/library/dis.rst | 154 ++++++++++++++++--------------- 1 files changed, 78 insertions(+), 76 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -9,9 +9,9 @@ -------------- The :mod:`dis` module supports the analysis of CPython :term:`bytecode` by -disassembling it. The CPython bytecode which this module takes as an -input is defined in the file :file:`Include/opcode.h` and used by the compiler -and the interpreter. +disassembling it. The CPython bytecode which this module takes as an input is +defined in the file :file:`Include/opcode.h` and used by the compiler and the +interpreter. .. impl-detail:: @@ -40,17 +40,18 @@ .. function:: dis([bytesource]) - Disassemble the *bytesource* object. *bytesource* can denote either a module, a - class, a method, a function, or a code object. For a module, it disassembles - all functions. For a class, it disassembles all methods. For a single code - sequence, it prints one line per bytecode instruction. If no object is - provided, it disassembles the last traceback. + Disassemble the *bytesource* object. *bytesource* can denote either a module, + a class, a method, a function, or a code object. For a module, it + disassembles all functions. For a class, it disassembles all methods. For a + single code sequence, it prints one line per bytecode instruction. If no + object is provided, it disassembles the last traceback. .. function:: distb([tb]) - Disassembles the top-of-stack function of a traceback, using the last traceback - if none was passed. The instruction causing the exception is indicated. + Disassembles the top-of-stack function of a traceback, using the last + traceback if none was passed. The instruction causing the exception is + indicated. .. function:: disassemble(code[, lasti]) @@ -233,8 +234,8 @@ .. opcode:: BINARY_DIVIDE () - Implements ``TOS = TOS1 / TOS`` when ``from __future__ import division`` is not - in effect. + Implements ``TOS = TOS1 / TOS`` when ``from __future__ import division`` is + not in effect. .. opcode:: BINARY_FLOOR_DIVIDE () @@ -244,8 +245,8 @@ .. opcode:: BINARY_TRUE_DIVIDE () - Implements ``TOS = TOS1 / TOS`` when ``from __future__ import division`` is in - effect. + Implements ``TOS = TOS1 / TOS`` when ``from __future__ import division`` is + in effect. .. opcode:: BINARY_MODULO () @@ -445,32 +446,32 @@ .. opcode:: PRINT_EXPR () Implements the expression statement for the interactive mode. TOS is removed - from the stack and printed. In non-interactive mode, an expression statement is - terminated with :opcode:`POP_TOP`. + from the stack and printed. In non-interactive mode, an expression statement + is terminated with :opcode:`POP_TOP`. .. opcode:: PRINT_ITEM () - Prints TOS to the file-like object bound to ``sys.stdout``. There is one such - instruction for each item in the :keyword:`print` statement. + Prints TOS to the file-like object bound to ``sys.stdout``. There is one + such instruction for each item in the :keyword:`print` statement. .. opcode:: PRINT_ITEM_TO () - Like ``PRINT_ITEM``, but prints the item second from TOS to the file-like object - at TOS. This is used by the extended print statement. + Like ``PRINT_ITEM``, but prints the item second from TOS to the file-like + object at TOS. This is used by the extended print statement. .. opcode:: PRINT_NEWLINE () - Prints a new line on ``sys.stdout``. This is generated as the last operation of - a :keyword:`print` statement, unless the statement ends with a comma. + Prints a new line on ``sys.stdout``. This is generated as the last operation + of a :keyword:`print` statement, unless the statement ends with a comma. .. opcode:: PRINT_NEWLINE_TO () - Like ``PRINT_NEWLINE``, but prints the new line on the file-like object on the - TOS. This is used by the extended print statement. + Like ``PRINT_NEWLINE``, but prints the new line on the file-like object on + the TOS. This is used by the extended print statement. .. opcode:: BREAK_LOOP () @@ -487,15 +488,15 @@ .. opcode:: LIST_APPEND (i) Calls ``list.append(TOS[-i], TOS)``. Used to implement list comprehensions. - While the appended value is popped off, the list object remains on the - stack so that it is available for further iterations of the loop. + While the appended value is popped off, the list object remains on the stack + so that it is available for further iterations of the loop. .. opcode:: LOAD_LOCALS () - Pushes a reference to the locals of the current scope on the stack. This is used - in the code for a class definition: After the class body is evaluated, the - locals are passed to the class definition. + Pushes a reference to the locals of the current scope on the stack. This is + used in the code for a class definition: After the class body is evaluated, + the locals are passed to the class definition. .. opcode:: RETURN_VALUE () @@ -510,9 +511,9 @@ .. opcode:: IMPORT_STAR () - Loads all symbols not starting with ``'_'`` directly from the module TOS to the - local namespace. The module is popped after loading all names. This opcode - implements ``from module import *``. + Loads all symbols not starting with ``'_'`` directly from the module TOS to + the local namespace. The module is popped after loading all names. This + opcode implements ``from module import *``. .. opcode:: EXEC_STMT () @@ -523,8 +524,8 @@ .. opcode:: POP_BLOCK () - Removes one block from the block stack. Per frame, there is a stack of blocks, - denoting nested loops, try statements, and such. + Removes one block from the block stack. Per frame, there is a stack of + blocks, denoting nested loops, try statements, and such. .. opcode:: END_FINALLY () @@ -600,8 +601,8 @@ .. opcode:: DUP_TOPX (count) - Duplicate *count* items, keeping them in the same order. Due to implementation - limits, *count* should be between 1 and 5 inclusive. + Duplicate *count* items, keeping them in the same order. Due to + implementation limits, *count* should be between 1 and 5 inclusive. .. opcode:: STORE_ATTR (namei) @@ -637,8 +638,8 @@ .. opcode:: BUILD_TUPLE (count) - Creates a tuple consuming *count* items from the stack, and pushes the resulting - tuple onto the stack. + Creates a tuple consuming *count* items from the stack, and pushes the + resulting tuple onto the stack. .. opcode:: BUILD_LIST (count) @@ -667,9 +668,9 @@ Imports the module ``co_names[namei]``. TOS and TOS1 are popped and provide the *fromlist* and *level* arguments of :func:`__import__`. The module - object is pushed onto the stack. The current namespace is not affected: - for a proper import statement, a subsequent ``STORE_FAST`` instruction - modifies the namespace. + object is pushed onto the stack. The current namespace is not affected: for + a proper import statement, a subsequent ``STORE_FAST`` instruction modifies + the namespace. .. opcode:: IMPORT_FROM (namei) @@ -696,14 +697,14 @@ .. opcode:: JUMP_IF_TRUE_OR_POP (target) - If TOS is true, sets the bytecode counter to *target* and leaves TOS - on the stack. Otherwise (TOS is false), TOS is popped. + If TOS is true, sets the bytecode counter to *target* and leaves TOS on the + stack. Otherwise (TOS is false), TOS is popped. .. opcode:: JUMP_IF_FALSE_OR_POP (target) - If TOS is false, sets the bytecode counter to *target* and leaves - TOS on the stack. Otherwise (TOS is true), TOS is popped. + If TOS is false, sets the bytecode counter to *target* and leaves TOS on the + stack. Otherwise (TOS is true), TOS is popped. .. opcode:: JUMP_ABSOLUTE (target) @@ -732,19 +733,19 @@ .. opcode:: SETUP_EXCEPT (delta) - Pushes a try block from a try-except clause onto the block stack. *delta* points - to the first except block. + Pushes a try block from a try-except clause onto the block stack. *delta* + points to the first except block. .. opcode:: SETUP_FINALLY (delta) - Pushes a try block from a try-except clause onto the block stack. *delta* points - to the finally block. + Pushes a try block from a try-except clause onto the block stack. *delta* + points to the finally block. .. opcode:: STORE_MAP () - Store a key and value pair in a dictionary. Pops the key and value while leaving - the dictionary on the stack. + Store a key and value pair in a dictionary. Pops the key and value while + leaving the dictionary on the stack. .. opcode:: LOAD_FAST (var_num) @@ -764,8 +765,8 @@ .. opcode:: LOAD_CLOSURE (i) Pushes a reference to the cell contained in slot *i* of the cell and free - variable storage. The name of the variable is ``co_cellvars[i]`` if *i* is - less than the length of *co_cellvars*. Otherwise it is ``co_freevars[i - + variable storage. The name of the variable is ``co_cellvars[i]`` if *i* is + less than the length of *co_cellvars*. Otherwise it is ``co_freevars[i - len(co_cellvars)]``. @@ -797,18 +798,19 @@ Calls a function. The low byte of *argc* indicates the number of positional parameters, the high byte the number of keyword parameters. On the stack, the - opcode finds the keyword parameters first. For each keyword argument, the value - is on top of the key. Below the keyword parameters, the positional parameters - are on the stack, with the right-most parameter on top. Below the parameters, - the function object to call is on the stack. Pops all function arguments, and - the function itself off the stack, and pushes the return value. + opcode finds the keyword parameters first. For each keyword argument, the + value is on top of the key. Below the keyword parameters, the positional + parameters are on the stack, with the right-most parameter on top. Below the + parameters, the function object to call is on the stack. Pops all function + arguments, and the function itself off the stack, and pushes the return + value. .. opcode:: MAKE_FUNCTION (argc) - Pushes a new function object on the stack. TOS is the code associated with the - function. The function object is defined to have *argc* default parameters, - which are found below TOS. + Pushes a new function object on the stack. TOS is the code associated with + the function. The function object is defined to have *argc* default + parameters, which are found below TOS. .. opcode:: MAKE_CLOSURE (argc) @@ -832,34 +834,34 @@ Prefixes any opcode which has an argument too big to fit into the default two bytes. *ext* holds two additional bytes which, taken together with the - subsequent opcode's argument, comprise a four-byte argument, *ext* being the two - most-significant bytes. + subsequent opcode's argument, comprise a four-byte argument, *ext* being the + two most-significant bytes. .. opcode:: CALL_FUNCTION_VAR (argc) - Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The top element - on the stack contains the variable argument list, followed by keyword and - positional arguments. + Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The + top element on the stack contains the variable argument list, followed by + keyword and positional arguments. .. opcode:: CALL_FUNCTION_KW (argc) - Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The top element - on the stack contains the keyword arguments dictionary, followed by explicit - keyword and positional arguments. + Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The + top element on the stack contains the keyword arguments dictionary, followed + by explicit keyword and positional arguments. .. opcode:: CALL_FUNCTION_VAR_KW (argc) - Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The top - element on the stack contains the keyword arguments dictionary, followed by the - variable-arguments tuple, followed by explicit keyword and positional arguments. + Calls a function. *argc* is interpreted as in :opcode:`CALL_FUNCTION`. The + top element on the stack contains the keyword arguments dictionary, followed + by the variable-arguments tuple, followed by explicit keyword and positional + arguments. .. opcode:: HAVE_ARGUMENT () - This is not really an opcode. It identifies the dividing line between opcodes - which don't take arguments ``< HAVE_ARGUMENT`` and those which do ``>= - HAVE_ARGUMENT``. - + This is not really an opcode. It identifies the dividing line between + opcodes which don't take arguments ``< HAVE_ARGUMENT`` and those which do + ``>= HAVE_ARGUMENT``. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 17:01:44 2015 From: python-checkins at python.org (steve.dower) Date: Mon, 02 Mar 2015 16:01:44 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323451=3A_Update_p?= =?utf-8?q?yconfig=2Eh_for_Windows_to_require_Vista_headers_and_remove?= Message-ID: <20150302160133.77550.10226@psf.io> https://hg.python.org/cpython/rev/57e2549cc9a6 changeset: 94824:57e2549cc9a6 user: Steve Dower date: Mon Mar 02 08:01:10 2015 -0800 summary: Issue #23451: Update pyconfig.h for Windows to require Vista headers and remove unnecessary version checks. files: Modules/_winapi.c | 8 ++++ Modules/socketmodule.h | 7 ++++ Objects/unicodeobject.c | 19 +---------- PC/pyconfig.h | 6 +- Python/pytime.c | 49 +--------------------------- Python/sysmodule.c | 8 ++++ 6 files changed, 29 insertions(+), 68 deletions(-) diff --git a/Modules/_winapi.c b/Modules/_winapi.c --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -1001,6 +1001,12 @@ \n\ Return the version number of the current operating system."); +/* Disable deprecation warnings about GetVersionEx as the result is + being passed straight through to the caller, who is responsible for + using it correctly. */ +#pragma warning(push) +#pragma warning(disable:4996) + static PyObject * winapi_GetVersion(PyObject* self, PyObject* args) { @@ -1010,6 +1016,8 @@ return PyLong_FromUnsignedLong(GetVersion()); } +#pragma warning(pop) + static PyObject * winapi_OpenProcess(PyObject *self, PyObject *args) { diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -14,6 +14,13 @@ #else /* MS_WINDOWS */ # include +/* Windows 'supports' CMSG_LEN, but does not follow the POSIX standard + * interface at all, so there is no point including the code that + * attempts to use it. + */ +# ifdef PySocket_BUILDING_SOCKET +# undef CMSG_LEN +# endif # include /* VC6 is shipped with old platform headers, and does not have MSTcpIP.h * Separate SDKs have all the functions we want, but older ones don't have diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -519,10 +519,6 @@ return _PyUnicode_Copy(unicode); } -#ifdef HAVE_MBCS -static OSVERSIONINFOEX winver; -#endif - /* --- Bloom Filters ----------------------------------------------------- */ /* stuff to implement simple "bloom filters" for Unicode characters. @@ -7112,13 +7108,7 @@ encode_code_page_flags(UINT code_page, const char *errors) { if (code_page == CP_UTF8) { - if (winver.dwMajorVersion >= 6) - /* CP_UTF8 supports WC_ERR_INVALID_CHARS on Windows Vista - and later */ - return WC_ERR_INVALID_CHARS; - else - /* CP_UTF8 only supports flags=0 on Windows older than Vista */ - return 0; + return WC_ERR_INVALID_CHARS; } else if (code_page == CP_UTF7) { /* CP_UTF7 only supports flags=0 */ @@ -14976,13 +14966,6 @@ if (PyType_Ready(&PyFormatterIter_Type) < 0) Py_FatalError("Can't initialize formatter iter type"); -#ifdef HAVE_MBCS - winver.dwOSVersionInfoSize = sizeof(winver); - if (!GetVersionEx((OSVERSIONINFO*)&winver)) { - PyErr_SetFromWindowsErr(0); - return -1; - } -#endif return 0; } diff --git a/PC/pyconfig.h b/PC/pyconfig.h --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -156,9 +156,9 @@ #endif /* MS_WIN64 */ /* set the version macros for the windows headers */ -/* Python 3.4+ requires Windows XP or greater */ -#define Py_WINVER 0x0501 /* _WIN32_WINNT_WINXP */ -#define Py_NTDDI NTDDI_WINXP +/* Python 3.5+ requires Windows Vista or greater */ +#define Py_WINVER 0x0600 /* _WIN32_WINNT_VISTA */ +#define Py_NTDDI NTDDI_VISTA /* We only set these values when building Python - we don't want to force these values on extensions, as that will affect the prototypes and diff --git a/Python/pytime.c b/Python/pytime.c --- a/Python/pytime.c +++ b/Python/pytime.c @@ -7,10 +7,6 @@ #include /* mach_absolute_time(), mach_timebase_info() */ #endif -#ifdef MS_WINDOWS -static OSVERSIONINFOEX winver; -#endif - static int pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) { @@ -124,41 +120,11 @@ static _PyTime_timeval last = {0, -1}; #endif #if defined(MS_WINDOWS) - static ULONGLONG (*GetTickCount64) (void) = NULL; - static ULONGLONG (CALLBACK *Py_GetTickCount64)(void); - static int has_gettickcount64 = -1; ULONGLONG result; assert(info == NULL || raise); - if (has_gettickcount64 == -1) { - /* GetTickCount64() was added to Windows Vista */ - has_gettickcount64 = (winver.dwMajorVersion >= 6); - if (has_gettickcount64) { - HINSTANCE hKernel32; - hKernel32 = GetModuleHandleW(L"KERNEL32"); - *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32, - "GetTickCount64"); - assert(Py_GetTickCount64 != NULL); - } - } - - if (has_gettickcount64) { - result = Py_GetTickCount64(); - } - else { - static DWORD last_ticks = 0; - static DWORD n_overflow = 0; - DWORD ticks; - - ticks = GetTickCount(); - if (ticks < last_ticks) - n_overflow++; - last_ticks = ticks; - - result = (ULONGLONG)n_overflow << 32; - result += ticks; - } + result = GetTickCount64(); tp->tv_sec = result / 1000; tp->tv_usec = (result % 1000) * 1000; @@ -166,10 +132,7 @@ if (info) { DWORD timeAdjustment, timeIncrement; BOOL isTimeAdjustmentDisabled, ok; - if (has_gettickcount64) - info->implementation = "GetTickCount64()"; - else - info->implementation = "GetTickCount()"; + info->implementation = "GetTickCount64()"; info->monotonic = 1; ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled); @@ -409,14 +372,6 @@ { _PyTime_timeval tv; -#ifdef MS_WINDOWS - winver.dwOSVersionInfoSize = sizeof(winver); - if (!GetVersionEx((OSVERSIONINFO*)&winver)) { - PyErr_SetFromWindowsErr(0); - return -1; - } -#endif - /* ensure that the system clock works */ if (_PyTime_gettimeofday_info(&tv, NULL) < 0) return -1; diff --git a/Python/sysmodule.c b/Python/sysmodule.c --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -772,6 +772,12 @@ via indexing, the rest are name only */ }; +/* Disable deprecation warnings about GetVersionEx as the result is + being passed straight through to the caller, who is responsible for + using it correctly. */ +#pragma warning(push) +#pragma warning(disable:4996) + static PyObject * sys_getwindowsversion(PyObject *self) { @@ -803,6 +809,8 @@ return version; } +#pragma warning(pop) + #endif /* MS_WINDOWS */ #ifdef HAVE_DLOPEN -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 17:06:45 2015 From: python-checkins at python.org (steve.dower) Date: Mon, 02 Mar 2015 16:06:45 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzE4Mzgy?= =?utf-8?q?=3A_Zero-length_messages_are_consumed_by_ReadFile_on_Windows_8_?= =?utf-8?q?and?= Message-ID: <20150302160645.16426.58747@psf.io> https://hg.python.org/cpython/rev/7401a28d3d41 changeset: 94825:7401a28d3d41 branch: 3.4 parent: 94821:1a6f5c18e317 user: Steve Dower date: Mon Mar 02 08:05:27 2015 -0800 summary: Issue #18382: Zero-length messages are consumed by ReadFile on Windows 8 and later files: Lib/multiprocessing/connection.py | 13 +++++++++++-- 1 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -844,7 +844,7 @@ try: ov, err = _winapi.ReadFile(fileno(), 0, True) except OSError as e: - err = e.winerror + ov, err = None, e.winerror if err not in _ready_errors: raise if err == _winapi.ERROR_IO_PENDING: @@ -853,7 +853,16 @@ else: # If o.fileno() is an overlapped pipe handle and # err == 0 then there is a zero length message - # in the pipe, but it HAS NOT been consumed. + # in the pipe, but it HAS NOT been consumed... + if ov and sys.getwindowsversion()[:2] >= (6, 2): + # ... except on Windows 8 and later, where + # the message HAS been consumed. + try: + _, err = ov.GetOverlappedResult(False) + except OSError as e: + err = e.winerror + if not err and hasattr(o, '_got_empty_message'): + o._got_empty_message = True ready_objects.add(o) timeout = 0 -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 17:06:45 2015 From: python-checkins at python.org (steve.dower) Date: Mon, 02 Mar 2015 16:06:45 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2318382=3A_Zero-length_messages_are_consumed_by_R?= =?utf-8?q?eadFile_on_Windows_8_and?= Message-ID: <20150302160645.16418.65931@psf.io> https://hg.python.org/cpython/rev/6ccbcf1df7bd changeset: 94826:6ccbcf1df7bd parent: 94824:57e2549cc9a6 parent: 94825:7401a28d3d41 user: Steve Dower date: Mon Mar 02 08:06:30 2015 -0800 summary: Issue #18382: Zero-length messages are consumed by ReadFile on Windows 8 and later files: Lib/multiprocessing/connection.py | 13 +++++++++++-- 1 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -829,7 +829,7 @@ try: ov, err = _winapi.ReadFile(fileno(), 0, True) except OSError as e: - err = e.winerror + ov, err = None, e.winerror if err not in _ready_errors: raise if err == _winapi.ERROR_IO_PENDING: @@ -838,7 +838,16 @@ else: # If o.fileno() is an overlapped pipe handle and # err == 0 then there is a zero length message - # in the pipe, but it HAS NOT been consumed. + # in the pipe, but it HAS NOT been consumed... + if ov and sys.getwindowsversion()[:2] >= (6, 2): + # ... except on Windows 8 and later, where + # the message HAS been consumed. + try: + _, err = ov.GetOverlappedResult(False) + except OSError as e: + err = e.winerror + if not err and hasattr(o, '_got_empty_message'): + o._got_empty_message = True ready_objects.add(o) timeout = 0 -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 17:21:35 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 16:21:35 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy4zIC0+IDMuNCk6?= =?utf-8?q?_merge_3=2E3_=28=2323367=29?= Message-ID: <20150302162134.39447.10152@psf.io> https://hg.python.org/cpython/rev/90f960e79c9e changeset: 94828:90f960e79c9e branch: 3.4 parent: 94825:7401a28d3d41 parent: 94827:84025a32fa2b user: Benjamin Peterson date: Mon Mar 02 11:18:40 2015 -0500 summary: merge 3.3 (#23367) files: Misc/NEWS | 2 ++ Modules/unicodedata.c | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -53,6 +53,8 @@ - Issue #23421: Fixed compression in tarfile CLI. Patch by wdv4758h. +- Issue #23367: Fix possible overflows in the unicodedata module. + - Issue #23361: Fix possible overflow in Windows subprocess creation code. Build diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -553,10 +553,17 @@ stackptr = 0; isize = PyUnicode_GET_LENGTH(input); + space = isize; /* Overallocate at most 10 characters. */ - space = (isize > 10 ? 10 : isize) + isize; + if (space > 10) { + if (space <= PY_SSIZE_T_MAX - 10) + space += 10; + } + else { + space *= 2; + } osize = space; - output = PyMem_New(Py_UCS4, space); + output = PyMem_NEW(Py_UCS4, space); if (!output) { PyErr_NoMemory(); return NULL; @@ -703,7 +710,7 @@ /* We allocate a buffer for the output. If we find that we made no changes, we still return the NFD result. */ - output = PyMem_New(Py_UCS4, len); + output = PyMem_NEW(Py_UCS4, len); if (!output) { PyErr_NoMemory(); Py_DECREF(result); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 17:21:35 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 16:21:35 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E3=29=3A_fix_possible_o?= =?utf-8?q?verflow_bugs_in_unicodedata_=28closes_=2323367=29?= Message-ID: <20150302162134.77554.16343@psf.io> https://hg.python.org/cpython/rev/84025a32fa2b changeset: 94827:84025a32fa2b branch: 3.3 parent: 94678:221301c8095f user: Benjamin Peterson date: Mon Mar 02 11:17:05 2015 -0500 summary: fix possible overflow bugs in unicodedata (closes #23367) files: Misc/NEWS | 2 ++ Modules/unicodedata.c | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,8 @@ Library ------- +- Issue #23367: Fix possible overflows in the unicodedata module. + - Issue #23361: Fix possible overflow in Windows subprocess creation code. - Issue #23363: Fix possible overflow in itertools.permutations. diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -507,10 +507,17 @@ stackptr = 0; isize = PyUnicode_GET_LENGTH(input); + space = isize; /* Overallocate at most 10 characters. */ - space = (isize > 10 ? 10 : isize) + isize; + if (space > 10) { + if (space <= PY_SSIZE_T_MAX - 10) + space += 10; + } + else { + space *= 2; + } osize = space; - output = PyMem_Malloc(space * sizeof(Py_UCS4)); + output = PyMem_NEW(Py_UCS4, space); if (!output) { PyErr_NoMemory(); return NULL; @@ -657,7 +664,7 @@ /* We allocate a buffer for the output. If we find that we made no changes, we still return the NFD result. */ - output = PyMem_Malloc(len * sizeof(Py_UCS4)); + output = PyMem_NEW(Py_UCS4, len); if (!output) { PyErr_NoMemory(); Py_DECREF(result); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 17:21:42 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 16:21:42 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_fix_possible_o?= =?utf-8?q?verflow_bugs_in_unicodedata_=28closes_=2323367=29?= Message-ID: <20150302162135.15322.42605@psf.io> https://hg.python.org/cpython/rev/3019effc44f2 changeset: 94830:3019effc44f2 branch: 2.7 parent: 94822:f57af1b337ca user: Benjamin Peterson date: Mon Mar 02 11:17:05 2015 -0500 summary: fix possible overflow bugs in unicodedata (closes #23367) files: Misc/NEWS | 2 ++ Modules/unicodedata.c | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -50,6 +50,8 @@ posixpath.expandvars(). Fixed all os.path implementations on unicode-disabled builds. +- Issue #23367: Fix possible overflows in the unicodedata module. + - Issue #23363: Fix possible overflow in itertools.permutations. - Issue #23364: Fix possible overflow in itertools.product. diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -506,8 +506,15 @@ stackptr = 0; isize = PyUnicode_GET_SIZE(input); + space = isize; /* Overallocate at most 10 characters. */ - space = (isize > 10 ? 10 : isize) + isize; + if (space > 10) { + if (space <= PY_SSIZE_T_MAX - 10) + space += 10; + } + else { + space *= 2; + } result = PyUnicode_FromUnicode(NULL, space); if (!result) return NULL; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 17:21:42 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 16:21:42 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjMzNjcp?= Message-ID: <20150302162134.16400.81984@psf.io> https://hg.python.org/cpython/rev/93244000efea changeset: 94829:93244000efea parent: 94826:6ccbcf1df7bd parent: 94828:90f960e79c9e user: Benjamin Peterson date: Mon Mar 02 11:18:56 2015 -0500 summary: merge 3.4 (#23367) files: Misc/NEWS | 2 ++ Modules/unicodedata.c | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -78,6 +78,8 @@ - Issue #23421: Fixed compression in tarfile CLI. Patch by wdv4758h. +- Issue #23367: Fix possible overflows in the unicodedata module. + - Issue #23361: Fix possible overflow in Windows subprocess creation code. - logging.handlers.QueueListener now takes a respect_handler_level keyword diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -553,10 +553,17 @@ stackptr = 0; isize = PyUnicode_GET_LENGTH(input); + space = isize; /* Overallocate at most 10 characters. */ - space = (isize > 10 ? 10 : isize) + isize; + if (space > 10) { + if (space <= PY_SSIZE_T_MAX - 10) + space += 10; + } + else { + space *= 2; + } osize = space; - output = PyMem_New(Py_UCS4, space); + output = PyMem_NEW(Py_UCS4, space); if (!output) { PyErr_NoMemory(); return NULL; @@ -703,7 +710,7 @@ /* We allocate a buffer for the output. If we find that we made no changes, we still return the NFD result. */ - output = PyMem_New(Py_UCS4, len); + output = PyMem_NEW(Py_UCS4, len); if (!output) { PyErr_NoMemory(); Py_DECREF(result); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 17:45:05 2015 From: python-checkins at python.org (larry.hastings) Date: Mon, 02 Mar 2015 16:45:05 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321293=3A_Remove_u?= =?utf-8?q?nnecessary_=22capsule_hack=22=2E?= Message-ID: <20150302164504.16424.12093@psf.io> https://hg.python.org/cpython/rev/b22755f8ab5f changeset: 94831:b22755f8ab5f parent: 94829:93244000efea user: Larry Hastings date: Mon Mar 02 08:44:51 2015 -0800 summary: Issue #21293: Remove unnecessary "capsule hack". files: Objects/object.c | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Objects/object.c b/Objects/object.c --- a/Objects/object.c +++ b/Objects/object.c @@ -1825,9 +1825,6 @@ #endif -/* Hack to force loading of pycapsule.o */ -PyTypeObject *_PyCapsule_hack = &PyCapsule_Type; - /* Hack to force loading of abstract.o */ Py_ssize_t (*_Py_abstract_hack)(PyObject *) = PyObject_Size; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 19:24:38 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 18:24:38 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40IChudWxsKQ==?= Message-ID: <20150302182427.16410.29865@psf.io> https://hg.python.org/cpython/rev/88cc0edc2713 changeset: 94834:88cc0edc2713 parent: 94829:93244000efea parent: 94833:880906bbf792 user: Benjamin Peterson date: Mon Mar 02 13:24:12 2015 -0500 summary: merge 3.4 (null) files: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 19:24:38 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 18:24:38 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAobWVyZ2UgMy4zIC0+IDMuNCk6?= =?utf-8?q?_merge_3=2E3_=28=2323362=29?= Message-ID: <20150302182427.19164.74715@psf.io> https://hg.python.org/cpython/rev/880906bbf792 changeset: 94833:880906bbf792 branch: 3.4 parent: 94828:90f960e79c9e parent: 94832:21cd7f83e0aa user: Benjamin Peterson date: Mon Mar 02 13:23:41 2015 -0500 summary: merge 3.3 (#23362) files: Objects/unicodeobject.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -8665,7 +8665,7 @@ /* allocate enough for a simple 1:1 translation without replacements, if we need more, we'll resize */ osize = size; - output = PyMem_Malloc(osize * sizeof(Py_UCS4)); + output = PyMem_NEW(Py_UCS4, osize); opos = 0; if (output == NULL) { PyErr_NoMemory(); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 19:24:38 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 18:24:38 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_merge_heads?= Message-ID: <20150302182427.16430.40738@psf.io> https://hg.python.org/cpython/rev/17f6dd585ebf changeset: 94835:17f6dd585ebf parent: 94834:88cc0edc2713 parent: 94831:b22755f8ab5f user: Benjamin Peterson date: Mon Mar 02 13:24:21 2015 -0500 summary: merge heads files: Objects/object.c | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Objects/object.c b/Objects/object.c --- a/Objects/object.c +++ b/Objects/object.c @@ -1825,9 +1825,6 @@ #endif -/* Hack to force loading of pycapsule.o */ -PyTypeObject *_PyCapsule_hack = &PyCapsule_Type; - /* Hack to force loading of abstract.o */ Py_ssize_t (*_Py_abstract_hack)(PyObject *) = PyObject_Size; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 19:24:38 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 02 Mar 2015 18:24:38 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy4zKTogdXNlIFB5TWVtX05F?= =?utf-8?q?W_to_detect_overflow_=28closes_=2323362=29?= Message-ID: <20150302182427.20315.24507@psf.io> https://hg.python.org/cpython/rev/21cd7f83e0aa changeset: 94832:21cd7f83e0aa branch: 3.3 parent: 94827:84025a32fa2b user: Benjamin Peterson date: Mon Mar 02 13:23:25 2015 -0500 summary: use PyMem_NEW to detect overflow (closes #23362) files: Objects/unicodeobject.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -8501,7 +8501,7 @@ /* allocate enough for a simple 1:1 translation without replacements, if we need more, we'll resize */ osize = size; - output = PyMem_Malloc(osize * sizeof(Py_UCS4)); + output = PyMem_NEW(Py_UCS4, osize); opos = 0; if (output == NULL) { PyErr_NoMemory(); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 20:47:22 2015 From: python-checkins at python.org (larry.hastings) Date: Mon, 02 Mar 2015 19:47:22 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_Make_the_=22null-merge_to_def?= =?utf-8?q?ault=22_step_its_own_explicit_step=2E?= Message-ID: <20150302194704.15314.69863@psf.io> https://hg.python.org/peps/rev/0cb9c8d26c7c changeset: 5718:0cb9c8d26c7c user: Larry Hastings date: Mon Mar 02 11:47:01 2015 -0800 summary: Make the "null-merge to default" step its own explicit step. I have now forgotten to do it twice, so hopefully this will draw more attention to it and I won't miss it in the future. files: pep-0101.txt | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pep-0101.txt b/pep-0101.txt --- a/pep-0101.txt +++ b/pep-0101.txt @@ -572,7 +572,7 @@ (usually only one, except if you made a new maintenance release). Easily resolvable conflicts may appear in Misc/NEWS. - If releasing from other than the default branch, remember to carefully + ___ If releasing from other than the default branch, remember to carefully merge any touched branches with higher level branches, up to default. For example: @@ -584,7 +584,7 @@ $ hg resolve --mark - Commit and push to the main repo. + ___ Commit and push to the main repo. ___ You can delete the remote release clone, or simply reuse it for the next release. -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Mon Mar 2 21:30:55 2015 From: python-checkins at python.org (ethan.furman) Date: Mon, 02 Mar 2015 20:30:55 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_issue19075=3A_add_visual_s?= =?utf-8?q?orting_algorithms_to_turtledemo=3B_original_code_from?= Message-ID: <20150302203030.16406.72331@psf.io> https://hg.python.org/cpython/rev/596228491890 changeset: 94836:596228491890 user: Ethan Furman date: Mon Mar 02 12:29:58 2015 -0800 summary: issue19075: add visual sorting algorithms to turtledemo; original code from Jason Yeo files: Doc/library/turtle.rst | 3 + Lib/turtledemo/sorting_animate.py | 204 ++++++++++++++++++ Misc/NEWS | 3 + 3 files changed, 210 insertions(+), 0 deletions(-) diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -2351,6 +2351,9 @@ | | pairwise in opposite | shapesize, tilt, | | | direction | get_shapepoly, update | +----------------+------------------------------+-----------------------+ +| sorting_animate| visual demonstration of | simple alignment, | +| | different sorting methods | randomization | ++----------------+------------------------------+-----------------------+ | tree | a (graphical) breadth | :func:`clone` | | | first tree (using generators)| | +----------------+------------------------------+-----------------------+ diff --git a/Lib/turtledemo/sorting_animate.py b/Lib/turtledemo/sorting_animate.py new file mode 100644 --- /dev/null +++ b/Lib/turtledemo/sorting_animate.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +""" + + sorting_animation.py + +A minimal sorting algorithm animation: +Sorts a shelf of 10 blocks using insertion +sort, selection sort and quicksort. + +Shelfs are implemented using builtin lists. + +Blocks are turtles with shape "square", but +stretched to rectangles by shapesize() + --------------------------------------- + To exit press space button + --------------------------------------- +""" +from turtle import * +import random + + +class Block(Turtle): + + def __init__(self, size): + self.size = size + Turtle.__init__(self, shape="square", visible=False) + self.pu() + self.shapesize(size * 1.5, 1.5, 2) # square-->rectangle + self.fillcolor("black") + self.st() + + def glow(self): + self.fillcolor("red") + + def unglow(self): + self.fillcolor("black") + + def __repr__(self): + return "Block size: {0}".format(self.size) + + +class Shelf(list): + + def __init__(self, y): + "create a shelf. y is y-position of first block" + self.y = y + self.x = -150 + + def push(self, d): + width, _, _ = d.shapesize() + # align blocks by the bottom edge + y_offset = width / 2 * 20 + d.sety(self.y + y_offset) + d.setx(self.x + 34 * len(self)) + self.append(d) + + def _close_gap_from_i(self, i): + for b in self[i:]: + xpos, _ = b.pos() + b.setx(xpos - 34) + + def _open_gap_from_i(self, i): + for b in self[i:]: + xpos, _ = b.pos() + b.setx(xpos + 34) + + def pop(self, key): + b = list.pop(self, key) + b.glow() + b.sety(200) + self._close_gap_from_i(key) + return b + + def insert(self, key, b): + self._open_gap_from_i(key) + list.insert(self, key, b) + b.setx(self.x + 34 * key) + width, _, _ = b.shapesize() + # align blocks by the bottom edge + y_offset = width / 2 * 20 + b.sety(self.y + y_offset) + b.unglow() + +def isort(shelf): + length = len(shelf) + for i in range(1, length): + hole = i + while hole > 0 and shelf[i].size < shelf[hole - 1].size: + hole = hole - 1 + shelf.insert(hole, shelf.pop(i)) + return + +def ssort(shelf): + length = len(shelf) + for j in range(0, length - 1): + imin = j + for i in range(j + 1, length): + if shelf[i].size < shelf[imin].size: + imin = i + if imin != j: + shelf.insert(j, shelf.pop(imin)) + +def partition(shelf, left, right, pivot_index): + pivot = shelf[pivot_index] + shelf.insert(right, shelf.pop(pivot_index)) + store_index = left + for i in range(left, right): # range is non-inclusive of ending value + if shelf[i].size < pivot.size: + shelf.insert(store_index, shelf.pop(i)) + store_index = store_index + 1 + shelf.insert(store_index, shelf.pop(right)) # move pivot to correct position + return store_index + +def qsort(shelf, left, right): + if left < right: + pivot_index = left + pivot_new_index = partition(shelf, left, right, pivot_index) + qsort(shelf, left, pivot_new_index - 1) + qsort(shelf, pivot_new_index + 1, right) + +def randomize(): + disable_keys() + clear() + target = list(range(10)) + random.shuffle(target) + for i, t in enumerate(target): + for j in range(i, len(s)): + if s[j].size == t + 1: + s.insert(i, s.pop(j)) + show_text(instructions1) + show_text(instructions2, line=1) + enable_keys() + +def show_text(text, line=0): + line = 20 * line + goto(0,-250 - line) + write(text, align="center", font=("Courier", 16, "bold")) + +def start_ssort(): + disable_keys() + clear() + show_text("Selection Sort") + ssort(s) + clear() + show_text(instructions1) + show_text(instructions2, line=1) + enable_keys() + +def start_isort(): + disable_keys() + clear() + show_text("Insertion Sort") + isort(s) + clear() + show_text(instructions1) + show_text(instructions2, line=1) + enable_keys() + +def start_qsort(): + disable_keys() + clear() + show_text("Quicksort") + qsort(s, 0, len(s) - 1) + clear() + show_text(instructions1) + show_text(instructions2, line=1) + enable_keys() + +def init_shelf(): + global s + s = Shelf(-200) + vals = (4, 2, 8, 9, 1, 5, 10, 3, 7, 6) + for i in vals: + s.push(Block(i)) + +def disable_keys(): + onkey(None, "s") + onkey(None, "i") + onkey(None, "q") + onkey(None, "r") + +def enable_keys(): + onkey(start_isort, "i") + onkey(start_ssort, "s") + onkey(start_qsort, "q") + onkey(randomize, "r") + onkey(bye, "space") + +def main(): + getscreen().clearscreen() + ht(); penup() + init_shelf() + show_text(instructions1) + show_text(instructions2, line=1) + enable_keys() + listen() + return "EVENTLOOP" + +instructions1 = "press i for insertion sort, s for selection sort, q for quicksort" +instructions2 = "spacebar to quit, r to randomize" + +if __name__=="__main__": + msg = main() + mainloop() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -86,6 +86,9 @@ argument which, if set to True, will pass messages to handlers taking handler levels into account. +- Issue #19705: turtledemo now has a visual sorting algorithm demo. Original + patch from Jason Yeo. + Build ----- -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 2 21:46:53 2015 From: python-checkins at python.org (ethan.furman) Date: Mon, 02 Mar 2015 20:46:53 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_missing_space_after_comma_mes?= =?utf-8?q?sing_up_formatting?= Message-ID: <20150302204652.19144.52555@psf.io> https://hg.python.org/peps/rev/a83464f73c7b changeset: 5719:a83464f73c7b user: Ethan Furman date: Mon Mar 02 12:46:30 2015 -0800 summary: missing space after comma messing up formatting files: pep-0440.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0440.txt b/pep-0440.txt --- a/pep-0440.txt +++ b/pep-0440.txt @@ -459,7 +459,7 @@ Post release separators ~~~~~~~~~~~~~~~~~~~~~~~ -Post releases allow a ``.``,``-``, or ``_`` separator as well as omitting the +Post releases allow a ``.``, ``-``, or ``_`` separator as well as omitting the separator all together. The normal form of this is with the ``.`` separator. This allows versions such as ``1.2-post2`` or ``1.2post2`` which normalize to ``1.2.post2``. Like the pre-release seperator this also allows an optional -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Tue Mar 3 06:45:11 2015 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 03 Mar 2015 05:45:11 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Beautify_and_better_docume?= =?utf-8?q?nt_the_use_of_the_size=5Ft_cast_for_bounds_checking=2E?= Message-ID: <20150303054511.26792.1505@psf.io> https://hg.python.org/cpython/rev/8c1d4ab76db0 changeset: 94837:8c1d4ab76db0 user: Raymond Hettinger date: Mon Mar 02 21:45:02 2015 -0800 summary: Beautify and better document the use of the size_t cast for bounds checking. files: Modules/_collectionsmodule.c | 18 ++++++++++++------ 1 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -765,6 +765,14 @@ Py_SIZE(deque) == 0); } +static int +valid_index(Py_ssize_t i, Py_ssize_t limit) +{ + /* The cast to size_t let us use just a single comparison + to check whether i is in the range: 0 <= i < limit */ + return (size_t) i < (size_t) limit; +} + static PyObject * deque_item(dequeobject *deque, Py_ssize_t i) { @@ -772,9 +780,8 @@ PyObject *item; Py_ssize_t n, index=i; - if ((size_t)i >= (size_t)Py_SIZE(deque)) { - PyErr_SetString(PyExc_IndexError, - "deque index out of range"); + if (!valid_index(i, Py_SIZE(deque))) { + PyErr_SetString(PyExc_IndexError, "deque index out of range"); return NULL; } @@ -836,9 +843,8 @@ block *b; Py_ssize_t n, len=Py_SIZE(deque), halflen=(len+1)>>1, index=i; - if ((size_t)i >= (size_t)len) { - PyErr_SetString(PyExc_IndexError, - "deque index out of range"); + if (!valid_index(i, len)) { + PyErr_SetString(PyExc_IndexError, "deque index out of range"); return -1; } if (v == NULL) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 3 07:23:44 2015 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 03 Mar 2015 06:23:44 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Minor_code_beautification?= =?utf-8?q?=2E__Replace_macro_with_in-lineable_functions=2E?= Message-ID: <20150303062344.12007.26922@psf.io> https://hg.python.org/cpython/rev/513c8b7c129d changeset: 94838:513c8b7c129d user: Raymond Hettinger date: Mon Mar 02 22:23:37 2015 -0800 summary: Minor code beautification. Replace macro with in-lineable functions. files: Modules/_collectionsmodule.c | 55 +++++++++++++++-------- 1 files changed, 35 insertions(+), 20 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -130,22 +130,6 @@ PyObject *weakreflist; /* List of weak references */ } dequeobject; -/* The deque's size limit is d.maxlen. The limit can be zero or positive. - * If there is no limit, then d.maxlen == -1. - * - * After an item is added to a deque, we check to see if the size has grown past - * the limit. If it has, we get the size back down to the limit by popping an - * item off of the opposite end. The methods that can trigger this are append(), - * appendleft(), extend(), and extendleft(). - */ - -#define TRIM(d, popfunction) \ - if (d->maxlen != -1 && Py_SIZE(d) > d->maxlen) { \ - PyObject *rv = popfunction(d, NULL); \ - assert(rv != NULL && Py_SIZE(d) <= d->maxlen); \ - Py_DECREF(rv); \ - } - static PyTypeObject deque_type; /* XXX Todo: @@ -261,6 +245,37 @@ PyDoc_STRVAR(popleft_doc, "Remove and return the leftmost element."); +/* The deque's size limit is d.maxlen. The limit can be zero or positive. + * If there is no limit, then d.maxlen == -1. + * + * After an item is added to a deque, we check to see if the size has grown past + * the limit. If it has, we get the size back down to the limit by popping an + * item off of the opposite end. The methods that can trigger this are append(), + * appendleft(), extend(), and extendleft(). + */ + +static void +deque_trim_right(dequeobject *deque) +{ + if (deque->maxlen != -1 && Py_SIZE(deque) > deque->maxlen) { + PyObject *rv = deque_pop(deque, NULL); + assert(rv != NULL); + assert(Py_SIZE(deque) <= deque->maxlen); + Py_DECREF(rv); + } +} + +static void +deque_trim_left(dequeobject *deque) +{ + if (deque->maxlen != -1 && Py_SIZE(deque) > deque->maxlen) { + PyObject *rv = deque_popleft(deque, NULL); + assert(rv != NULL); + assert(Py_SIZE(deque) <= deque->maxlen); + Py_DECREF(rv); + } +} + static PyObject * deque_append(dequeobject *deque, PyObject *item) { @@ -280,7 +295,7 @@ Py_SIZE(deque)++; deque->rightindex++; deque->rightblock->data[deque->rightindex] = item; - TRIM(deque, deque_popleft); + deque_trim_left(deque); Py_RETURN_NONE; } @@ -305,7 +320,7 @@ Py_SIZE(deque)++; deque->leftindex--; deque->leftblock->data[deque->leftindex] = item; - TRIM(deque, deque_pop); + deque_trim_right(deque); Py_RETURN_NONE; } @@ -378,7 +393,7 @@ Py_SIZE(deque)++; deque->rightindex++; deque->rightblock->data[deque->rightindex] = item; - TRIM(deque, deque_popleft); + deque_trim_left(deque); } Py_DECREF(it); if (PyErr_Occurred()) @@ -439,7 +454,7 @@ Py_SIZE(deque)++; deque->leftindex--; deque->leftblock->data[deque->leftindex] = item; - TRIM(deque, deque_pop); + deque_trim_right(deque); } Py_DECREF(it); if (PyErr_Occurred()) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 3 07:31:54 2015 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 03 Mar 2015 06:31:54 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Minor_beautification=2E__M?= =?utf-8?q?ove_struct_definitions_to_the_top=2E__Fix-up_a_comment=2E?= Message-ID: <20150303063141.26052.47322@psf.io> https://hg.python.org/cpython/rev/e972cc540f33 changeset: 94839:e972cc540f33 user: Raymond Hettinger date: Mon Mar 02 22:31:35 2015 -0800 summary: Minor beautification. Move struct definitions to the top. Fix-up a comment. files: Modules/_collectionsmodule.c | 30 ++++++++++++------------ 1 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -15,9 +15,9 @@ /* The block length may be set to any number over 1. Larger numbers * reduce the number of calls to the memory allocator, give faster - * indexing and rotation, and reduce the link::data overhead ratio. + * indexing and rotation, and reduce the link to data overhead ratio. * Making the block length a power of two speeds-up the modulo - * calculation in deque_item(). + * and division calculations in deque_item() and deque_ass_item(). */ #define BLOCKLEN 64 @@ -56,6 +56,19 @@ struct BLOCK *rightlink; } block; +typedef struct { + PyObject_VAR_HEAD + block *leftblock; + block *rightblock; + Py_ssize_t leftindex; /* in range(BLOCKLEN) */ + Py_ssize_t rightindex; /* in range(BLOCKLEN) */ + long state; /* incremented whenever the indices move */ + Py_ssize_t maxlen; + PyObject *weakreflist; /* List of weak references */ +} dequeobject; + +static PyTypeObject deque_type; + /* For debug builds, add error checking to track the endpoints * in the chain of links. The goal is to make sure that link * assignments only take place at endpoints so that links already @@ -119,19 +132,6 @@ } } -typedef struct { - PyObject_VAR_HEAD - block *leftblock; - block *rightblock; - Py_ssize_t leftindex; /* in range(BLOCKLEN) */ - Py_ssize_t rightindex; /* in range(BLOCKLEN) */ - long state; /* incremented whenever the indices move */ - Py_ssize_t maxlen; - PyObject *weakreflist; /* List of weak references */ -} dequeobject; - -static PyTypeObject deque_type; - /* XXX Todo: If aligned memory allocations become available, make the deque object 64 byte aligned so that all of the fields -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 3 07:47:51 2015 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 03 Mar 2015 06:47:51 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Switch_the_state_variable_?= =?utf-8?q?to_unsigned_for_defined_wrap-around_behavior=2E?= Message-ID: <20150303064751.26792.67440@psf.io> https://hg.python.org/cpython/rev/a4f9c98f08e2 changeset: 94840:a4f9c98f08e2 user: Raymond Hettinger date: Mon Mar 02 22:47:46 2015 -0800 summary: Switch the state variable to unsigned for defined wrap-around behavior. files: Modules/_collectionsmodule.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -62,7 +62,7 @@ block *rightblock; Py_ssize_t leftindex; /* in range(BLOCKLEN) */ Py_ssize_t rightindex; /* in range(BLOCKLEN) */ - long state; /* incremented whenever the indices move */ + size_t state; /* incremented whenever the indices move */ Py_ssize_t maxlen; PyObject *weakreflist; /* List of weak references */ } dequeobject; @@ -692,8 +692,8 @@ Py_ssize_t n = Py_SIZE(deque); Py_ssize_t i; Py_ssize_t count = 0; + size_t start_state = deque->state; PyObject *item; - long start_state = deque->state; int cmp; for (i=0 ; i results for 596228491890 on branch "default" -------------------------------------------- test_asyncio leaked [0, 0, 3] memory blocks, sum=3 test_collections leaked [-2, 0, 0] references, sum=-2 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_poll leaked [0, 22, -22] references, sum=0 test_poll leaked [0, 6, -6] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogOJmZ8t', '-x'] From solipsis at pitrou.net Tue Mar 3 07:50:49 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 03 Mar 2015 07:50:49 +0100 Subject: [Python-checkins] Daily reference leaks (7839681ca931): sum=-9 Message-ID: results for 7839681ca931 on branch "default" -------------------------------------------- test_collections leaked [-2, -4, 0] references, sum=-6 test_collections leaked [-1, -2, 0] memory blocks, sum=-3 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogyXhT6r', '-x'] From solipsis at pitrou.net Tue Mar 3 07:50:51 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 03 Mar 2015 07:50:51 +0100 Subject: [Python-checkins] Daily reference leaks (4a1fe339dcf6): sum=6 Message-ID: results for 4a1fe339dcf6 on branch "default" -------------------------------------------- test_asyncio leaked [0, 3, 0] memory blocks, sum=3 test_collections leaked [0, -2, 4] references, sum=2 test_collections leaked [0, -1, 2] memory blocks, sum=1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogBfiZru', '-x'] From python-checkins at python.org Tue Mar 3 08:32:11 2015 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 03 Mar 2015 07:32:11 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Minor_neatening-up=2E__Mak?= =?utf-8?q?e_assignments_in_same_order_a_struct_fields=2E__Line-up?= Message-ID: <20150303073208.26066.56478@psf.io> https://hg.python.org/cpython/rev/656543a2ad75 changeset: 94841:656543a2ad75 user: Raymond Hettinger date: Mon Mar 02 23:32:02 2015 -0800 summary: Minor neatening-up. Make assignments in same order a struct fields. Line-up comments. files: Modules/_collectionsmodule.c | 24 ++++++++++++------------ 1 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -158,14 +158,14 @@ MARK_END(b->rightlink); assert(BLOCKLEN >= 2); + Py_SIZE(deque) = 0; deque->leftblock = b; deque->rightblock = b; deque->leftindex = CENTER + 1; deque->rightindex = CENTER; - Py_SIZE(deque) = 0; deque->state = 0; + deque->maxlen = -1; deque->weakreflist = NULL; - deque->maxlen = -1; return (PyObject *)deque; } @@ -775,9 +775,9 @@ assert (item != NULL); Py_DECREF(item); } - assert(deque->leftblock == deque->rightblock && - deque->leftindex - 1 == deque->rightindex && - Py_SIZE(deque) == 0); + assert(deque->leftblock == deque->rightblock); + assert(deque->leftindex - 1 == deque->rightindex); + assert(Py_SIZE(deque) == 0); } static int @@ -1152,10 +1152,10 @@ 0, /* sq_repeat */ (ssizeargfunc)deque_item, /* sq_item */ 0, /* sq_slice */ - (ssizeobjargproc)deque_ass_item, /* sq_ass_item */ + (ssizeobjargproc)deque_ass_item, /* sq_ass_item */ 0, /* sq_ass_slice */ 0, /* sq_contains */ - (binaryfunc)deque_inplace_concat, /* sq_inplace_concat */ + (binaryfunc)deque_inplace_concat, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; @@ -1254,8 +1254,8 @@ typedef struct { PyObject_HEAD + block *b; Py_ssize_t index; - block *b; dequeobject *deque; size_t state; /* state when the iterator is created */ Py_ssize_t counter; /* number of items remaining for iteration */ @@ -1374,7 +1374,7 @@ static PyTypeObject dequeiter_type = { PyVarObject_HEAD_INIT(NULL, 0) - "_collections._deque_iterator", /* tp_name */ + "_collections._deque_iterator", /* tp_name */ sizeof(dequeiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ @@ -1393,7 +1393,7 @@ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dequeiter_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -1496,7 +1496,7 @@ static PyTypeObject dequereviter_type = { PyVarObject_HEAD_INIT(NULL, 0) - "_collections._deque_reverse_iterator", /* tp_name */ + "_collections._deque_reverse_iterator", /* tp_name */ sizeof(dequeiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ @@ -1515,7 +1515,7 @@ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dequeiter_traverse, /* tp_traverse */ 0, /* tp_clear */ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 3 19:22:35 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Tue, 03 Mar 2015 18:22:35 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323563=3A_Optimize?= =?utf-8?q?d_utility_functions_in_urllib=2Eparse=2E?= Message-ID: <20150303182228.7304.60481@psf.io> https://hg.python.org/cpython/rev/461afc24fabc changeset: 94842:461afc24fabc user: Serhiy Storchaka date: Tue Mar 03 20:21:35 2015 +0200 summary: Issue #23563: Optimized utility functions in urllib.parse. files: Lib/urllib/parse.py | 88 ++++++++++---------------------- Misc/NEWS | 2 + 2 files changed, 30 insertions(+), 60 deletions(-) diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py --- a/Lib/urllib/parse.py +++ b/Lib/urllib/parse.py @@ -869,12 +869,12 @@ """splittype('type:opaquestring') --> 'type', 'opaquestring'.""" global _typeprog if _typeprog is None: - _typeprog = re.compile('^([^/:]+):') + _typeprog = re.compile('([^/:]+):(.*)', re.DOTALL) match = _typeprog.match(url) if match: - scheme = match.group(1) - return scheme.lower(), url[len(scheme) + 1:] + scheme, data = match.groups() + return scheme.lower(), data return None, url _hostprog = None @@ -882,38 +882,25 @@ """splithost('//host[:port]/path') --> 'host[:port]', '/path'.""" global _hostprog if _hostprog is None: - _hostprog = re.compile('^//([^/?]*)(.*)$') + _hostprog = re.compile('//([^/?]*)(.*)', re.DOTALL) match = _hostprog.match(url) if match: - host_port = match.group(1) - path = match.group(2) - if path and not path.startswith('/'): + host_port, path = match.groups() + if path and path[0] != '/': path = '/' + path return host_port, path return None, url -_userprog = None def splituser(host): """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" - global _userprog - if _userprog is None: - _userprog = re.compile('^(.*)@(.*)$') + user, delim, host = host.rpartition('@') + return (user if delim else None), host - match = _userprog.match(host) - if match: return match.group(1, 2) - return None, host - -_passwdprog = None def splitpasswd(user): """splitpasswd('user:passwd') -> 'user', 'passwd'.""" - global _passwdprog - if _passwdprog is None: - _passwdprog = re.compile('^([^:]*):(.*)$',re.S) - - match = _passwdprog.match(user) - if match: return match.group(1, 2) - return user, None + user, delim, passwd = user.partition(':') + return user, (passwd if delim else None) # splittag('/path#tag') --> '/path', 'tag' _portprog = None @@ -921,7 +908,7 @@ """splitport('host:port') --> 'host', 'port'.""" global _portprog if _portprog is None: - _portprog = re.compile('^(.*):([0-9]*)$') + _portprog = re.compile('(.*):([0-9]*)$', re.DOTALL) match = _portprog.match(host) if match: @@ -930,47 +917,34 @@ return host, port return host, None -_nportprog = None def splitnport(host, defport=-1): """Split host and port, returning numeric port. Return given default port if no ':' found; defaults to -1. Return numerical port if a valid number are found after ':'. Return None if ':' but not a valid number.""" - global _nportprog - if _nportprog is None: - _nportprog = re.compile('^(.*):(.*)$') - - match = _nportprog.match(host) - if match: - host, port = match.group(1, 2) - if port: - try: - nport = int(port) - except ValueError: - nport = None - return host, nport + host, delim, port = host.rpartition(':') + if not delim: + host = port + elif port: + try: + nport = int(port) + except ValueError: + nport = None + return host, nport return host, defport -_queryprog = None def splitquery(url): """splitquery('/path?query') --> '/path', 'query'.""" - global _queryprog - if _queryprog is None: - _queryprog = re.compile('^(.*)\?([^?]*)$') - - match = _queryprog.match(url) - if match: return match.group(1, 2) + path, delim, query = url.rpartition('?') + if delim: + return path, query return url, None -_tagprog = None def splittag(url): """splittag('/path#tag') --> '/path', 'tag'.""" - global _tagprog - if _tagprog is None: - _tagprog = re.compile('^(.*)#([^#]*)$') - - match = _tagprog.match(url) - if match: return match.group(1, 2) + path, delim, tag = url.rpartition('#') + if delim: + return path, tag return url, None def splitattr(url): @@ -979,13 +953,7 @@ words = url.split(';') return words[0], words[1:] -_valueprog = None def splitvalue(attr): """splitvalue('attr=value') --> 'attr', 'value'.""" - global _valueprog - if _valueprog is None: - _valueprog = re.compile('^([^=]*)=(.*)$') - - match = _valueprog.match(attr) - if match: return match.group(1, 2) - return attr, None + attr, delim, value = attr.partition('=') + return attr, (value if delim else None) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,8 @@ Library ------- +- Issue #23563: Optimized utility functions in urllib.parse. + - Issue #7830: Flatten nested functools.partial. - Issue #20204: Added the __module__ attribute to _tkinter classes. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 4 08:45:21 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 04 Mar 2015 07:45:21 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323504=3A_Added_an_=5F=5Fall=5F=5F_to_the_types_?= =?utf-8?q?module=2E?= Message-ID: <20150304074509.9672.43992@psf.io> https://hg.python.org/cpython/rev/5c73b01d58ac changeset: 94845:5c73b01d58ac parent: 94842:461afc24fabc parent: 94844:4888f9498db6 user: Serhiy Storchaka date: Wed Mar 04 09:44:24 2015 +0200 summary: Issue #23504: Added an __all__ to the types module. files: Lib/types.py | 3 +++ Misc/NEWS | 2 ++ 2 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Lib/types.py b/Lib/types.py --- a/Lib/types.py +++ b/Lib/types.py @@ -156,3 +156,6 @@ result = type(self)(self.fget, self.fset, fdel, self.__doc__) result.overwrite_doc = self.overwrite_doc return result + + +__all__ = [n for n in globals() if n[:1] != '_'] diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,8 @@ Library ------- +- Issue #23504: Added an __all__ to the types module. + - Issue #23563: Optimized utility functions in urllib.parse. - Issue #7830: Flatten nested functools.partial. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 4 08:45:21 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 04 Mar 2015 07:45:21 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIzNTA0?= =?utf-8?q?=3A_Added_an_=5F=5Fall=5F=5F_to_the_types_module=2E?= Message-ID: <20150304074508.16663.35097@psf.io> https://hg.python.org/cpython/rev/cb5fe8cc60eb changeset: 94843:cb5fe8cc60eb branch: 2.7 parent: 94830:3019effc44f2 user: Serhiy Storchaka date: Wed Mar 04 09:42:59 2015 +0200 summary: Issue #23504: Added an __all__ to the types module. files: Lib/types.py | 2 ++ Misc/NEWS | 2 ++ 2 files changed, 4 insertions(+), 0 deletions(-) diff --git a/Lib/types.py b/Lib/types.py --- a/Lib/types.py +++ b/Lib/types.py @@ -82,3 +82,5 @@ MemberDescriptorType = type(FunctionType.func_globals) del sys, _f, _g, _C, _x # Not for export + +__all__ = list(n for n in globals() if n[:1] != '_') diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,8 @@ Library ------- +- Issue #23504: Added an __all__ to the types module. + - Issue #23458: On POSIX, the file descriptor kept open by os.urandom() is now set to non inheritable -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 4 08:45:21 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 04 Mar 2015 07:45:21 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNTA0?= =?utf-8?q?=3A_Added_an_=5F=5Fall=5F=5F_to_the_types_module=2E?= Message-ID: <20150304074508.19083.23222@psf.io> https://hg.python.org/cpython/rev/4888f9498db6 changeset: 94844:4888f9498db6 branch: 3.4 parent: 94833:880906bbf792 user: Serhiy Storchaka date: Wed Mar 04 09:43:27 2015 +0200 summary: Issue #23504: Added an __all__ to the types module. files: Lib/types.py | 3 +++ Misc/NEWS | 2 ++ 2 files changed, 5 insertions(+), 0 deletions(-) diff --git a/Lib/types.py b/Lib/types.py --- a/Lib/types.py +++ b/Lib/types.py @@ -156,3 +156,6 @@ result = type(self)(self.fget, self.fset, fdel, self.__doc__) result.overwrite_doc = self.overwrite_doc return result + + +__all__ = [n for n in globals() if n[:1] != '_'] diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,8 @@ Library ------- +- Issue #23504: Added an __all__ to the types module. + - Issue #20204: Added the __module__ attribute to _tkinter classes. - Issue #23521: Corrected pure python implementation of timedelta division. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 4 18:47:31 2015 From: python-checkins at python.org (victor.stinner) Date: Wed, 04 Mar 2015 17:47:31 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323285=3A_Fix_hand?= =?utf-8?q?ling_of_EINTR_in_fileio=2Ec?= Message-ID: <20150304174555.7569.4701@psf.io> https://hg.python.org/cpython/rev/cad6eac598ec changeset: 94846:cad6eac598ec user: Victor Stinner date: Wed Mar 04 18:40:10 2015 +0100 summary: Issue #23285: Fix handling of EINTR in fileio.c Fix handling of EINTR: don't return None if PyErr_CheckSignals() raised an exception. Initialize also the length outside the loop to only initialize it once. files: Modules/_io/fileio.c | 74 +++++++++++++++++++------------ 1 files changed, 46 insertions(+), 28 deletions(-) diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -376,10 +376,12 @@ else #endif self->fd = open(name, flags, 0666); - Py_END_ALLOW_THREADS } while (self->fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (async_err) + goto error; } else { PyObject *fdobj; @@ -408,8 +410,7 @@ fd_is_own = 1; if (self->fd < 0) { - if (!async_err) - PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); goto error; } @@ -576,12 +577,15 @@ if (_PyVerify_fd(self->fd)) { len = pbuf.len; +#ifdef MS_WINDOWS + if (len > INT_MAX) + len = INT_MAX; +#endif + do { Py_BEGIN_ALLOW_THREADS errno = 0; #ifdef MS_WINDOWS - if (len > INT_MAX) - len = INT_MAX; n = read(self->fd, pbuf.buf, (int)len); #else n = read(self->fd, pbuf.buf, len); @@ -589,6 +593,9 @@ Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (async_err) + return NULL; } else n = -1; err = errno; @@ -597,8 +604,7 @@ if (err == EAGAIN) Py_RETURN_NONE; errno = err; - if (!async_err) - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_IOError); return NULL; } @@ -633,7 +639,7 @@ Py_off_t pos, end; PyObject *result; Py_ssize_t bytes_read = 0; - Py_ssize_t n; + Py_ssize_t len, n; size_t bufsize; int async_err = 0; @@ -682,20 +688,26 @@ return NULL; } } + + len = bufsize - bytes_read; +#ifdef MS_WINDOWS + if (len > INT_MAX) + len = INT_MAX; +#endif do { Py_BEGIN_ALLOW_THREADS errno = 0; - n = bufsize - bytes_read; #ifdef MS_WINDOWS - if (n > INT_MAX) - n = INT_MAX; - n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, (int)n); + n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, (int)len); #else - n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, n); + n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, len); #endif Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (async_err) + return NULL; if (n == 0) break; if (n < 0) { @@ -706,8 +718,7 @@ Py_RETURN_NONE; } Py_DECREF(result); - if (!async_err) - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_IOError); return NULL; } bytes_read += n; @@ -775,6 +786,9 @@ Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (async_err) + return NULL; } else n = -1; @@ -784,8 +798,7 @@ if (err == EAGAIN) Py_RETURN_NONE; errno = err; - if (!async_err) - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_IOError); return NULL; } @@ -815,19 +828,22 @@ return NULL; if (_PyVerify_fd(self->fd)) { + len = pbuf.len; +#ifdef MS_WINDOWS + if (len > 32767 && isatty(self->fd)) { + /* Issue #11395: the Windows console returns an error (12: not + enough space error) on writing into stdout if stdout mode is + binary and the length is greater than 66,000 bytes (or less, + depending on heap usage). */ + len = 32767; + } else if (len > INT_MAX) + len = INT_MAX; +#endif + do { Py_BEGIN_ALLOW_THREADS errno = 0; - len = pbuf.len; #ifdef MS_WINDOWS - if (len > 32767 && isatty(self->fd)) { - /* Issue #11395: the Windows console returns an error (12: not - enough space error) on writing into stdout if stdout mode is - binary and the length is greater than 66,000 bytes (or less, - depending on heap usage). */ - len = 32767; - } else if (len > INT_MAX) - len = INT_MAX; n = write(self->fd, pbuf.buf, (int)len); #else n = write(self->fd, pbuf.buf, len); @@ -835,6 +851,9 @@ Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (async_err) + return NULL; } else n = -1; err = errno; @@ -845,8 +864,7 @@ if (err == EAGAIN) Py_RETURN_NONE; errno = err; - if (!async_err) - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_IOError); return NULL; } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 4 19:52:50 2015 From: python-checkins at python.org (larry.hastings) Date: Wed, 04 Mar 2015 18:52:50 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_PEP_485_has_been_accepted=2C_?= =?utf-8?q?so_it_goes_in_the_list_for_Python_3=2E5=2E?= Message-ID: <20150304185249.7587.65440@psf.io> https://hg.python.org/peps/rev/a9d52dee7afe changeset: 5720:a9d52dee7afe user: Larry Hastings date: Wed Mar 04 10:52:44 2015 -0800 summary: PEP 485 has been accepted, so it goes in the list for Python 3.5. files: pep-0478.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/pep-0478.txt b/pep-0478.txt --- a/pep-0478.txt +++ b/pep-0478.txt @@ -66,6 +66,7 @@ * PEP 448, additional unpacking generalizations * PEP 471, os.scandir() * PEP 475, retrying system calls that fail with EINTR +* PEP 485, math.isclose(), a function for testing approximate equality * PEP 486, make the Python Launcher aware of virtual environments Proposed changes for 3.5: -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Wed Mar 4 20:56:34 2015 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 04 Mar 2015 19:56:34 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNTc2?= =?utf-8?q?=3A_Avoid_stalling_in_SSL_reads_when_EOF_has_been_reached_in_th?= =?utf-8?q?e_SSL?= Message-ID: <20150304195629.9662.34342@psf.io> https://hg.python.org/cpython/rev/01cf9ce75eda changeset: 94847:01cf9ce75eda branch: 3.4 parent: 94844:4888f9498db6 user: Antoine Pitrou date: Wed Mar 04 20:51:55 2015 +0100 summary: Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the SSL layer but the underlying connection hasn't been closed. files: Misc/NEWS | 3 +++ Modules/_ssl.c | 20 -------------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,9 @@ Library ------- +- Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the + SSL layer but the underlying connection hasn't been closed. + - Issue #23504: Added an __all__ to the types module. - Issue #20204: Added the __module__ attribute to _tkinter classes. diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1717,26 +1717,6 @@ BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); - /* first check if there are bytes ready to be read */ - PySSL_BEGIN_ALLOW_THREADS - count = SSL_pending(self->ssl); - PySSL_END_ALLOW_THREADS - - if (!count) { - sockstate = check_socket_and_wait_for_timeout(sock, 0); - if (sockstate == SOCKET_HAS_TIMED_OUT) { - PyErr_SetString(PySocketModule.timeout_error, - "The read operation timed out"); - goto error; - } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { - PyErr_SetString(PySSLErrorObject, - "Underlying socket too large for select()."); - goto error; - } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { - count = 0; - goto done; - } - } do { PySSL_BEGIN_ALLOW_THREADS count = SSL_read(self->ssl, mem, len); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 4 21:02:26 2015 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 04 Mar 2015 20:02:26 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323576=3A_Avoid_stalling_in_SSL_reads_when_EOF_h?= =?utf-8?q?as_been_reached_in_the_SSL?= Message-ID: <20150304195629.1775.85920@psf.io> https://hg.python.org/cpython/rev/fc0201ccbcd4 changeset: 94848:fc0201ccbcd4 parent: 94846:cad6eac598ec parent: 94847:01cf9ce75eda user: Antoine Pitrou date: Wed Mar 04 20:54:57 2015 +0100 summary: Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the SSL layer but the underlying connection hasn't been closed. files: Misc/NEWS | 3 +++ Modules/_ssl.c | 20 -------------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,9 @@ Library ------- +- Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the + SSL layer but the underlying connection hasn't been closed. + - Issue #23504: Added an __all__ to the types module. - Issue #23563: Optimized utility functions in urllib.parse. diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1841,26 +1841,6 @@ BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); } - /* first check if there are bytes ready to be read */ - PySSL_BEGIN_ALLOW_THREADS - count = SSL_pending(self->ssl); - PySSL_END_ALLOW_THREADS - - if (!count) { - sockstate = check_socket_and_wait_for_timeout(sock, 0); - if (sockstate == SOCKET_HAS_TIMED_OUT) { - PyErr_SetString(PySocketModule.timeout_error, - "The read operation timed out"); - goto error; - } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { - PyErr_SetString(PySSLErrorObject, - "Underlying socket too large for select()."); - goto error; - } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { - count = 0; - goto done; - } - } do { PySSL_BEGIN_ALLOW_THREADS count = SSL_read(self->ssl, mem, len); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 4 21:08:51 2015 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 04 Mar 2015 20:08:51 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIzNTc2?= =?utf-8?q?=3A_Avoid_stalling_in_SSL_reads_when_EOF_has_been_reached_in_th?= =?utf-8?q?e_SSL?= Message-ID: <20150304195809.16663.31074@psf.io> https://hg.python.org/cpython/rev/371cf371a6a1 changeset: 94849:371cf371a6a1 branch: 2.7 parent: 94843:cb5fe8cc60eb user: Antoine Pitrou date: Wed Mar 04 20:51:55 2015 +0100 summary: Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the SSL layer but the underlying connection hasn't been closed. files: Misc/NEWS | 3 +++ Modules/_ssl.c | 20 -------------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,9 @@ Library ------- +- Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the + SSL layer but the underlying connection hasn't been closed. + - Issue #23504: Added an __all__ to the types module. - Issue #23458: On POSIX, the file descriptor kept open by os.urandom() is now diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1720,26 +1720,6 @@ BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); - /* first check if there are bytes ready to be read */ - PySSL_BEGIN_ALLOW_THREADS - count = SSL_pending(self->ssl); - PySSL_END_ALLOW_THREADS - - if (!count) { - sockstate = check_socket_and_wait_for_timeout(sock, 0); - if (sockstate == SOCKET_HAS_TIMED_OUT) { - PyErr_SetString(PySSLErrorObject, - "The read operation timed out"); - goto error; - } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { - PyErr_SetString(PySSLErrorObject, - "Underlying socket too large for select()."); - goto error; - } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { - count = 0; - goto done; - } - } do { PySSL_BEGIN_ALLOW_THREADS count = SSL_read(self->ssl, mem, len); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 00:08:26 2015 From: python-checkins at python.org (robert.collins) Date: Wed, 04 Mar 2015 23:08:26 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317911=3A_tracebac?= =?utf-8?q?k_module_overhaul?= Message-ID: <20150304230825.9688.15131@psf.io> https://hg.python.org/cpython/rev/73afda5a4e4c changeset: 94850:73afda5a4e4c parent: 94848:fc0201ccbcd4 user: Robert Collins date: Thu Mar 05 12:07:57 2015 +1300 summary: Issue #17911: traceback module overhaul Provide a way to seed the linecache for a PEP-302 module without actually loading the code. Provide a new object API for traceback, including the ability to not lookup lines at all until the traceback is actually rendered, without any trace of the original objects being kept alive. files: Doc/library/linecache.rst | 8 + Doc/library/traceback.rst | 124 ++++++ Lib/linecache.py | 88 +++- Lib/test/test_linecache.py | 44 ++ Lib/test/test_traceback.py | 196 ++++++++++ Lib/traceback.py | 478 ++++++++++++++++++------ Misc/NEWS | 7 + 7 files changed, 791 insertions(+), 154 deletions(-) diff --git a/Doc/library/linecache.rst b/Doc/library/linecache.rst --- a/Doc/library/linecache.rst +++ b/Doc/library/linecache.rst @@ -43,6 +43,14 @@ changed on disk, and you require the updated version. If *filename* is omitted, it will check all the entries in the cache. +.. function:: lazycache(filename, module_globals) + + Capture enough detail about a non-file based module to permit getting its + lines later via :func:`getline` even if *module_globals* is None in the later + call. This avoids doing I/O until a line is actually needed, without having + to carry the module globals around indefinitely. + + .. versionadded:: 3.5 Example:: diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -136,6 +136,130 @@ .. versionadded:: 3.4 +.. function:: walk_stack(f) + + Walk a stack following f.f_back from the given frame, yielding the frame and + line number for each frame. If f is None, the current stack is used. + This helper is used with *Stack.extract*. + + .. versionadded:: 3.5 + +.. function:: walk_tb(tb) + + Walk a traceback following tb_next yielding the frame and line number for + each frame. This helper is used with *Stack.extract*. + + .. versionadded:: 3.5 + +The module also defines the following classes: + +:class:`TracebackException` Objects +----------------------------------- + +:class:`.TracebackException` objects are created from actual exceptions to +capture data for later printing in a lightweight fashion. + +.. class:: TracebackException(exc_type, exc_value, exc_traceback, limit=None, lookup_lines=True) + + Capture an exception for later rendering. limit, lookup_lines are as for + the :class:`.StackSummary` class. + + .. versionadded:: 3.5 + +.. classmethod:: `.from_exception`(exc, limit=None, lookup_lines=True) + + Capture an exception for later rendering. limit and lookup_lines + are as for the :class:`.StackSummary` class. + + .. versionadded:: 3.5 + +.. attribute:: `.__cause__` A TracebackException of the original *__cause__*. + +.. attribute:: `.__context__` A TracebackException of the original *__context__*. +.. attribute:: `.__suppress_context__` The *__suppress_context__* value from the + original exception. +.. attribute:: `.stack` A `StackSummary` representing the traceback. +.. attribute:: `.exc_type` The class of the original traceback. +.. attribute:: `.filename` For syntax errors - the filename where the error + occured. +.. attribute:: `.lineno` For syntax errors - the linenumber where the error + occured. +.. attribute:: `.text` For syntax errors - the text where the error + occured. +.. attribute:: `.offset` For syntax errors - the offset into the text where the + error occured. +.. attribute:: `.msg` For syntax errors - the compiler error message. + +.. method:: TracebackException.format(chain=True) + + Format the exception. + + If chain is not *True*, *__cause__* and *__context__* will not be formatted. + + The return value is a generator of strings, each ending in a newline and + some containing internal newlines. `print_exception` is a wrapper around + this method which just prints the lines to a file. + + The message indicating which exception occurred is always the last + string in the output. + + .. versionadded:: 3.5 + +.. method:: TracebackException.format_exception_only() + + Format the exception part of the traceback. + + The return value is a generator of strings, each ending in a newline. + + Normally, the generator emits a single string; however, for + SyntaxError exceptions, it emites several lines that (when + printed) display detailed information about where the syntax + error occurred. + + The message indicating which exception occurred is always the last + string in the output. + + .. versionadded:: 3.5 + + +:class:`StackSummary` Objects +----------------------------- + +:class:`.StackSummary` objects represent a call stack ready for formatting. + +.. classmethod:: StackSummary.extract(frame_gen, limit=None, lookup_lines=True) + + Construct a StackSummary object from a frame generator (such as is returned by + `walk_stack` or `walk_tb`. + + If limit is supplied, only this many frames are taken from frame_gen. + If lookup_lines is False, the returned FrameSummary objects will not have read + their lines in yet, making the cost of creating the StackSummary cheaper (which + may be valuable if it may not actually get formatted). + + .. versionadded:: 3.5 + +.. classmethod:: StackSummary.from_list(a_list) + + Construct a StackSummary object from a supplied old-style list of tuples. Each + tuple should be a 4-tuple with filename, lineno, name, line as the elements. + + .. versionadded:: 3.5 + +:class:`FrameSummary` Objects +----------------------------- + +FrameSummary objects represent a single frame in a traceback. + +.. class:: FrameSummary(filename, lineno, name, lookup_line=True, locals=None, line=None) + :noindex: + + Represent a single frame in the traceback or stack that is being formatted + or printed. It may optionally have a stringified version of the frames + locals included in it. If *lookup_line* is False, the source code is not + looked up until the FrameSummary has the :attr:`line` attribute accessed (which + also happens when casting it to a tuple). Line may be directly provided, and + will prevent line lookups happening at all. .. _traceback-example: diff --git a/Lib/linecache.py b/Lib/linecache.py --- a/Lib/linecache.py +++ b/Lib/linecache.py @@ -5,6 +5,7 @@ that name. """ +import functools import sys import os import tokenize @@ -21,7 +22,9 @@ # The cache -cache = {} # The cache +# The cache. Maps filenames to either a thunk which will provide source code, +# or a tuple (size, mtime, lines, fullname) once loaded. +cache = {} def clearcache(): @@ -36,6 +39,9 @@ Update the cache if it doesn't contain an entry for this file already.""" if filename in cache: + entry = cache[filename] + if len(entry) == 1: + return updatecache(filename, module_globals) return cache[filename][2] else: return updatecache(filename, module_globals) @@ -54,7 +60,11 @@ return for filename in filenames: - size, mtime, lines, fullname = cache[filename] + entry = cache[filename] + if len(entry) == 1: + # lazy cache entry, leave it lazy. + continue + size, mtime, lines, fullname = entry if mtime is None: continue # no-op for files loaded via a __loader__ try: @@ -72,7 +82,8 @@ and return an empty list.""" if filename in cache: - del cache[filename] + if len(cache[filename]) != 1: + del cache[filename] if not filename or (filename.startswith('<') and filename.endswith('>')): return [] @@ -82,27 +93,23 @@ except OSError: basename = filename - # Try for a __loader__, if available - if module_globals and '__loader__' in module_globals: - name = module_globals.get('__name__') - loader = module_globals['__loader__'] - get_source = getattr(loader, 'get_source', None) - - if name and get_source: - try: - data = get_source(name) - except (ImportError, OSError): - pass - else: - if data is None: - # No luck, the PEP302 loader cannot find the source - # for this module. - return [] - cache[filename] = ( - len(data), None, - [line+'\n' for line in data.splitlines()], fullname - ) - return cache[filename][2] + # Realise a lazy loader based lookup if there is one + # otherwise try to lookup right now. + if lazycache(filename, module_globals): + try: + data = cache[filename][0]() + except (ImportError, OSError): + pass + else: + if data is None: + # No luck, the PEP302 loader cannot find the source + # for this module. + return [] + cache[filename] = ( + len(data), None, + [line+'\n' for line in data.splitlines()], fullname + ) + return cache[filename][2] # Try looking through the module search path, which is only useful # when handling a relative filename. @@ -132,3 +139,36 @@ size, mtime = stat.st_size, stat.st_mtime cache[filename] = size, mtime, lines, fullname return lines + + +def lazycache(filename, module_globals): + """Seed the cache for filename with module_globals. + + The module loader will be asked for the source only when getlines is + called, not immediately. + + If there is an entry in the cache already, it is not altered. + + :return: True if a lazy load is registered in the cache, + otherwise False. To register such a load a module loader with a + get_source method must be found, the filename must be a cachable + filename, and the filename must not be already cached. + """ + if filename in cache: + if len(cache[filename]) == 1: + return True + else: + return False + if not filename or (filename.startswith('<') and filename.endswith('>')): + return False + # Try for a __loader__, if available + if module_globals and '__loader__' in module_globals: + name = module_globals.get('__name__') + loader = module_globals['__loader__'] + get_source = getattr(loader, 'get_source', None) + + if name and get_source: + get_lines = functools.partial(get_source, name) + cache[filename] = (get_lines,) + return True + return False diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py --- a/Lib/test/test_linecache.py +++ b/Lib/test/test_linecache.py @@ -7,6 +7,7 @@ FILENAME = linecache.__file__ +NONEXISTENT_FILENAME = FILENAME + '.missing' INVALID_NAME = '!@$)(!@#_1' EMPTY = '' TESTS = 'inspect_fodder inspect_fodder2 mapping_tests' @@ -126,6 +127,49 @@ self.assertEqual(line, getline(source_name, index + 1)) source_list.append(line) + def test_lazycache_no_globals(self): + lines = linecache.getlines(FILENAME) + linecache.clearcache() + self.assertEqual(False, linecache.lazycache(FILENAME, None)) + self.assertEqual(lines, linecache.getlines(FILENAME)) + + def test_lazycache_smoke(self): + lines = linecache.getlines(NONEXISTENT_FILENAME, globals()) + linecache.clearcache() + self.assertEqual( + True, linecache.lazycache(NONEXISTENT_FILENAME, globals())) + self.assertEqual(1, len(linecache.cache[NONEXISTENT_FILENAME])) + # Note here that we're looking up a non existant filename with no + # globals: this would error if the lazy value wasn't resolved. + self.assertEqual(lines, linecache.getlines(NONEXISTENT_FILENAME)) + + def test_lazycache_provide_after_failed_lookup(self): + linecache.clearcache() + lines = linecache.getlines(NONEXISTENT_FILENAME, globals()) + linecache.clearcache() + linecache.getlines(NONEXISTENT_FILENAME) + linecache.lazycache(NONEXISTENT_FILENAME, globals()) + self.assertEqual(lines, linecache.updatecache(NONEXISTENT_FILENAME)) + + def test_lazycache_check(self): + linecache.clearcache() + linecache.lazycache(NONEXISTENT_FILENAME, globals()) + linecache.checkcache() + + def test_lazycache_bad_filename(self): + linecache.clearcache() + self.assertEqual(False, linecache.lazycache('', globals())) + self.assertEqual(False, linecache.lazycache('', globals())) + + def test_lazycache_already_cached(self): + linecache.clearcache() + lines = linecache.getlines(NONEXISTENT_FILENAME, globals()) + self.assertEqual( + False, + linecache.lazycache(NONEXISTENT_FILENAME, globals())) + self.assertEqual(4, len(linecache.cache[NONEXISTENT_FILENAME])) + + def test_main(): support.run_unittest(LineCacheTests) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1,6 +1,8 @@ """Test cases for traceback module""" +from collections import namedtuple from io import StringIO +import linecache import sys import unittest import re @@ -12,6 +14,11 @@ import traceback +test_code = namedtuple('code', ['co_filename', 'co_name']) +test_frame = namedtuple('frame', ['f_code', 'f_globals']) +test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next']) + + class SyntaxTracebackCases(unittest.TestCase): # For now, a very minimal set of tests. I want to be sure that # formatting of SyntaxErrors works based on changes for 2.1. @@ -477,6 +484,195 @@ self.assertEqual(len(inner_frame.f_locals), 0) +class TestFrame(unittest.TestCase): + + def test_basics(self): + linecache.clearcache() + linecache.lazycache("f", globals()) + f = traceback.FrameSummary("f", 1, "dummy") + self.assertEqual( + ("f", 1, "dummy", '"""Test cases for traceback module"""'), + tuple(f)) + self.assertEqual(None, f.locals) + + def test_lazy_lines(self): + linecache.clearcache() + f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False) + self.assertEqual(None, f._line) + linecache.lazycache("f", globals()) + self.assertEqual( + '"""Test cases for traceback module"""', + f.line) + + def test_explicit_line(self): + f = traceback.FrameSummary("f", 1, "dummy", line="line") + self.assertEqual("line", f.line) + + +class TestStack(unittest.TestCase): + + def test_walk_stack(self): + s = list(traceback.walk_stack(None)) + self.assertGreater(len(s), 10) + + def test_walk_tb(self): + try: + 1/0 + except Exception: + _, _, tb = sys.exc_info() + s = list(traceback.walk_tb(tb)) + self.assertEqual(len(s), 1) + + def test_extract_stack(self): + s = traceback.StackSummary.extract(traceback.walk_stack(None)) + self.assertIsInstance(s, traceback.StackSummary) + + def test_extract_stack_limit(self): + s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5) + self.assertEqual(len(s), 5) + + def test_extract_stack_lookup_lines(self): + linecache.clearcache() + linecache.updatecache('/foo.py', globals()) + c = test_code('/foo.py', 'method') + f = test_frame(c, None) + s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True) + linecache.clearcache() + self.assertEqual(s[0].line, "import sys") + + def test_extract_stackup_deferred_lookup_lines(self): + linecache.clearcache() + c = test_code('/foo.py', 'method') + f = test_frame(c, None) + s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False) + self.assertEqual({}, linecache.cache) + linecache.updatecache('/foo.py', globals()) + self.assertEqual(s[0].line, "import sys") + + def test_from_list(self): + s = traceback.StackSummary([('foo.py', 1, 'fred', 'line')]) + self.assertEqual( + [' File "foo.py", line 1, in fred\n line\n'], + s.format()) + + def test_format_smoke(self): + # For detailed tests see the format_list tests, which consume the same + # code. + s = traceback.StackSummary([('foo.py', 1, 'fred', 'line')]) + self.assertEqual( + [' File "foo.py", line 1, in fred\n line\n'], + s.format()) + + + +class TestTracebackException(unittest.TestCase): + + def test_smoke(self): + try: + 1/0 + except Exception: + exc_info = sys.exc_info() + exc = traceback.TracebackException(*exc_info) + expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2])) + self.assertEqual(None, exc.__cause__) + self.assertEqual(None, exc.__context__) + self.assertEqual(False, exc.__suppress_context__) + self.assertEqual(expected_stack, exc.stack) + self.assertEqual(exc_info[0], exc.exc_type) + self.assertEqual(str(exc_info[1]), str(exc)) + + def test_from_exception(self): + # Check all the parameters are accepted. + def foo(): + 1/0 + try: + foo() + except Exception as e: + exc_info = sys.exc_info() + self.expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False, + capture_locals=True) + self.exc = traceback.TracebackException.from_exception( + e, limit=1, lookup_lines=False, capture_locals=True) + expected_stack = self.expected_stack + exc = self.exc + self.assertEqual(None, exc.__cause__) + self.assertEqual(None, exc.__context__) + self.assertEqual(False, exc.__suppress_context__) + self.assertEqual(expected_stack, exc.stack) + self.assertEqual(exc_info[0], exc.exc_type) + self.assertEqual(str(exc_info[1]), str(exc)) + + def test_cause(self): + try: + try: + 1/0 + finally: + exc_info_context = sys.exc_info() + exc_context = traceback.TracebackException(*exc_info_context) + cause = Exception("cause") + raise Exception("uh oh") from cause + except Exception: + exc_info = sys.exc_info() + exc = traceback.TracebackException(*exc_info) + expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2])) + exc_cause = traceback.TracebackException(Exception, cause, None) + self.assertEqual(exc_cause, exc.__cause__) + self.assertEqual(exc_context, exc.__context__) + self.assertEqual(True, exc.__suppress_context__) + self.assertEqual(expected_stack, exc.stack) + self.assertEqual(exc_info[0], exc.exc_type) + self.assertEqual(str(exc_info[1]), str(exc)) + + def test_context(self): + try: + try: + 1/0 + finally: + exc_info_context = sys.exc_info() + exc_context = traceback.TracebackException(*exc_info_context) + raise Exception("uh oh") + except Exception: + exc_info = sys.exc_info() + exc = traceback.TracebackException(*exc_info) + expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2])) + self.assertEqual(None, exc.__cause__) + self.assertEqual(exc_context, exc.__context__) + self.assertEqual(False, exc.__suppress_context__) + self.assertEqual(expected_stack, exc.stack) + self.assertEqual(exc_info[0], exc.exc_type) + self.assertEqual(str(exc_info[1]), str(exc)) + + def test_limit(self): + def recurse(n): + if n: + recurse(n-1) + else: + 1/0 + try: + recurse(10) + except Exception: + exc_info = sys.exc_info() + exc = traceback.TracebackException(*exc_info, limit=5) + expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2]), limit=5) + self.assertEqual(expected_stack, exc.stack) + + def test_lookup_lines(self): + linecache.clearcache() + e = Exception("uh oh") + c = test_code('/foo.py', 'method') + f = test_frame(c, None) + tb = test_tb(f, 6, None) + exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False) + self.assertEqual({}, linecache.cache) + linecache.updatecache('/foo.py', globals()) + self.assertEqual(exc.stack[0].line, "import sys") + + def test_main(): run_unittest(__name__) diff --git a/Lib/traceback.py b/Lib/traceback.py --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -14,19 +14,12 @@ # Formatting and printing lists of traceback lines. # -def _format_list_iter(extracted_list): - for filename, lineno, name, line in extracted_list: - item = ' File "{}", line {}, in {}\n'.format(filename, lineno, name) - if line: - item = item + ' {}\n'.format(line.strip()) - yield item - def print_list(extracted_list, file=None): """Print the list of tuples as returned by extract_tb() or extract_stack() as a formatted stack trace to the given file.""" if file is None: file = sys.stderr - for item in _format_list_iter(extracted_list): + for item in StackSummary.from_list(extracted_list).format(): print(item, file=file, end="") def format_list(extracted_list): @@ -39,45 +32,12 @@ the strings may contain internal newlines as well, for those items whose source text line is not None. """ - return list(_format_list_iter(extracted_list)) + return StackSummary.from_list(extracted_list).format() # # Printing and Extracting Tracebacks. # -# extractor takes curr and needs to return a tuple of: -# - Frame object -# - Line number -# - Next item (same type as curr) -# In practice, curr is either a traceback or a frame. -def _extract_tb_or_stack_iter(curr, limit, extractor): - if limit is None: - limit = getattr(sys, 'tracebacklimit', None) - - n = 0 - while curr is not None and (limit is None or n < limit): - f, lineno, next_item = extractor(curr) - co = f.f_code - filename = co.co_filename - name = co.co_name - - linecache.checkcache(filename) - line = linecache.getline(filename, lineno, f.f_globals) - - if line: - line = line.strip() - else: - line = None - - yield (filename, lineno, name, line) - curr = next_item - n += 1 - -def _extract_tb_iter(tb, limit): - return _extract_tb_or_stack_iter( - tb, limit, - operator.attrgetter("tb_frame", "tb_lineno", "tb_next")) - def print_tb(tb, limit=None, file=None): """Print up to 'limit' stack trace entries from the traceback 'tb'. @@ -90,7 +50,7 @@ def format_tb(tb, limit=None): """A shorthand for 'format_list(extract_tb(tb, limit))'.""" - return format_list(extract_tb(tb, limit=limit)) + return extract_tb(tb, limit=limit).format() def extract_tb(tb, limit=None): """Return list of up to limit pre-processed entries from traceback. @@ -103,7 +63,7 @@ leading and trailing whitespace stripped; if the source is not available it is None. """ - return list(_extract_tb_iter(tb, limit=limit)) + return StackSummary.extract(walk_tb(tb), limit=limit) # # Exception formatting and output. @@ -111,47 +71,12 @@ _cause_message = ( "\nThe above exception was the direct cause " - "of the following exception:\n") + "of the following exception:\n\n") _context_message = ( "\nDuring handling of the above exception, " - "another exception occurred:\n") + "another exception occurred:\n\n") -def _iter_chain(exc, custom_tb=None, seen=None): - if seen is None: - seen = set() - seen.add(exc) - its = [] - context = exc.__context__ - cause = exc.__cause__ - if cause is not None and cause not in seen: - its.append(_iter_chain(cause, False, seen)) - its.append([(_cause_message, None)]) - elif (context is not None and - not exc.__suppress_context__ and - context not in seen): - its.append(_iter_chain(context, None, seen)) - its.append([(_context_message, None)]) - its.append([(exc, custom_tb or exc.__traceback__)]) - # itertools.chain is in an extension module and may be unavailable - for it in its: - yield from it - -def _format_exception_iter(etype, value, tb, limit, chain): - if chain: - values = _iter_chain(value, tb) - else: - values = [(value, tb)] - - for value, tb in values: - if isinstance(value, str): - # This is a cause/context message line - yield value + '\n' - continue - if tb: - yield 'Traceback (most recent call last):\n' - yield from _format_list_iter(_extract_tb_iter(tb, limit=limit)) - yield from _format_exception_only_iter(type(value), value) def print_exception(etype, value, tb, limit=None, file=None, chain=True): """Print exception up to 'limit' stack trace entries from 'tb' to 'file'. @@ -166,9 +91,11 @@ """ if file is None: file = sys.stderr - for line in _format_exception_iter(etype, value, tb, limit, chain): + for line in TracebackException( + etype, value, tb, limit=limit).format(chain=chain): print(line, file=file, end="") + def format_exception(etype, value, tb, limit=None, chain=True): """Format a stack trace and the exception information. @@ -178,7 +105,9 @@ these lines are concatenated and printed, exactly the same text is printed as does print_exception(). """ - return list(_format_exception_iter(etype, value, tb, limit, chain)) + return list(TracebackException( + etype, value, tb, limit=limit).format(chain=chain)) + def format_exception_only(etype, value): """Format the exception part of a traceback. @@ -196,46 +125,14 @@ string in the list. """ - return list(_format_exception_only_iter(etype, value)) + return list(TracebackException(etype, value, None).format_exception_only()) -def _format_exception_only_iter(etype, value): - # Gracefully handle (the way Python 2.4 and earlier did) the case of - # being called with (None, None). - if etype is None: - yield _format_final_exc_line(etype, value) - return - stype = etype.__qualname__ - smod = etype.__module__ - if smod not in ("__main__", "builtins"): - stype = smod + '.' + stype - - if not issubclass(etype, SyntaxError): - yield _format_final_exc_line(stype, value) - return - - # It was a syntax error; show exactly where the problem was found. - filename = value.filename or "" - lineno = str(value.lineno) or '?' - yield ' File "{}", line {}\n'.format(filename, lineno) - - badline = value.text - offset = value.offset - if badline is not None: - yield ' {}\n'.format(badline.strip()) - if offset is not None: - caretspace = badline.rstrip('\n') - offset = min(len(caretspace), offset) - 1 - caretspace = caretspace[:offset].lstrip() - # non-space whitespace (likes tabs) must be kept for alignment - caretspace = ((c.isspace() and c or ' ') for c in caretspace) - yield ' {}^\n'.format(''.join(caretspace)) - msg = value.msg or "" - yield "{}: {}\n".format(stype, msg) +# -- not offical API but folk probably use these two functions. def _format_final_exc_line(etype, value): valuestr = _some_str(value) - if value is None or not valuestr: + if value == 'None' or value is None or not valuestr: line = "%s\n" % etype else: line = "%s: %s\n" % (etype, valuestr) @@ -247,6 +144,8 @@ except: return '' % type(value).__name__ +# -- + def print_exc(limit=None, file=None, chain=True): """Shorthand for 'print_exception(*sys.exc_info(), limit, file)'.""" print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain) @@ -267,15 +166,6 @@ # Printing and Extracting Stacks. # -def _extract_stack_iter(f, limit=None): - return _extract_tb_or_stack_iter( - f, limit, lambda f: (f, f.f_lineno, f.f_back)) - -def _get_stack(f): - if f is None: - f = sys._getframe().f_back.f_back - return f - def print_stack(f=None, limit=None, file=None): """Print a stack trace from its invocation point. @@ -283,11 +173,13 @@ stack frame at which to start. The optional 'limit' and 'file' arguments have the same meaning as for print_exception(). """ - print_list(extract_stack(_get_stack(f), limit=limit), file=file) + print_list(extract_stack(f, limit=limit), file=file) + def format_stack(f=None, limit=None): """Shorthand for 'format_list(extract_stack(f, limit))'.""" - return format_list(extract_stack(_get_stack(f), limit=limit)) + return format_list(extract_stack(f, limit=limit)) + def extract_stack(f=None, limit=None): """Extract the raw traceback from the current stack frame. @@ -298,10 +190,11 @@ line number, function name, text), and the entries are in order from oldest to newest stack frame. """ - stack = list(_extract_stack_iter(_get_stack(f), limit=limit)) + stack = StackSummary.extract(walk_stack(f), limit=limit) stack.reverse() return stack + def clear_frames(tb): "Clear all references to local variables in the frames of a traceback." while tb is not None: @@ -311,3 +204,328 @@ # Ignore the exception raised if the frame is still executing. pass tb = tb.tb_next + + +class FrameSummary: + """A single frame from a traceback. + + - :attr:`filename` The filename for the frame. + - :attr:`lineno` The line within filename for the frame that was + active when the frame was captured. + - :attr:`name` The name of the function or method that was executing + when the frame was captured. + - :attr:`line` The text from the linecache module for the + of code that was running when the frame was captured. + - :attr:`locals` Either None if locals were not supplied, or a dict + mapping the name to the str() of the variable. + """ + + __slots__ = ('filename', 'lineno', 'name', '_line', 'locals') + + def __init__(self, filename, lineno, name, lookup_line=True, locals=None, + line=None): + """Construct a FrameSummary. + + :param lookup_line: If True, `linecache` is consulted for the source + code line. Otherwise, the line will be looked up when first needed. + :param locals: If supplied the frame locals, which will be captured as + strings. + :param line: If provided, use this instead of looking up the line in + the linecache. + """ + self.filename = filename + self.lineno = lineno + self.name = name + self._line = line + if lookup_line: + self.line + self.locals = \ + dict((k, str(v)) for k, v in locals.items()) if locals else None + + def __eq__(self, other): + return (self.filename == other.filename and + self.lineno == other.lineno and + self.name == other.name and + self.locals == other.locals) + + def __getitem__(self, pos): + return (self.filename, self.lineno, self.name, self.line)[pos] + + def __iter__(self): + return iter([self.filename, self.lineno, self.name, self.line]) + + def __repr__(self): + return "".format( + filename=self.filename, lineno=self.lineno, name=self.name) + + @property + def line(self): + if self._line is None: + self._line = linecache.getline(self.filename, self.lineno).strip() + return self._line + + +def walk_stack(f): + """Walk a stack yielding the frame and line number for each frame. + + This will follow f.f_back from the given frame. If no frame is given, the + current stack is used. Usually used with StackSummary.extract. + """ + if f is None: + f = sys._getframe().f_back.f_back + while f is not None: + yield f, f.f_lineno + f = f.f_back + + +def walk_tb(tb): + """Walk a traceback yielding the frame and line number for each frame. + + This will follow tb.tb_next (and thus is in the opposite order to + walk_stack). Usually used with StackSummary.extract. + """ + while tb is not None: + yield tb.tb_frame, tb.tb_lineno + tb = tb.tb_next + + +class StackSummary(list): + """A stack of frames.""" + + @classmethod + def extract(klass, frame_gen, limit=None, lookup_lines=True): + """Create a StackSummary from a traceback or stack object. + + :param frame_gen: A generator that yields (frame, lineno) tuples to + include in the stack. + :param limit: None to include all frames or the number of frames to + include. + :param lookup_lines: If True, lookup lines for each frame immediately, + otherwise lookup is deferred until the frame is rendered. + """ + if limit is None: + limit = getattr(sys, 'tracebacklimit', None) + + result = klass() + fnames = set() + for pos, (f, lineno) in enumerate(frame_gen): + if limit is not None and pos >= limit: + break + co = f.f_code + filename = co.co_filename + name = co.co_name + + fnames.add(filename) + linecache.lazycache(filename, f.f_globals) + # Must defer line lookups until we have called checkcache. + result.append(FrameSummary(filename, lineno, name, lookup_line=False)) + for filename in fnames: + linecache.checkcache(filename) + # If immediate lookup was desired, trigger lookups now. + if lookup_lines: + for f in result: + f.line + return result + + @classmethod + def from_list(klass, a_list): + """Create a StackSummary from a simple list of tuples. + + This method supports the older Python API. Each tuple should be a + 4-tuple with (filename, lineno, name, line) elements. + """ + if isinstance(a_list, StackSummary): + return StackSummary(a_list) + result = StackSummary() + for filename, lineno, name, line in a_list: + result.append(FrameSummary(filename, lineno, name, line=line)) + return result + + def format(self): + """Format the stack ready for printing. + + Returns a list of strings ready for printing. Each string in the + resulting list corresponds to a single frame from the stack. + Each string ends in a newline; the strings may contain internal + newlines as well, for those items with source text lines. + """ + result = [] + for filename, lineno, name, line in self: + item = ' File "{}", line {}, in {}\n'.format(filename, lineno, name) + if line: + item = item + ' {}\n'.format(line.strip()) + result.append(item) + return result + + +class TracebackException: + """An exception ready for rendering. + + The traceback module captures enough attributes from the original exception + to this intermediary form to ensure that no references are held, while + still being able to fully print or format it. + + Use `from_exception` to create TracebackException instances from exception + objects, or the constructor to create TracebackException instances from + individual components. + + - :attr:`__cause__` A TracebackException of the original *__cause__*. + - :attr:`__context__` A TracebackException of the original *__context__*. + - :attr:`__suppress_context__` The *__suppress_context__* value from the + original exception. + - :attr:`stack` A `StackSummary` representing the traceback. + - :attr:`exc_type` The class of the original traceback. + - :attr:`filename` For syntax errors - the filename where the error + occured. + - :attr:`lineno` For syntax errors - the linenumber where the error + occured. + - :attr:`text` For syntax errors - the text where the error + occured. + - :attr:`offset` For syntax errors - the offset into the text where the + error occured. + - :attr:`msg` For syntax errors - the compiler error message. + """ + + def __init__(self, exc_type, exc_value, exc_traceback, limit=None, + lookup_lines=True, _seen=None): + # NB: we need to accept exc_traceback, exc_value, exc_traceback to + # permit backwards compat with the existing API, otherwise we + # need stub thunk objects just to glue it together. + # Handle loops in __cause__ or __context__. + if _seen is None: + _seen = set() + _seen.add(exc_value) + # Gracefully handle (the way Python 2.4 and earlier did) the case of + # being called with no type or value (None, None, None). + if (exc_value and exc_value.__cause__ is not None + and exc_value.__cause__ not in _seen): + cause = TracebackException( + type(exc_value.__cause__), + exc_value.__cause__, + exc_value.__cause__.__traceback__, + limit=limit, + lookup_lines=False, + _seen=_seen) + else: + cause = None + if (exc_value and exc_value.__context__ is not None + and exc_value.__context__ not in _seen): + context = TracebackException( + type(exc_value.__context__), + exc_value.__context__, + exc_value.__context__.__traceback__, + limit=limit, + lookup_lines=False, + _seen=_seen) + else: + context = None + self.__cause__ = cause + self.__context__ = context + self.__suppress_context__ = \ + exc_value.__suppress_context__ if exc_value else False + # TODO: locals. + self.stack = StackSummary.extract( + walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines) + self.exc_type = exc_type + # Capture now to permit freeing resources: only complication is in the + # unofficial API _format_final_exc_line + self._str = _some_str(exc_value) + if exc_type and issubclass(exc_type, SyntaxError): + # Handle SyntaxError's specially + self.filename = exc_value.filename + self.lineno = str(exc_value.lineno) + self.text = exc_value.text + self.offset = exc_value.offset + self.msg = exc_value.msg + if lookup_lines: + self._load_lines() + + @classmethod + def from_exception(self, exc, *args, **kwargs): + """Create a TracebackException from an exception.""" + return TracebackException( + type(exc), exc, exc.__traceback__, *args, **kwargs) + + def _load_lines(self): + """Private API. force all lines in the stack to be loaded.""" + for frame in self.stack: + frame.line + if self.__context__: + self.__context__._load_lines() + if self.__cause__: + self.__cause__._load_lines() + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __str__(self): + return self._str + + def format_exception_only(self): + """Format the exception part of the traceback. + + The return value is a generator of strings, each ending in a newline. + + Normally, the generator emits a single string; however, for + SyntaxError exceptions, it emites several lines that (when + printed) display detailed information about where the syntax + error occurred. + + The message indicating which exception occurred is always the last + string in the output. + """ + if self.exc_type is None: + yield _format_final_exc_line(None, self._str) + return + + stype = self.exc_type.__qualname__ + smod = self.exc_type.__module__ + if smod not in ("__main__", "builtins"): + stype = smod + '.' + stype + + if not issubclass(self.exc_type, SyntaxError): + yield _format_final_exc_line(stype, self._str) + return + + # It was a syntax error; show exactly where the problem was found. + filename = self.filename or "" + lineno = str(self.lineno) or '?' + yield ' File "{}", line {}\n'.format(filename, lineno) + + badline = self.text + offset = self.offset + if badline is not None: + yield ' {}\n'.format(badline.strip()) + if offset is not None: + caretspace = badline.rstrip('\n') + offset = min(len(caretspace), offset) - 1 + caretspace = caretspace[:offset].lstrip() + # non-space whitespace (likes tabs) must be kept for alignment + caretspace = ((c.isspace() and c or ' ') for c in caretspace) + yield ' {}^\n'.format(''.join(caretspace)) + msg = self.msg or "" + yield "{}: {}\n".format(stype, msg) + + def format(self, chain=True): + """Format the exception. + + If chain is not *True*, *__cause__* and *__context__* will not be formatted. + + The return value is a generator of strings, each ending in a newline and + some containing internal newlines. `print_exception` is a wrapper around + this method which just prints the lines to a file. + + The message indicating which exception occurred is always the last + string in the output. + """ + if chain: + if self.__cause__ is not None: + yield from self.__cause__.format(chain=chain) + yield _cause_message + elif (self.__context__ is not None and + not self.__suppress_context__): + yield from self.__context__.format(chain=chain) + yield _context_message + yield 'Traceback (most recent call last):\n' + yield from self.stack.format() + yield from self.format_exception_only() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -441,6 +441,13 @@ now clears its internal reference to the selector mapping to break a reference cycle. Initial patch written by Martin Richard. +- Issue #17911: Provide a way to seed the linecache for a PEP-302 module + without actually loading the code. + +- Issue #17911: Provide a new object API for traceback, including the ability + to not lookup lines at all until the traceback is actually rendered, without + any trace of the original objects being kept alive. + - Issue #19777: Provide a home() classmethod on Path objects. Contributed by Victor Salgado and Mayank Tripathi. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 00:26:21 2015 From: python-checkins at python.org (robert.collins) Date: Wed, 04 Mar 2015 23:26:21 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_brownbag_in_issue_1791?= =?utf-8?q?1_commit?= Message-ID: <20150304232611.19101.48538@psf.io> https://hg.python.org/cpython/rev/7cea10917f40 changeset: 94851:7cea10917f40 user: Robert Collins date: Thu Mar 05 12:26:00 2015 +1300 summary: Fix brownbag in issue 17911 commit files: Lib/test/test_traceback.py | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -591,10 +591,9 @@ except Exception as e: exc_info = sys.exc_info() self.expected_stack = traceback.StackSummary.extract( - traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False, - capture_locals=True) + traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False) self.exc = traceback.TracebackException.from_exception( - e, limit=1, lookup_lines=False, capture_locals=True) + e, limit=1, lookup_lines=False) expected_stack = self.expected_stack exc = self.exc self.assertEqual(None, exc.__cause__) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 02:42:09 2015 From: python-checkins at python.org (victor.stinner) Date: Thu, 05 Mar 2015 01:42:09 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjE5?= =?utf-8?q?=3A_Try_to_fix_test=5Fbroken=5Fpipe=5Fcleanup=28=29?= Message-ID: <20150305014209.9684.88684@psf.io> https://hg.python.org/cpython/rev/77a978716517 changeset: 94852:77a978716517 branch: 3.4 parent: 94847:01cf9ce75eda user: Victor Stinner date: Thu Mar 05 02:38:41 2015 +0100 summary: Issue #21619: Try to fix test_broken_pipe_cleanup() files: Lib/test/test_subprocess.py | 15 +++++++++------ 1 files changed, 9 insertions(+), 6 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 @@ -2523,13 +2523,16 @@ def test_broken_pipe_cleanup(self): """Broken pipe error should not prevent wait() (Issue 21619)""" - proc = subprocess.Popen([sys.executable, "-c", - "import sys;" - "sys.stdin.close();" - "sys.stdout.close();" # Signals that input pipe is closed - ], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + args = [sys.executable, "-c", + "import sys;" + "sys.stdin.close();" + "sys.stdout.close();"] # Signals that input pipe is closed + proc = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + bufsize=support.PIPE_MAX_SIZE*2) proc.stdout.read() # Make sure subprocess has closed its input - proc.stdin.write(b"buffered data") + proc.stdin.write(b"x" * support.PIPE_MAX_SIZE) self.assertIsNone(proc.returncode) self.assertRaises(OSError, proc.__exit__, None, None, None) self.assertEqual(0, proc.returncode) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 02:42:09 2015 From: python-checkins at python.org (victor.stinner) Date: Thu, 05 Mar 2015 01:42:09 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogTWVyZ2UgMy40ICh0ZXN0X3N1YnByb2Nlc3Mp?= Message-ID: <20150305014209.19091.33454@psf.io> https://hg.python.org/cpython/rev/36c62d9f8752 changeset: 94853:36c62d9f8752 parent: 94851:7cea10917f40 parent: 94852:77a978716517 user: Victor Stinner date: Thu Mar 05 02:40:17 2015 +0100 summary: Merge 3.4 (test_subprocess) files: Lib/test/test_subprocess.py | 15 +++++++++------ 1 files changed, 9 insertions(+), 6 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 @@ -2504,13 +2504,16 @@ def test_broken_pipe_cleanup(self): """Broken pipe error should not prevent wait() (Issue 21619)""" - proc = subprocess.Popen([sys.executable, "-c", - "import sys;" - "sys.stdin.close();" - "sys.stdout.close();" # Signals that input pipe is closed - ], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + args = [sys.executable, "-c", + "import sys;" + "sys.stdin.close();" + "sys.stdout.close();"] # Signals that input pipe is closed + proc = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + bufsize=support.PIPE_MAX_SIZE*2) proc.stdout.read() # Make sure subprocess has closed its input - proc.stdin.write(b"buffered data") + proc.stdin.write(b"x" * support.PIPE_MAX_SIZE) self.assertIsNone(proc.returncode) self.assertRaises(OSError, proc.__exit__, None, None, None) self.assertEqual(0, proc.returncode) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 03:45:21 2015 From: python-checkins at python.org (robert.collins) Date: Thu, 05 Mar 2015 02:45:21 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Remaining_fallout_from_179?= =?utf-8?q?11?= Message-ID: <20150305024521.19093.10360@psf.io> https://hg.python.org/cpython/rev/5f3dd0a2b1ab changeset: 94854:5f3dd0a2b1ab user: Robert Collins date: Thu Mar 05 15:45:01 2015 +1300 summary: Remaining fallout from 17911 The code module was using a private function from traceback in order to skip a frame - used the direct interface to do that instead, The decimal module suffered minor fallout from formatting changes ('None' as a value is now not printed by traceback, the same as None was not before). The cgitb module was passing a bogus exception type (type.__name__) into format_exception, which uncovered that format_exception and print_exception had been ignoring the etype for some time, so the compatibility thunk to the new code now does the same thing. files: Lib/_pydecimal.py | 2 +- Lib/code.py | 33 ++++++++------------------------- Lib/traceback.py | 10 ++++++++-- 3 files changed, 17 insertions(+), 28 deletions(-) diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -4108,7 +4108,7 @@ >>> context.create_decimal_from_float(3.1415926535897932) Traceback (most recent call last): ... - decimal.Inexact: None + decimal.Inexact """ d = Decimal.from_float(f) # An exact conversion diff --git a/Lib/code.py b/Lib/code.py --- a/Lib/code.py +++ b/Lib/code.py @@ -140,32 +140,15 @@ sys.last_type, sys.last_value, last_tb = ei = sys.exc_info() sys.last_traceback = last_tb try: - lines = [] - for value, tb in traceback._iter_chain(*ei[1:]): - if isinstance(value, str): - lines.append(value) - lines.append('\n') - continue - if tb: - tblist = traceback.extract_tb(tb) - if tb is last_tb: - # The last traceback includes the frame we - # exec'd in - del tblist[:1] - tblines = traceback.format_list(tblist) - if tblines: - lines.append("Traceback (most recent call last):\n") - lines.extend(tblines) - lines.extend(traceback.format_exception_only(type(value), - value)) + lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next) + if sys.excepthook is sys.__excepthook__: + self.write(''.join(lines)) + else: + # If someone has set sys.excepthook, we let that take precedence + # over self.write + sys.excepthook(ei[0], ei[1], last_tb) finally: - tblist = last_tb = ei = None - if sys.excepthook is sys.__excepthook__: - self.write(''.join(lines)) - else: - # If someone has set sys.excepthook, we let that take precedence - # over self.write - sys.excepthook(type, value, last_tb) + last_tb = ei = None def write(self, data): """Write a string. diff --git a/Lib/traceback.py b/Lib/traceback.py --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -89,10 +89,13 @@ occurred with a caret on the next line indicating the approximate position of the error. """ + # format_exception has ignored etype for some time, and code such as cgitb + # passes in bogus values as a result. For compatibility with such code we + # ignore it here (rather than in the new TracebackException API). if file is None: file = sys.stderr for line in TracebackException( - etype, value, tb, limit=limit).format(chain=chain): + type(value), value, tb, limit=limit).format(chain=chain): print(line, file=file, end="") @@ -105,8 +108,11 @@ these lines are concatenated and printed, exactly the same text is printed as does print_exception(). """ + # format_exception has ignored etype for some time, and code such as cgitb + # passes in bogus values as a result. For compatibility with such code we + # ignore it here (rather than in the new TracebackException API). return list(TracebackException( - etype, value, tb, limit=limit).format(chain=chain)) + type(value), value, tb, limit=limit).format(chain=chain)) def format_exception_only(etype, value): -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 04:11:53 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 03:11:53 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogZW5hYmxlIFg1MDlf?= =?utf-8?q?V=5FFLAG=5FTRUSTED=5FFIRST_when_possible_=28closes_=2323476=29?= Message-ID: <20150305031153.16671.42950@psf.io> https://hg.python.org/cpython/rev/37da00170836 changeset: 94856:37da00170836 branch: 2.7 parent: 94849:371cf371a6a1 user: Benjamin Peterson date: Wed Mar 04 22:11:12 2015 -0500 summary: enable X509_V_FLAG_TRUSTED_FIRST when possible (closes #23476) files: Misc/NEWS | 3 +++ Modules/_ssl.c | 9 +++++++++ 2 files changed, 12 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,9 @@ Library ------- +- Issue #23476: In the ssl module, enable OpenSSL's X509_V_FLAG_TRUSTED_FIRST + flag on certificate stores when it is available. + - Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the SSL layer but the underlying connection hasn't been closed. diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2072,6 +2072,15 @@ sizeof(SID_CTX)); #undef SID_CTX +#ifdef X509_V_FLAG_TRUSTED_FIRST + { + /* Improve trust chain building when cross-signed intermediate + certificates are present. See https://bugs.python.org/issue23476. */ + X509_STORE *store = SSL_CTX_get_cert_store(self->ctx); + X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); + } +#endif + return (PyObject *)self; } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 04:11:53 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 03:11:53 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogZW5hYmxlIFg1MDlf?= =?utf-8?q?V=5FFLAG=5FTRUSTED=5FFIRST_when_possible_=28closes_=2323476=29?= Message-ID: <20150305031153.9664.10331@psf.io> https://hg.python.org/cpython/rev/7f64437a707f changeset: 94855:7f64437a707f branch: 3.4 parent: 94852:77a978716517 user: Benjamin Peterson date: Wed Mar 04 22:11:12 2015 -0500 summary: enable X509_V_FLAG_TRUSTED_FIRST when possible (closes #23476) files: Misc/NEWS | 3 +++ Modules/_ssl.c | 9 +++++++++ 2 files changed, 12 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,9 @@ Library ------- +- Issue #23476: In the ssl module, enable OpenSSL's X509_V_FLAG_TRUSTED_FIRST + flag on certificate stores when it is available. + - Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the SSL layer but the underlying connection hasn't been closed. diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2063,6 +2063,15 @@ sizeof(SID_CTX)); #undef SID_CTX +#ifdef X509_V_FLAG_TRUSTED_FIRST + { + /* Improve trust chain building when cross-signed intermediate + certificates are present. See https://bugs.python.org/issue23476. */ + X509_STORE *store = SSL_CTX_get_cert_store(self->ctx); + X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); + } +#endif + return (PyObject *)self; } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 04:11:53 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 03:11:53 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjM0NzYp?= Message-ID: <20150305031153.19107.73098@psf.io> https://hg.python.org/cpython/rev/442e2c357979 changeset: 94857:442e2c357979 parent: 94854:5f3dd0a2b1ab parent: 94855:7f64437a707f user: Benjamin Peterson date: Wed Mar 04 22:11:48 2015 -0500 summary: merge 3.4 (#23476) files: Misc/NEWS | 3 +++ Modules/_ssl.c | 9 +++++++++ 2 files changed, 12 insertions(+), 0 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,9 @@ Library ------- +- Issue #23476: In the ssl module, enable OpenSSL's X509_V_FLAG_TRUSTED_FIRST + flag on certificate stores when it is available. + - Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the SSL layer but the underlying connection hasn't been closed. diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2199,6 +2199,15 @@ sizeof(SID_CTX)); #undef SID_CTX +#ifdef X509_V_FLAG_TRUSTED_FIRST + { + /* Improve trust chain building when cross-signed intermediate + certificates are present. See https://bugs.python.org/issue23476. */ + X509_STORE *store = SSL_CTX_get_cert_store(self->ctx); + X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); + } +#endif + return (PyObject *)self; } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 04:50:30 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 03:50:30 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogZXhwb3NlIFg1MDlf?= =?utf-8?q?V=5FFLAG=5FTRUSTED=5FFIRST?= Message-ID: <20150305035030.16651.30576@psf.io> https://hg.python.org/cpython/rev/bb6fb47e0141 changeset: 94858:bb6fb47e0141 branch: 3.4 parent: 94855:7f64437a707f user: Benjamin Peterson date: Wed Mar 04 22:49:41 2015 -0500 summary: expose X509_V_FLAG_TRUSTED_FIRST files: Doc/library/ssl.rst | 14 +++++++++++--- Lib/test/test_ssl.py | 5 +++-- Modules/_ssl.c | 4 ++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -499,9 +499,9 @@ .. data:: VERIFY_DEFAULT - Possible value for :attr:`SSLContext.verify_flags`. In this mode, - certificate revocation lists (CRLs) are not checked. By default OpenSSL - does neither require nor verify CRLs. + Possible value for :attr:`SSLContext.verify_flags`. In this mode, certificate + revocation lists (CRLs) are not checked. By default OpenSSL does neither + require nor verify CRLs. .. versionadded:: 3.4 @@ -529,6 +529,14 @@ .. versionadded:: 3.4 +.. data:: VERIFY_X509_TRUSTED_FIRST + + Possible value for :attr:`SSLContext.verify_flags`. It instructs OpenSSL to + prefer trusted certificates when building the trust chain to validate a + certificate. This flag is enabled by default. + + .. versionadded:: 3.4.5 + .. data:: PROTOCOL_SSLv23 Selects the highest protocol version that both the client and server support. 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 @@ -710,8 +710,9 @@ "verify_flags need OpenSSL > 0.9.8") def test_verify_flags(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - # default value by OpenSSL - self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT) + # default value + tf = getattr(ssl, "VERIFY_X509_TRUSTED_FIRST", 0) + self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT | tf) ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF self.assertEqual(ctx.verify_flags, ssl.VERIFY_CRL_CHECK_LEAF) ctx.verify_flags = ssl.VERIFY_CRL_CHECK_CHAIN diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4004,6 +4004,10 @@ X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); PyModule_AddIntConstant(m, "VERIFY_X509_STRICT", X509_V_FLAG_X509_STRICT); +#ifdef X509_V_FLAG_TRUSTED_FIRST + PyModule_AddIntConstant(m, "VERIFY_X509_TRUSTED_FIRST", + X509_V_FLAG_TRUSTED_FIRST); +#endif /* Alert Descriptions from ssl.h */ /* note RESERVED constants no longer intended for use have been removed */ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 04:50:30 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 03:50:30 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogZXhwb3NlIFg1MDlf?= =?utf-8?q?V=5FFLAG=5FTRUSTED=5FFIRST?= Message-ID: <20150305035030.19087.55652@psf.io> https://hg.python.org/cpython/rev/b2ae7818ca9a changeset: 94859:b2ae7818ca9a branch: 2.7 parent: 94856:37da00170836 user: Benjamin Peterson date: Wed Mar 04 22:49:41 2015 -0500 summary: expose X509_V_FLAG_TRUSTED_FIRST files: Doc/library/ssl.rst | 14 +++++++++++--- Lib/test/test_ssl.py | 5 +++-- Modules/_ssl.c | 4 ++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -482,9 +482,9 @@ .. data:: VERIFY_DEFAULT - Possible value for :attr:`SSLContext.verify_flags`. In this mode, - certificate revocation lists (CRLs) are not checked. By default OpenSSL - does neither require nor verify CRLs. + Possible value for :attr:`SSLContext.verify_flags`. In this mode, certificate + revocation lists (CRLs) are not checked. By default OpenSSL does neither + require nor verify CRLs. .. versionadded:: 2.7.9 @@ -512,6 +512,14 @@ .. versionadded:: 2.7.9 +.. data:: VERIFY_X509_TRUSTED_FIRST + + Possible value for :attr:`SSLContext.verify_flags`. It instructs OpenSSL to + prefer trusted certificates when building the trust chain to validate a + certificate. This flag is enabled by default. + + .. versionadded:: 2.7.10 + .. data:: PROTOCOL_SSLv23 Selects the highest protocol version that both the client and server support. 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 @@ -749,8 +749,9 @@ "verify_flags need OpenSSL > 0.9.8") def test_verify_flags(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - # default value by OpenSSL - self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT) + # default value + tf = getattr(ssl, "VERIFY_X509_TRUSTED_FIRST", 0) + self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT | tf) ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF self.assertEqual(ctx.verify_flags, ssl.VERIFY_CRL_CHECK_LEAF) ctx.verify_flags = ssl.VERIFY_CRL_CHECK_CHAIN diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4052,6 +4052,10 @@ X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); PyModule_AddIntConstant(m, "VERIFY_X509_STRICT", X509_V_FLAG_X509_STRICT); +#ifdef X509_V_FLAG_TRUSTED_FIRST + PyModule_AddIntConstant(m, "VERIFY_X509_TRUSTED_FIRST", + X509_V_FLAG_TRUSTED_FIRST); +#endif /* Alert Descriptions from ssl.h */ /* note RESERVED constants no longer intended for use have been removed */ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 04:50:30 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 03:50:30 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <20150305035030.19087.94970@psf.io> https://hg.python.org/cpython/rev/135e16acdec7 changeset: 94860:135e16acdec7 parent: 94857:442e2c357979 parent: 94858:bb6fb47e0141 user: Benjamin Peterson date: Wed Mar 04 22:50:25 2015 -0500 summary: merge 3.4 files: Doc/library/ssl.rst | 14 +++++++++++--- Lib/test/test_ssl.py | 5 +++-- Modules/_ssl.c | 4 ++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -520,9 +520,9 @@ .. data:: VERIFY_DEFAULT - Possible value for :attr:`SSLContext.verify_flags`. In this mode, - certificate revocation lists (CRLs) are not checked. By default OpenSSL - does neither require nor verify CRLs. + Possible value for :attr:`SSLContext.verify_flags`. In this mode, certificate + revocation lists (CRLs) are not checked. By default OpenSSL does neither + require nor verify CRLs. .. versionadded:: 3.4 @@ -550,6 +550,14 @@ .. versionadded:: 3.4 +.. data:: VERIFY_X509_TRUSTED_FIRST + + Possible value for :attr:`SSLContext.verify_flags`. It instructs OpenSSL to + prefer trusted certificates when building the trust chain to validate a + certificate. This flag is enabled by default. + + .. versionadded:: 3.4.5 + .. data:: PROTOCOL_SSLv23 Selects the highest protocol version that both the client and server support. 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 @@ -818,8 +818,9 @@ "verify_flags need OpenSSL > 0.9.8") def test_verify_flags(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - # default value by OpenSSL - self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT) + # default value + tf = getattr(ssl, "VERIFY_X509_TRUSTED_FIRST", 0) + self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT | tf) ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF self.assertEqual(ctx.verify_flags, ssl.VERIFY_CRL_CHECK_LEAF) ctx.verify_flags = ssl.VERIFY_CRL_CHECK_CHAIN diff --git a/Modules/_ssl.c b/Modules/_ssl.c --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4458,6 +4458,10 @@ X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); PyModule_AddIntConstant(m, "VERIFY_X509_STRICT", X509_V_FLAG_X509_STRICT); +#ifdef X509_V_FLAG_TRUSTED_FIRST + PyModule_AddIntConstant(m, "VERIFY_X509_TRUSTED_FIRST", + X509_V_FLAG_TRUSTED_FIRST); +#endif /* Alert Descriptions from ssl.h */ /* note RESERVED constants no longer intended for use have been removed */ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 05:20:28 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 04:20:28 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogdXNlIF9pbXBvcnRf?= =?utf-8?q?symbols_to_import_VERIFY=5F*_constants?= Message-ID: <20150305042028.16669.51377@psf.io> https://hg.python.org/cpython/rev/9ca2c98e4395 changeset: 94862:9ca2c98e4395 branch: 3.4 user: Benjamin Peterson date: Wed Mar 04 23:18:57 2015 -0500 summary: use _import_symbols to import VERIFY_* constants files: Lib/ssl.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Lib/ssl.py b/Lib/ssl.py --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -103,8 +103,6 @@ SSLSyscallError, SSLEOFError, ) from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED -from _ssl import (VERIFY_DEFAULT, VERIFY_CRL_CHECK_LEAF, VERIFY_CRL_CHECK_CHAIN, - VERIFY_X509_STRICT) from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes try: @@ -122,6 +120,7 @@ _import_symbols('ALERT_DESCRIPTION_') _import_symbols('SSL_ERROR_') _import_symbols('PROTOCOL_') +_import_symbols('VERIFY_') from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 05:20:28 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 04:20:28 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYWRqdXN0IHRlc3Rf?= =?utf-8?q?crl=5Fcheck_for_trusted_first_being_default?= Message-ID: <20150305042028.16667.13238@psf.io> https://hg.python.org/cpython/rev/a845a596702d changeset: 94861:a845a596702d branch: 3.4 parent: 94858:bb6fb47e0141 user: Benjamin Peterson date: Wed Mar 04 23:18:48 2015 -0500 summary: adjust test_crl_check for trusted first being default files: Lib/test/test_ssl.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) 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 @@ -2007,7 +2007,8 @@ context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) context.verify_mode = ssl.CERT_REQUIRED context.load_verify_locations(SIGNING_CA) - self.assertEqual(context.verify_flags, ssl.VERIFY_DEFAULT) + tf = getattr(ssl, "VERIFY_X509_TRUSTED_FIRST", 0) + self.assertEqual(context.verify_flags, ssl.VERIFY_DEFAULT | tf) # VERIFY_DEFAULT should pass server = ThreadedEchoServer(context=server_context, chatty=True) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 05:20:28 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 04:20:28 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogYWRqdXN0IHRlc3Rf?= =?utf-8?q?crl=5Fcheck_for_trusted_first_being_default?= Message-ID: <20150305042028.9674.79302@psf.io> https://hg.python.org/cpython/rev/047413f86eaa changeset: 94863:047413f86eaa branch: 2.7 parent: 94859:b2ae7818ca9a user: Benjamin Peterson date: Wed Mar 04 23:18:48 2015 -0500 summary: adjust test_crl_check for trusted first being default files: Lib/test/test_ssl.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) 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 @@ -2065,7 +2065,8 @@ context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) context.verify_mode = ssl.CERT_REQUIRED context.load_verify_locations(SIGNING_CA) - self.assertEqual(context.verify_flags, ssl.VERIFY_DEFAULT) + tf = getattr(ssl, "VERIFY_X509_TRUSTED_FIRST", 0) + self.assertEqual(context.verify_flags, ssl.VERIFY_DEFAULT | tf) # VERIFY_DEFAULT should pass server = ThreadedEchoServer(context=server_context, chatty=True) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 05:20:28 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 04:20:28 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogdXNlIF9pbXBvcnRf?= =?utf-8?q?symbols_to_import_VERIFY=5F*_constants?= Message-ID: <20150305042028.16671.88574@psf.io> https://hg.python.org/cpython/rev/ec9bffc35cad changeset: 94864:ec9bffc35cad branch: 2.7 user: Benjamin Peterson date: Wed Mar 04 23:18:57 2015 -0500 summary: use _import_symbols to import VERIFY_* constants files: Lib/ssl.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Lib/ssl.py b/Lib/ssl.py --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -103,8 +103,6 @@ SSLSyscallError, SSLEOFError, ) from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED -from _ssl import (VERIFY_DEFAULT, VERIFY_CRL_CHECK_LEAF, VERIFY_CRL_CHECK_CHAIN, - VERIFY_X509_STRICT) from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj from _ssl import RAND_status, RAND_add try: @@ -122,6 +120,7 @@ _import_symbols('ALERT_DESCRIPTION_') _import_symbols('SSL_ERROR_') _import_symbols('PROTOCOL_') +_import_symbols('VERIFY_') from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 05:20:28 2015 From: python-checkins at python.org (benjamin.peterson) Date: Thu, 05 Mar 2015 04:20:28 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <20150305042028.16671.65234@psf.io> https://hg.python.org/cpython/rev/e9da85cb3dc5 changeset: 94865:e9da85cb3dc5 parent: 94860:135e16acdec7 parent: 94862:9ca2c98e4395 user: Benjamin Peterson date: Wed Mar 04 23:20:23 2015 -0500 summary: merge 3.4 files: Lib/ssl.py | 3 +-- Lib/test/test_ssl.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/ssl.py b/Lib/ssl.py --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -104,8 +104,6 @@ SSLSyscallError, SSLEOFError, ) from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED -from _ssl import (VERIFY_DEFAULT, VERIFY_CRL_CHECK_LEAF, VERIFY_CRL_CHECK_CHAIN, - VERIFY_X509_STRICT) from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes try: @@ -122,6 +120,7 @@ _import_symbols('OP_') _import_symbols('ALERT_DESCRIPTION_') _import_symbols('SSL_ERROR_') +_import_symbols('VERIFY_') from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN 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 @@ -2284,7 +2284,8 @@ context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) context.verify_mode = ssl.CERT_REQUIRED context.load_verify_locations(SIGNING_CA) - self.assertEqual(context.verify_flags, ssl.VERIFY_DEFAULT) + tf = getattr(ssl, "VERIFY_X509_TRUSTED_FIRST", 0) + self.assertEqual(context.verify_flags, ssl.VERIFY_DEFAULT | tf) # VERIFY_DEFAULT should pass server = ThreadedEchoServer(context=server_context, chatty=True) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 05:30:55 2015 From: python-checkins at python.org (steve.dower) Date: Thu, 05 Mar 2015 04:30:55 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Add_scripts_for_uploading_?= =?utf-8?q?Windows_builds_to_a_Linux_server_using_pscp?= Message-ID: <20150305043054.7559.31155@psf.io> https://hg.python.org/cpython/rev/4953c86af1bf changeset: 94866:4953c86af1bf user: Steve Dower date: Wed Mar 04 20:27:03 2015 -0800 summary: Add scripts for uploading Windows builds to a Linux server using pscp files: Tools/msi/buildrelease.bat | 1 + Tools/msi/uploadrelease.bat | 46 ++++++++++++++++++ Tools/msi/uploadrelease.proj | 61 ++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 0 deletions(-) diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -140,6 +140,7 @@ copy /Y "%BUILD%en-us\*.cab" "%OUTDIR%\%OUTDIR_PLAT%" copy /Y "%BUILD%en-us\*.exe" "%OUTDIR%\%OUTDIR_PLAT%" copy /Y "%BUILD%en-us\*.msi" "%OUTDIR%\%OUTDIR_PLAT%" + copy /Y "%BUILD%en-us\*.msu" "%OUTDIR%\%OUTDIR_PLAT%" ) exit /B 0 diff --git a/Tools/msi/uploadrelease.bat b/Tools/msi/uploadrelease.bat new file mode 100644 --- /dev/null +++ b/Tools/msi/uploadrelease.bat @@ -0,0 +1,46 @@ + at setlocal + at echo off + +set D=%~dp0 +set PCBUILD=%D%..\..\PCBuild\ + +set HOST= +set USER= +set TARGET= +set DRYRUN=false + +:CheckOpts +if "%1" EQU "-h" goto Help +if "%1" EQU "-o" (set HOST=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--host" (set HOST=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "-u" (set USER=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--user" (set USER=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "-t" (set TARGET=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--target" (set TARGET=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--dry-run" (set DRYRUN=true) && shift && goto CheckOpts + +if not defined PLINK where plink > "%TEMP%\plink.loc" 2> nul && set /P PLINK= < "%TEMP%\plink.loc" & del "%TEMP%\plink.loc" +if not defined PLINK where /R "%ProgramFiles(x86)%" plink > "%TEMP%\plink.loc" 2> nul && set /P PLINK= < "%TEMP%\plink.loc" & del "%TEMP%\plink.loc" +if not defined PLINK echo Cannot locate plink.exe & exit /B 1 +echo Found plink.exe at %PLINK% + +if not defined PSCP where pscp > "%TEMP%\pscp.loc" 2> nul && set /P pscp= < "%TEMP%\pscp.loc" & del "%TEMP%\pscp.loc" +if not defined PSCP where /R "%ProgramFiles(x86)%" pscp > "%TEMP%\pscp.loc" 2> nul && set /P pscp= < "%TEMP%\pscp.loc" & del "%TEMP%\pscp.loc" +if not defined PSCP echo Cannot locate pscp.exe & exit /B 1 +echo Found pscp.exe at %PSCP% + +call "%PCBUILD%env.bat" > nul 2> nul +msbuild /v:m /nologo uploadrelease.proj /t:Upload /p:Platform=x86 +msbuild /v:m /nologo uploadrelease.proj /t:Upload /p:Platform=x64 /p:IncludeDoc=false + +exit /B 0 + +:Help +echo uploadrelease.bat --host HOST --user USERNAME [--target TARGET] [--dry-run] [-h] +echo. +echo --host (-o) Specify the upload host (required) +echo --user (-u) Specify the user on the host (required) +echo --target (-t) Specify the target directory on the host +echo --dry-run Display commands and filenames without executing them +echo -h Display this help information +echo. diff --git a/Tools/msi/uploadrelease.proj b/Tools/msi/uploadrelease.proj new file mode 100644 --- /dev/null +++ b/Tools/msi/uploadrelease.proj @@ -0,0 +1,61 @@ + + + + {2D69F2AB-D5D0-4344-84B5-EF6DB34A9BC9} + python + + + $(TARGET) + /srv/www.python.org/ftp/python + true + false + + + + + + + $(DownloadUrlBase.TrimEnd(`/`))/$(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber) + $(DownloadUrl.TrimEnd(`/`)) + + + + + $(MSITarget) + + + $(EXETarget) + + + $(EXETarget) + + + + + + + + + + + + + + + + + + + + + + + + -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 08:29:15 2015 From: python-checkins at python.org (robert.collins) Date: Thu, 05 Mar 2015 07:29:15 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2322936=3A_Make_it_?= =?utf-8?q?possible_to_show_local_variables_in_tracebacks=2E?= Message-ID: <20150305072904.9670.49035@psf.io> https://hg.python.org/cpython/rev/50741316dd3a changeset: 94867:50741316dd3a user: Robert Collins date: Thu Mar 05 20:28:52 2015 +1300 summary: Issue #22936: Make it possible to show local variables in tracebacks. files: Doc/library/traceback.rst | 28 ++++++--- Lib/test/test_traceback.py | 68 ++++++++++++++++++++++--- Lib/traceback.py | 48 ++++++++++++------ Misc/NEWS | 2 + 4 files changed, 112 insertions(+), 34 deletions(-) diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -159,17 +159,21 @@ :class:`.TracebackException` objects are created from actual exceptions to capture data for later printing in a lightweight fashion. -.. class:: TracebackException(exc_type, exc_value, exc_traceback, limit=None, lookup_lines=True) +.. class:: TracebackException(exc_type, exc_value, exc_traceback, *, limit=None, lookup_lines=True, capture_locals=False) - Capture an exception for later rendering. limit, lookup_lines are as for - the :class:`.StackSummary` class. + Capture an exception for later rendering. limit, lookup_lines and + capture_locals=False are as for the :class:`.StackSummary` class. + + Note that when locals are captured, they are also shown in the traceback. .. versionadded:: 3.5 -.. classmethod:: `.from_exception`(exc, limit=None, lookup_lines=True) +.. classmethod:: `.from_exception`(exc, *, limit=None, lookup_lines=True, capture_locals=False) - Capture an exception for later rendering. limit and lookup_lines - are as for the :class:`.StackSummary` class. + Capture an exception for later rendering. limit, lookup_lines and + capture_locals=False are as for the :class:`.StackSummary` class. + + Note that when locals are captured, they are also shown in the traceback. .. versionadded:: 3.5 @@ -190,7 +194,7 @@ error occured. .. attribute:: `.msg` For syntax errors - the compiler error message. -.. method:: TracebackException.format(chain=True) +.. method:: TracebackException.format(*, chain=True) Format the exception. @@ -227,7 +231,7 @@ :class:`.StackSummary` objects represent a call stack ready for formatting. -.. classmethod:: StackSummary.extract(frame_gen, limit=None, lookup_lines=True) +.. classmethod:: StackSummary.extract(frame_gen, *, limit=None, lookup_lines=True, capture_locals=False) Construct a StackSummary object from a frame generator (such as is returned by `walk_stack` or `walk_tb`. @@ -236,6 +240,8 @@ If lookup_lines is False, the returned FrameSummary objects will not have read their lines in yet, making the cost of creating the StackSummary cheaper (which may be valuable if it may not actually get formatted). + If capture_locals is True the local variables in each *FrameSummary* are + captured as object representations. .. versionadded:: 3.5 @@ -258,8 +264,10 @@ or printed. It may optionally have a stringified version of the frames locals included in it. If *lookup_line* is False, the source code is not looked up until the FrameSummary has the :attr:`line` attribute accessed (which - also happens when casting it to a tuple). Line may be directly provided, and - will prevent line lookups happening at all. + also happens when casting it to a tuple). *line* may be directly provided, and + will prevent line lookups happening at all. *locals* is an optional local variable + dictionary, and if supplied the variable representations are stored in the summary + for later display. .. _traceback-example: diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -15,7 +15,7 @@ test_code = namedtuple('code', ['co_filename', 'co_name']) -test_frame = namedtuple('frame', ['f_code', 'f_globals']) +test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals']) test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next']) @@ -535,7 +535,7 @@ linecache.clearcache() linecache.updatecache('/foo.py', globals()) c = test_code('/foo.py', 'method') - f = test_frame(c, None) + f = test_frame(c, None, None) s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True) linecache.clearcache() self.assertEqual(s[0].line, "import sys") @@ -543,14 +543,14 @@ def test_extract_stackup_deferred_lookup_lines(self): linecache.clearcache() c = test_code('/foo.py', 'method') - f = test_frame(c, None) + f = test_frame(c, None, None) s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False) self.assertEqual({}, linecache.cache) linecache.updatecache('/foo.py', globals()) self.assertEqual(s[0].line, "import sys") def test_from_list(self): - s = traceback.StackSummary([('foo.py', 1, 'fred', 'line')]) + s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) self.assertEqual( [' File "foo.py", line 1, in fred\n line\n'], s.format()) @@ -558,11 +558,42 @@ def test_format_smoke(self): # For detailed tests see the format_list tests, which consume the same # code. - s = traceback.StackSummary([('foo.py', 1, 'fred', 'line')]) + s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) self.assertEqual( [' File "foo.py", line 1, in fred\n line\n'], s.format()) + def test_locals(self): + linecache.updatecache('/foo.py', globals()) + c = test_code('/foo.py', 'method') + f = test_frame(c, globals(), {'something': 1}) + s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True) + self.assertEqual(s[0].locals, {'something': '1'}) + + def test_no_locals(self): + linecache.updatecache('/foo.py', globals()) + c = test_code('/foo.py', 'method') + f = test_frame(c, globals(), {'something': 1}) + s = traceback.StackSummary.extract(iter([(f, 6)])) + self.assertEqual(s[0].locals, None) + + def test_format_locals(self): + def some_inner(k, v): + a = 1 + b = 2 + return traceback.StackSummary.extract( + traceback.walk_stack(None), capture_locals=True, limit=1) + s = some_inner(3, 4) + self.assertEqual( + [' File "' + __file__ + '", line 585, ' + 'in some_inner\n' + ' traceback.walk_stack(None), capture_locals=True, limit=1)\n' + ' a = 1\n' + ' b = 2\n' + ' k = 3\n' + ' v = 4\n' + ], s.format()) + class TestTracebackException(unittest.TestCase): @@ -591,9 +622,10 @@ except Exception as e: exc_info = sys.exc_info() self.expected_stack = traceback.StackSummary.extract( - traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False) + traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False, + capture_locals=True) self.exc = traceback.TracebackException.from_exception( - e, limit=1, lookup_lines=False) + e, limit=1, lookup_lines=False, capture_locals=True) expected_stack = self.expected_stack exc = self.exc self.assertEqual(None, exc.__cause__) @@ -664,13 +696,33 @@ linecache.clearcache() e = Exception("uh oh") c = test_code('/foo.py', 'method') - f = test_frame(c, None) + f = test_frame(c, None, None) tb = test_tb(f, 6, None) exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False) self.assertEqual({}, linecache.cache) linecache.updatecache('/foo.py', globals()) self.assertEqual(exc.stack[0].line, "import sys") + def test_locals(self): + linecache.updatecache('/foo.py', globals()) + e = Exception("uh oh") + c = test_code('/foo.py', 'method') + f = test_frame(c, globals(), {'something': 1, 'other': 'string'}) + tb = test_tb(f, 6, None) + exc = traceback.TracebackException( + Exception, e, tb, capture_locals=True) + self.assertEqual( + exc.stack[0].locals, {'something': '1', 'other': "'string'"}) + + def test_no_locals(self): + linecache.updatecache('/foo.py', globals()) + e = Exception("uh oh") + c = test_code('/foo.py', 'method') + f = test_frame(c, globals(), {'something': 1}) + tb = test_tb(f, 6, None) + exc = traceback.TracebackException(Exception, e, tb) + self.assertEqual(exc.stack[0].locals, None) + def test_main(): run_unittest(__name__) diff --git a/Lib/traceback.py b/Lib/traceback.py --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -223,19 +223,19 @@ - :attr:`line` The text from the linecache module for the of code that was running when the frame was captured. - :attr:`locals` Either None if locals were not supplied, or a dict - mapping the name to the str() of the variable. + mapping the name to the repr() of the variable. """ __slots__ = ('filename', 'lineno', 'name', '_line', 'locals') - def __init__(self, filename, lineno, name, lookup_line=True, locals=None, - line=None): + def __init__(self, filename, lineno, name, *, lookup_line=True, + locals=None, line=None): """Construct a FrameSummary. :param lookup_line: If True, `linecache` is consulted for the source code line. Otherwise, the line will be looked up when first needed. :param locals: If supplied the frame locals, which will be captured as - strings. + object representations. :param line: If provided, use this instead of looking up the line in the linecache. """ @@ -246,7 +246,7 @@ if lookup_line: self.line self.locals = \ - dict((k, str(v)) for k, v in locals.items()) if locals else None + dict((k, repr(v)) for k, v in locals.items()) if locals else None def __eq__(self, other): return (self.filename == other.filename and @@ -299,7 +299,8 @@ """A stack of frames.""" @classmethod - def extract(klass, frame_gen, limit=None, lookup_lines=True): + def extract(klass, frame_gen, *, limit=None, lookup_lines=True, + capture_locals=False): """Create a StackSummary from a traceback or stack object. :param frame_gen: A generator that yields (frame, lineno) tuples to @@ -308,6 +309,8 @@ include. :param lookup_lines: If True, lookup lines for each frame immediately, otherwise lookup is deferred until the frame is rendered. + :param capture_locals: If True, the local variables from each frame will + be captured as object representations into the FrameSummary. """ if limit is None: limit = getattr(sys, 'tracebacklimit', None) @@ -324,7 +327,12 @@ fnames.add(filename) linecache.lazycache(filename, f.f_globals) # Must defer line lookups until we have called checkcache. - result.append(FrameSummary(filename, lineno, name, lookup_line=False)) + if capture_locals: + f_locals = f.f_locals + else: + f_locals = None + result.append(FrameSummary( + filename, lineno, name, lookup_line=False, locals=f_locals)) for filename in fnames: linecache.checkcache(filename) # If immediate lookup was desired, trigger lookups now. @@ -356,11 +364,16 @@ newlines as well, for those items with source text lines. """ result = [] - for filename, lineno, name, line in self: - item = ' File "{}", line {}, in {}\n'.format(filename, lineno, name) - if line: - item = item + ' {}\n'.format(line.strip()) - result.append(item) + for frame in self: + row = [] + row.append(' File "{}", line {}, in {}\n'.format( + frame.filename, frame.lineno, frame.name)) + if frame.line: + row.append(' {}\n'.format(frame.line.strip())) + if frame.locals: + for name, value in sorted(frame.locals.items()): + row.append(' {name} = {value}\n'.format(name=name, value=value)) + result.append(''.join(row)) return result @@ -392,8 +405,8 @@ - :attr:`msg` For syntax errors - the compiler error message. """ - def __init__(self, exc_type, exc_value, exc_traceback, limit=None, - lookup_lines=True, _seen=None): + def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, + lookup_lines=True, capture_locals=False, _seen=None): # NB: we need to accept exc_traceback, exc_value, exc_traceback to # permit backwards compat with the existing API, otherwise we # need stub thunk objects just to glue it together. @@ -411,6 +424,7 @@ exc_value.__cause__.__traceback__, limit=limit, lookup_lines=False, + capture_locals=capture_locals, _seen=_seen) else: cause = None @@ -422,6 +436,7 @@ exc_value.__context__.__traceback__, limit=limit, lookup_lines=False, + capture_locals=capture_locals, _seen=_seen) else: context = None @@ -431,7 +446,8 @@ exc_value.__suppress_context__ if exc_value else False # TODO: locals. self.stack = StackSummary.extract( - walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines) + walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines, + capture_locals=capture_locals) self.exc_type = exc_type # Capture now to permit freeing resources: only complication is in the # unofficial API _format_final_exc_line @@ -512,7 +528,7 @@ msg = self.msg or "" yield "{}: {}\n".format(stype, msg) - def format(self, chain=True): + def format(self, *, chain=True): """Format the exception. If chain is not *True*, *__cause__* and *__context__* will not be formatted. diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -39,6 +39,8 @@ - Issue #21619: Popen objects no longer leave a zombie after exit in the with statement if the pipe was broken. Patch by Martin Panter. +- Issue #22936: Make it possible to show local variables in tracebacks. + - Issue #15955: Add an option to limit the output size in bz2.decompress(). Patch by Nikolaus Rath. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 14:04:47 2015 From: python-checkins at python.org (victor.stinner) Date: Thu, 05 Mar 2015 13:04:47 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_=22GCC_diagnostic=22_i?= =?utf-8?q?n_socketmodule=2Ec?= Message-ID: <20150305130446.16667.85182@psf.io> https://hg.python.org/cpython/rev/cabd257b1070 changeset: 94868:cabd257b1070 user: Victor Stinner date: Thu Mar 05 14:04:03 2015 +0100 summary: Fix "GCC diagnostic" in socketmodule.c Fix regression of changeset 7c6e3358221a on GCC < 4.4. The _socket module cannot be compiled on "x86 FreeBSD 7.2 3.x" buildbot anymore. files: Modules/socketmodule.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1966,13 +1966,13 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wtautological-compare" #endif - #ifdef __GNUC__ + #if defined(__GNUC__) && ((__GNUC__ == 4) || ((__GNUC__ > 4) && (__GNUC_MINOR__ > 5))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" #endif if (msg->msg_controllen < 0) return 0; - #ifdef __GNUC__ + #if defined(__GNUC__) && ((__GNUC__ == 4) || ((__GNUC__ > 4) && (__GNUC_MINOR__ > 5))) #pragma GCC diagnostic pop #endif #ifdef __clang__ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 22:43:06 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 05 Mar 2015 21:43:06 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fixed_GCC_version_testing?= =?utf-8?q?=2E?= Message-ID: <20150305214303.19099.49065@psf.io> https://hg.python.org/cpython/rev/2d12a0e44da0 changeset: 94869:2d12a0e44da0 user: Serhiy Storchaka date: Thu Mar 05 23:42:24 2015 +0200 summary: Fixed GCC version testing. files: Modules/socketmodule.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1966,13 +1966,13 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wtautological-compare" #endif - #if defined(__GNUC__) && ((__GNUC__ == 4) || ((__GNUC__ > 4) && (__GNUC_MINOR__ > 5))) + #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" #endif if (msg->msg_controllen < 0) return 0; - #if defined(__GNUC__) && ((__GNUC__ == 4) || ((__GNUC__ > 4) && (__GNUC_MINOR__ > 5))) + #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))) #pragma GCC diagnostic pop #endif #ifdef __clang__ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 5 23:35:39 2015 From: python-checkins at python.org (robert.collins) Date: Thu, 05 Mar 2015 22:35:39 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_unittest=3A_Trivial_typo_f?= =?utf-8?b?aXgu?= Message-ID: <20150305223537.1775.76077@psf.io> https://hg.python.org/cpython/rev/1821bc784a23 changeset: 94870:1821bc784a23 user: Robert Collins date: Fri Mar 06 11:35:29 2015 +1300 summary: unittest: Trivial typo fix. files: Lib/unittest/main.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py --- a/Lib/unittest/main.py +++ b/Lib/unittest/main.py @@ -74,7 +74,7 @@ self.verbosity = verbosity self.buffer = buffer if warnings is None and not sys.warnoptions: - # even if DreprecationWarnings are ignored by default + # even if DeprecationWarnings are ignored by default # print them anyway unless other warnings settings are # specified by the warnings arg or the -W python flag self.warnings = 'default' -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Fri Mar 6 00:05:34 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 06 Mar 2015 00:05:34 +0100 Subject: [Python-checkins] Daily reference leaks (7cea10917f40): sum=-2 Message-ID: results for 7cea10917f40 on branch "default" -------------------------------------------- test_collections leaked [0, -2, 0] references, sum=-2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogShSIEs', '-x'] From solipsis at pitrou.net Fri Mar 6 00:05:59 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 06 Mar 2015 00:05:59 +0100 Subject: [Python-checkins] Daily reference leaks (461afc24fabc): sum=0 Message-ID: results for 461afc24fabc on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogXItKPy', '-x'] From python-checkins at python.org Fri Mar 6 00:47:47 2015 From: python-checkins at python.org (ned.deily) Date: Thu, 05 Mar 2015 23:47:47 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Fix_regression_introduced_?= =?utf-8?q?by_changeset_7c6e3358221a_that_caused_compile?= Message-ID: <20150305234738.7559.89903@psf.io> https://hg.python.org/cpython/rev/d2b9c615880e changeset: 94871:d2b9c615880e user: Ned Deily date: Thu Mar 05 15:47:10 2015 -0800 summary: Fix regression introduced by changeset 7c6e3358221a that caused compile errors of _testcapimodule.c with older versions of gcc. 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 @@ -69,7 +69,7 @@ static PyObject* test_sizeof_c_types(PyObject *self) { -#ifdef __GNUC__ +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" #endif @@ -130,7 +130,7 @@ #undef IS_SIGNED #undef CHECK_SIGNESS #undef CHECK_SIZEOF -#ifdef __GNUC__ +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))) #pragma GCC diagnostic pop #endif } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 01:46:58 2015 From: python-checkins at python.org (robert.collins) Date: Fri, 06 Mar 2015 00:46:58 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2322936=3A_Allow_sh?= =?utf-8?q?owing_local_variables_in_unittest_errors=2E?= Message-ID: <20150306004645.16655.8056@psf.io> https://hg.python.org/cpython/rev/b4a26b28f5b3 changeset: 94872:b4a26b28f5b3 user: Robert Collins date: Fri Mar 06 13:46:35 2015 +1300 summary: Issue #22936: Allow showing local variables in unittest errors. files: Doc/library/unittest.rst | 25 ++++++++-- Lib/unittest/main.py | 23 +++++++-- Lib/unittest/result.py | 7 ++- Lib/unittest/runner.py | 10 +++- Lib/unittest/test/test_break.py | 3 + Lib/unittest/test/test_program.py | 26 +++++++++- Lib/unittest/test/test_result.py | 43 +++++++++++++++--- Lib/unittest/test/test_runner.py | 10 +++- Misc/NEWS | 3 +- 9 files changed, 118 insertions(+), 32 deletions(-) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -223,9 +223,16 @@ Stop the test run on the first error or failure. +.. cmdoption:: --locals + + Show local variables in tracebacks. + .. versionadded:: 3.2 The command-line options ``-b``, ``-c`` and ``-f`` were added. +.. versionadded:: 3.5 + The command-line option ``--locals``. + The command line can also be used for test discovery, for running all of the tests in a project or just a subset. @@ -1782,12 +1789,10 @@ Set to ``True`` when the execution of tests should stop by :meth:`stop`. - .. attribute:: testsRun The total number of tests run so far. - .. attribute:: buffer If set to true, ``sys.stdout`` and ``sys.stderr`` will be buffered in between @@ -1797,7 +1802,6 @@ .. versionadded:: 3.2 - .. attribute:: failfast If set to true :meth:`stop` will be called on the first failure or error, @@ -1805,6 +1809,11 @@ .. versionadded:: 3.2 + .. attribute:: tb_locals + + If set to true then local variables will be shown in tracebacks. + + .. versionadded:: 3.5 .. method:: wasSuccessful() @@ -1815,7 +1824,6 @@ Returns ``False`` if there were any :attr:`unexpectedSuccesses` from tests marked with the :func:`expectedFailure` decorator. - .. method:: stop() This method can be called to signal that the set of tests being run should @@ -1947,12 +1955,14 @@ .. class:: TextTestRunner(stream=None, descriptions=True, verbosity=1, failfast=False, \ - buffer=False, resultclass=None, warnings=None) + buffer=False, resultclass=None, warnings=None, *, tb_locals=False) A basic test runner implementation that outputs results to a stream. If *stream* is ``None``, the default, :data:`sys.stderr` is used as the output stream. This class has a few configurable parameters, but is essentially very simple. Graphical - applications which run test suites should provide alternate implementations. + applications which run test suites should provide alternate implementations. Such + implementations should accept ``**kwargs`` as the interface to construct runners + changes when features are added to unittest. By default this runner shows :exc:`DeprecationWarning`, :exc:`PendingDeprecationWarning`, :exc:`ResourceWarning` and @@ -1971,6 +1981,9 @@ The default stream is set to :data:`sys.stderr` at instantiation time rather than import time. + .. versionchanged:: 3.5 + Added the tb_locals parameter. + .. method:: _makeResult() This method returns the instance of ``TestResult`` used by :meth:`run`. diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py --- a/Lib/unittest/main.py +++ b/Lib/unittest/main.py @@ -58,7 +58,7 @@ def __init__(self, module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, - buffer=None, warnings=None): + buffer=None, warnings=None, *, tb_locals=False): if isinstance(module, str): self.module = __import__(module) for part in module.split('.')[1:]: @@ -73,6 +73,7 @@ self.catchbreak = catchbreak self.verbosity = verbosity self.buffer = buffer + self.tb_locals = tb_locals if warnings is None and not sys.warnoptions: # even if DeprecationWarnings are ignored by default # print them anyway unless other warnings settings are @@ -159,7 +160,9 @@ parser.add_argument('-q', '--quiet', dest='verbosity', action='store_const', const=0, help='Quiet output') - + parser.add_argument('--locals', dest='tb_locals', + action='store_true', + help='Show local variables in tracebacks') if self.failfast is None: parser.add_argument('-f', '--failfast', dest='failfast', action='store_true', @@ -231,10 +234,18 @@ self.testRunner = runner.TextTestRunner if isinstance(self.testRunner, type): try: - testRunner = self.testRunner(verbosity=self.verbosity, - failfast=self.failfast, - buffer=self.buffer, - warnings=self.warnings) + try: + testRunner = self.testRunner(verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer, + warnings=self.warnings, + tb_locals=self.tb_locals) + except TypeError: + # didn't accept the tb_locals argument + testRunner = self.testRunner(verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer, + warnings=self.warnings) except TypeError: # didn't accept the verbosity, buffer or failfast arguments testRunner = self.testRunner() diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -45,6 +45,7 @@ self.unexpectedSuccesses = [] self.shouldStop = False self.buffer = False + self.tb_locals = False self._stdout_buffer = None self._stderr_buffer = None self._original_stdout = sys.stdout @@ -179,9 +180,11 @@ if exctype is test.failureException: # Skip assert*() traceback levels length = self._count_relevant_tb_levels(tb) - msgLines = traceback.format_exception(exctype, value, tb, length) else: - msgLines = traceback.format_exception(exctype, value, tb) + length = None + tb_e = traceback.TracebackException( + exctype, value, tb, limit=length, capture_locals=self.tb_locals) + msgLines = list(tb_e.format()) if self.buffer: output = sys.stdout.getvalue() diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py --- a/Lib/unittest/runner.py +++ b/Lib/unittest/runner.py @@ -126,7 +126,13 @@ resultclass = TextTestResult def __init__(self, stream=None, descriptions=True, verbosity=1, - failfast=False, buffer=False, resultclass=None, warnings=None): + failfast=False, buffer=False, resultclass=None, warnings=None, + *, tb_locals=False): + """Construct a TextTestRunner. + + Subclasses should accept **kwargs to ensure compatibility as the + interface changes. + """ if stream is None: stream = sys.stderr self.stream = _WritelnDecorator(stream) @@ -134,6 +140,7 @@ self.verbosity = verbosity self.failfast = failfast self.buffer = buffer + self.tb_locals = tb_locals self.warnings = warnings if resultclass is not None: self.resultclass = resultclass @@ -147,6 +154,7 @@ registerResult(result) result.failfast = self.failfast result.buffer = self.buffer + result.tb_locals = self.tb_locals with warnings.catch_warnings(): if self.warnings: # if self.warnings is set, use it to filter all the warnings diff --git a/Lib/unittest/test/test_break.py b/Lib/unittest/test/test_break.py --- a/Lib/unittest/test/test_break.py +++ b/Lib/unittest/test/test_break.py @@ -211,6 +211,7 @@ self.verbosity = verbosity self.failfast = failfast self.catchbreak = catchbreak + self.tb_locals = False self.testRunner = FakeRunner self.test = test self.result = None @@ -221,6 +222,7 @@ self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None, 'verbosity': verbosity, 'failfast': failfast, + 'tb_locals': False, 'warnings': None})]) self.assertEqual(FakeRunner.runArgs, [test]) self.assertEqual(p.result, result) @@ -235,6 +237,7 @@ self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None, 'verbosity': verbosity, 'failfast': failfast, + 'tb_locals': False, 'warnings': None})]) self.assertEqual(FakeRunner.runArgs, [test]) self.assertEqual(p.result, result) diff --git a/Lib/unittest/test/test_program.py b/Lib/unittest/test/test_program.py --- a/Lib/unittest/test/test_program.py +++ b/Lib/unittest/test/test_program.py @@ -134,6 +134,7 @@ result = None verbosity = 1 defaultTest = None + tb_locals = False testRunner = None testLoader = unittest.defaultTestLoader module = '__main__' @@ -147,18 +148,19 @@ class FakeRunner(object): initArgs = None test = None - raiseError = False + raiseError = 0 def __init__(self, **kwargs): FakeRunner.initArgs = kwargs if FakeRunner.raiseError: - FakeRunner.raiseError = False + FakeRunner.raiseError -= 1 raise TypeError def run(self, test): FakeRunner.test = test return RESULT + class TestCommandLineArgs(unittest.TestCase): def setUp(self): @@ -166,7 +168,7 @@ self.program.createTests = lambda: None FakeRunner.initArgs = None FakeRunner.test = None - FakeRunner.raiseError = False + FakeRunner.raiseError = 0 def testVerbosity(self): program = self.program @@ -256,6 +258,7 @@ self.assertEqual(FakeRunner.initArgs, {'verbosity': 'verbosity', 'failfast': 'failfast', 'buffer': 'buffer', + 'tb_locals': False, 'warnings': 'warnings'}) self.assertEqual(FakeRunner.test, 'test') self.assertIs(program.result, RESULT) @@ -274,10 +277,25 @@ self.assertEqual(FakeRunner.test, 'test') self.assertIs(program.result, RESULT) + def test_locals(self): + program = self.program + + program.testRunner = FakeRunner + program.parseArgs([None, '--locals']) + self.assertEqual(True, program.tb_locals) + program.runTests() + self.assertEqual(FakeRunner.initArgs, {'buffer': False, + 'failfast': False, + 'tb_locals': True, + 'verbosity': 1, + 'warnings': None}) + def testRunTestsOldRunnerClass(self): program = self.program - FakeRunner.raiseError = True + # Two TypeErrors are needed to fall all the way back to old-style + # runners - one to fail tb_locals, one to fail buffer etc. + FakeRunner.raiseError = 2 program.testRunner = FakeRunner program.verbosity = 'verbosity' program.failfast = 'failfast' diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py --- a/Lib/unittest/test/test_result.py +++ b/Lib/unittest/test/test_result.py @@ -8,6 +8,20 @@ import unittest +class MockTraceback(object): + class TracebackException: + def __init__(self, *args, **kwargs): + self.capture_locals = kwargs.get('capture_locals', False) + def format(self): + result = ['A traceback'] + if self.capture_locals: + result.append('locals') + return result + +def restore_traceback(): + unittest.result.traceback = traceback + + class Test_TestResult(unittest.TestCase): # Note: there are not separate tests for TestResult.wasSuccessful(), # TestResult.errors, TestResult.failures, TestResult.testsRun or @@ -227,6 +241,25 @@ self.assertIs(test_case, test) self.assertIsInstance(formatted_exc, str) + def test_addError_locals(self): + class Foo(unittest.TestCase): + def test_1(self): + 1/0 + + test = Foo('test_1') + result = unittest.TestResult() + result.tb_locals = True + + unittest.result.traceback = MockTraceback + self.addCleanup(restore_traceback) + result.startTestRun() + test.run(result) + result.stopTestRun() + + self.assertEqual(len(result.errors), 1) + test_case, formatted_exc = result.errors[0] + self.assertEqual('A tracebacklocals', formatted_exc) + def test_addSubTest(self): class Foo(unittest.TestCase): def test_1(self): @@ -398,6 +431,7 @@ self.testsRun = 0 self.shouldStop = False self.buffer = False + self.tb_locals = False classDict['__init__'] = __init__ OldResult = type('OldResult', (object,), classDict) @@ -454,15 +488,6 @@ runner.run(Test('testFoo')) -class MockTraceback(object): - @staticmethod - def format_exception(*_): - return ['A traceback'] - -def restore_traceback(): - unittest.result.traceback = traceback - - class TestOutputBuffering(unittest.TestCase): def setUp(self): diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py --- a/Lib/unittest/test/test_runner.py +++ b/Lib/unittest/test/test_runner.py @@ -158,7 +158,7 @@ self.assertEqual(runner.warnings, None) self.assertTrue(runner.descriptions) self.assertEqual(runner.resultclass, unittest.TextTestResult) - + self.assertFalse(runner.tb_locals) def test_multiple_inheritance(self): class AResult(unittest.TestResult): @@ -172,14 +172,13 @@ # on arguments in its __init__ super call ATextResult(None, None, 1) - def testBufferAndFailfast(self): class Test(unittest.TestCase): def testFoo(self): pass result = unittest.TestResult() runner = unittest.TextTestRunner(stream=io.StringIO(), failfast=True, - buffer=True) + buffer=True) # Use our result object runner._makeResult = lambda: result runner.run(Test('testFoo')) @@ -187,6 +186,11 @@ self.assertTrue(result.failfast) self.assertTrue(result.buffer) + def test_locals(self): + runner = unittest.TextTestRunner(stream=io.StringIO(), tb_locals=True) + result = runner.run(unittest.TestSuite()) + self.assertEqual(True, result.tb_locals) + def testRunnerRegistersResult(self): class Test(unittest.TestCase): def testFoo(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -39,7 +39,8 @@ - Issue #21619: Popen objects no longer leave a zombie after exit in the with statement if the pipe was broken. Patch by Martin Panter. -- Issue #22936: Make it possible to show local variables in tracebacks. +- Issue #22936: Make it possible to show local variables in tracebacks for + both the traceback module and unittest. - Issue #15955: Add an option to limit the output size in bz2.decompress(). Patch by Nikolaus Rath. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 02:37:33 2015 From: python-checkins at python.org (ned.deily) Date: Fri, 06 Mar 2015 01:37:33 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIzNTk0?= =?utf-8?q?=3A_Update_OS_X_10=2E5_installer_build_to_use_OpenSSL_1=2E0=2E2?= =?utf-8?q?=2E?= Message-ID: <20150306013732.16659.77527@psf.io> https://hg.python.org/cpython/rev/62c3742eb25f changeset: 94873:62c3742eb25f branch: 2.7 parent: 94864:ec9bffc35cad user: Ned Deily date: Thu Mar 05 17:32:28 2015 -0800 summary: Issue #23594: Update OS X 10.5 installer build to use OpenSSL 1.0.2. files: Mac/BuildScript/build-installer.py | 6 +- Mac/BuildScript/openssl_sdk_makedepend.patch | 26 +++++---- Misc/NEWS | 2 +- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -237,9 +237,9 @@ result.extend([ dict( - name="OpenSSL 1.0.1l", - url="https://www.openssl.org/source/openssl-1.0.1l.tar.gz", - checksum='cdb22925fc9bc97ccbf1e007661f2aa6', + name="OpenSSL 1.0.2", + url="https://www.openssl.org/source/openssl-1.0.2.tar.gz", + checksum='38373013fc85c790aabf8837969c5eba', patches=[ "openssl_sdk_makedepend.patch", ], diff --git a/Mac/BuildScript/openssl_sdk_makedepend.patch b/Mac/BuildScript/openssl_sdk_makedepend.patch --- a/Mac/BuildScript/openssl_sdk_makedepend.patch +++ b/Mac/BuildScript/openssl_sdk_makedepend.patch @@ -1,16 +1,18 @@ +# HG changeset patch +# Parent 973741cf5a045b2ba895094d98b2c3649dc00b61 # openssl_sdk_makedepend.patch # -# using openssl 1.0.1k +# using openssl 1.0.2 # # - support building with an OS X SDK # - allow "make depend" to use compilers with names other than "gcc" diff Configure -diff -r 99ae439a07f1 Configure ---- a/Configure Fri Jan 09 12:50:43 2015 -0800 -+++ b/Configure Fri Jan 09 12:53:52 2015 -0800 -@@ -577,11 +577,11 @@ +diff -r 973741cf5a04 Configure +--- a/Configure Thu Mar 05 13:45:59 2015 -0800 ++++ b/Configure Thu Mar 05 14:00:20 2015 -0800 +@@ -617,12 +617,12 @@ ##### MacOS X (a.k.a. Rhapsody or Darwin) setup "rhapsody-ppc-cc","cc:-O3 -DB_ENDIAN::(unknown):MACOSX_RHAPSODY::BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${no_asm}::", @@ -18,16 +20,18 @@ -"darwin64-ppc-cc","cc:-arch ppc64 -O3 -DB_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc64_asm}:osx64:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -"darwin-i386-cc","cc:-arch i386 -O3 -fomit-frame-pointer -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:".eval{my $asm=$x86_asm;$asm=~s/cast\-586\.o//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -"debug-darwin-i386-cc","cc:-arch i386 -g3 -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:${x86_asm}:macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", --"darwin64-x86_64-cc","cc:-arch x86_64 -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +-"darwin64-x86_64-cc","cc:-arch x86_64 -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +-"debug-darwin64-x86_64-cc","cc:-arch x86_64 -ggdb -g2 -O0 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"darwin-ppc-cc","cc:-arch ppc -isysroot \$(OSX_SDK) -O3 -DB_ENDIAN -Wa,-force_cpusubtype_ALL::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc32_asm}:osx32:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"darwin64-ppc-cc","cc:-arch ppc64 -isysroot \$(OSX_SDK) -O3 -DB_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc64_asm}:osx64:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"darwin-i386-cc","cc:-arch i386 -isysroot \$(OSX_SDK) -O3 -fomit-frame-pointer -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:".eval{my $asm=$x86_asm;$asm=~s/cast\-586\.o//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"debug-darwin-i386-cc","cc:-arch i386 -isysroot \$(OSX_SDK) -g3 -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:${x86_asm}:macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -+"darwin64-x86_64-cc","cc:-arch x86_64 -isysroot \$(OSX_SDK) -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", ++"darwin64-x86_64-cc","cc:-arch x86_64 -isysroot \$(OSX_SDK) -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", ++"debug-darwin64-x86_64-cc","cc:-arch x86_64 -isysroot \$(OSX_SDK) -ggdb -g2 -O0 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", "debug-darwin-ppc-cc","cc:-DBN_DEBUG -DREF_CHECK -DCONF_DEBUG -DCRYPTO_MDEBUG -DB_ENDIAN -g -Wall -O::-D_REENTRANT:MACOSX::BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc32_asm}:osx32:dlfcn:darwin-shared:-fPIC:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", # iPhoneOS/iOS "iphoneos-cross","llvm-gcc:-O3 -isysroot \$(CROSS_TOP)/SDKs/\$(CROSS_SDK) -fomit-frame-pointer -fno-common::-D_REENTRANT:iOS:-Wl,-search_paths_first%:BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${no_asm}:dlfcn:darwin-shared:-fPIC -fno-common:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -@@ -1629,7 +1629,7 @@ +@@ -1685,7 +1685,7 @@ s/^CC=.*$/CC= $cc/; s/^AR=\s*ar/AR= $ar/; s/^RANLIB=.*/RANLIB= $ranlib/; @@ -36,9 +40,9 @@ } s/^CFLAG=.*$/CFLAG= $cflags/; s/^DEPFLAG=.*$/DEPFLAG=$depflags/; -diff -r 99ae439a07f1 util/domd ---- a/util/domd Fri Jan 09 12:50:43 2015 -0800 -+++ b/util/domd Fri Jan 09 12:53:52 2015 -0800 +diff -r 973741cf5a04 util/domd +--- a/util/domd Thu Mar 05 13:45:59 2015 -0800 ++++ b/util/domd Thu Mar 05 14:00:20 2015 -0800 @@ -14,7 +14,7 @@ cp Makefile Makefile.save # fake the presence of Kerberos diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -177,7 +177,7 @@ - Issue #23032: Fix installer build failures on OS X 10.4 Tiger by disabling assembly code in the OpenSSL build. -- Issue #23212: Update 10.5 OS X installer build to use OpenSSL 1.0.1l. +- Issue #23594: Update OS X 10.5 installer build to use OpenSSL 1.0.2. C API ----- -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 02:37:33 2015 From: python-checkins at python.org (ned.deily) Date: Fri, 06 Mar 2015 01:37:33 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323594=3A_merge_3=2E4?= Message-ID: <20150306013732.1771.48805@psf.io> https://hg.python.org/cpython/rev/73aa2f168c5a changeset: 94875:73aa2f168c5a parent: 94872:b4a26b28f5b3 parent: 94874:b2f3a44dbe1b user: Ned Deily date: Thu Mar 05 17:37:11 2015 -0800 summary: Issue #23594: merge 3.4 files: Mac/BuildScript/build-installer.py | 6 +- Mac/BuildScript/openssl_sdk_makedepend.patch | 26 +++++---- Misc/NEWS | 2 + 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -237,9 +237,9 @@ result.extend([ dict( - name="OpenSSL 1.0.1l", - url="https://www.openssl.org/source/openssl-1.0.1l.tar.gz", - checksum='cdb22925fc9bc97ccbf1e007661f2aa6', + name="OpenSSL 1.0.2", + url="https://www.openssl.org/source/openssl-1.0.2.tar.gz", + checksum='38373013fc85c790aabf8837969c5eba', patches=[ "openssl_sdk_makedepend.patch", ], diff --git a/Mac/BuildScript/openssl_sdk_makedepend.patch b/Mac/BuildScript/openssl_sdk_makedepend.patch --- a/Mac/BuildScript/openssl_sdk_makedepend.patch +++ b/Mac/BuildScript/openssl_sdk_makedepend.patch @@ -1,16 +1,18 @@ +# HG changeset patch +# Parent 973741cf5a045b2ba895094d98b2c3649dc00b61 # openssl_sdk_makedepend.patch # -# using openssl 1.0.1k +# using openssl 1.0.2 # # - support building with an OS X SDK # - allow "make depend" to use compilers with names other than "gcc" diff Configure -diff -r 99ae439a07f1 Configure ---- a/Configure Fri Jan 09 12:50:43 2015 -0800 -+++ b/Configure Fri Jan 09 12:53:52 2015 -0800 -@@ -577,11 +577,11 @@ +diff -r 973741cf5a04 Configure +--- a/Configure Thu Mar 05 13:45:59 2015 -0800 ++++ b/Configure Thu Mar 05 14:00:20 2015 -0800 +@@ -617,12 +617,12 @@ ##### MacOS X (a.k.a. Rhapsody or Darwin) setup "rhapsody-ppc-cc","cc:-O3 -DB_ENDIAN::(unknown):MACOSX_RHAPSODY::BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${no_asm}::", @@ -18,16 +20,18 @@ -"darwin64-ppc-cc","cc:-arch ppc64 -O3 -DB_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc64_asm}:osx64:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -"darwin-i386-cc","cc:-arch i386 -O3 -fomit-frame-pointer -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:".eval{my $asm=$x86_asm;$asm=~s/cast\-586\.o//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -"debug-darwin-i386-cc","cc:-arch i386 -g3 -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:${x86_asm}:macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", --"darwin64-x86_64-cc","cc:-arch x86_64 -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +-"darwin64-x86_64-cc","cc:-arch x86_64 -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +-"debug-darwin64-x86_64-cc","cc:-arch x86_64 -ggdb -g2 -O0 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"darwin-ppc-cc","cc:-arch ppc -isysroot \$(OSX_SDK) -O3 -DB_ENDIAN -Wa,-force_cpusubtype_ALL::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc32_asm}:osx32:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"darwin64-ppc-cc","cc:-arch ppc64 -isysroot \$(OSX_SDK) -O3 -DB_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc64_asm}:osx64:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"darwin-i386-cc","cc:-arch i386 -isysroot \$(OSX_SDK) -O3 -fomit-frame-pointer -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:".eval{my $asm=$x86_asm;$asm=~s/cast\-586\.o//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"debug-darwin-i386-cc","cc:-arch i386 -isysroot \$(OSX_SDK) -g3 -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:${x86_asm}:macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -+"darwin64-x86_64-cc","cc:-arch x86_64 -isysroot \$(OSX_SDK) -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", ++"darwin64-x86_64-cc","cc:-arch x86_64 -isysroot \$(OSX_SDK) -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", ++"debug-darwin64-x86_64-cc","cc:-arch x86_64 -isysroot \$(OSX_SDK) -ggdb -g2 -O0 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", "debug-darwin-ppc-cc","cc:-DBN_DEBUG -DREF_CHECK -DCONF_DEBUG -DCRYPTO_MDEBUG -DB_ENDIAN -g -Wall -O::-D_REENTRANT:MACOSX::BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc32_asm}:osx32:dlfcn:darwin-shared:-fPIC:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", # iPhoneOS/iOS "iphoneos-cross","llvm-gcc:-O3 -isysroot \$(CROSS_TOP)/SDKs/\$(CROSS_SDK) -fomit-frame-pointer -fno-common::-D_REENTRANT:iOS:-Wl,-search_paths_first%:BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${no_asm}:dlfcn:darwin-shared:-fPIC -fno-common:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -@@ -1629,7 +1629,7 @@ +@@ -1685,7 +1685,7 @@ s/^CC=.*$/CC= $cc/; s/^AR=\s*ar/AR= $ar/; s/^RANLIB=.*/RANLIB= $ranlib/; @@ -36,9 +40,9 @@ } s/^CFLAG=.*$/CFLAG= $cflags/; s/^DEPFLAG=.*$/DEPFLAG=$depflags/; -diff -r 99ae439a07f1 util/domd ---- a/util/domd Fri Jan 09 12:50:43 2015 -0800 -+++ b/util/domd Fri Jan 09 12:53:52 2015 -0800 +diff -r 973741cf5a04 util/domd +--- a/util/domd Thu Mar 05 13:45:59 2015 -0800 ++++ b/util/domd Thu Mar 05 14:00:20 2015 -0800 @@ -14,7 +14,7 @@ cp Makefile Makefile.save # fake the presence of Kerberos diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -108,6 +108,8 @@ - Issue #23445: pydebug builds now use "gcc -Og" where possible, to make the resulting executable faster. +- Issue #23594: Update OS X 10.5 installer build to use OpenSSL 1.0.2. + C API ----- -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 02:37:33 2015 From: python-checkins at python.org (ned.deily) Date: Fri, 06 Mar 2015 01:37:33 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNTk0?= =?utf-8?q?=3A_Update_OS_X_10=2E5_installer_build_to_use_OpenSSL_1=2E0=2E2?= =?utf-8?q?=2E?= Message-ID: <20150306013732.1779.80618@psf.io> https://hg.python.org/cpython/rev/b2f3a44dbe1b changeset: 94874:b2f3a44dbe1b branch: 3.4 parent: 94862:9ca2c98e4395 user: Ned Deily date: Thu Mar 05 17:34:24 2015 -0800 summary: Issue #23594: Update OS X 10.5 installer build to use OpenSSL 1.0.2. files: Mac/BuildScript/build-installer.py | 6 +- Mac/BuildScript/openssl_sdk_makedepend.patch | 26 +++++---- Misc/NEWS | 2 + 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -237,9 +237,9 @@ result.extend([ dict( - name="OpenSSL 1.0.1l", - url="https://www.openssl.org/source/openssl-1.0.1l.tar.gz", - checksum='cdb22925fc9bc97ccbf1e007661f2aa6', + name="OpenSSL 1.0.2", + url="https://www.openssl.org/source/openssl-1.0.2.tar.gz", + checksum='38373013fc85c790aabf8837969c5eba', patches=[ "openssl_sdk_makedepend.patch", ], diff --git a/Mac/BuildScript/openssl_sdk_makedepend.patch b/Mac/BuildScript/openssl_sdk_makedepend.patch --- a/Mac/BuildScript/openssl_sdk_makedepend.patch +++ b/Mac/BuildScript/openssl_sdk_makedepend.patch @@ -1,16 +1,18 @@ +# HG changeset patch +# Parent 973741cf5a045b2ba895094d98b2c3649dc00b61 # openssl_sdk_makedepend.patch # -# using openssl 1.0.1k +# using openssl 1.0.2 # # - support building with an OS X SDK # - allow "make depend" to use compilers with names other than "gcc" diff Configure -diff -r 99ae439a07f1 Configure ---- a/Configure Fri Jan 09 12:50:43 2015 -0800 -+++ b/Configure Fri Jan 09 12:53:52 2015 -0800 -@@ -577,11 +577,11 @@ +diff -r 973741cf5a04 Configure +--- a/Configure Thu Mar 05 13:45:59 2015 -0800 ++++ b/Configure Thu Mar 05 14:00:20 2015 -0800 +@@ -617,12 +617,12 @@ ##### MacOS X (a.k.a. Rhapsody or Darwin) setup "rhapsody-ppc-cc","cc:-O3 -DB_ENDIAN::(unknown):MACOSX_RHAPSODY::BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${no_asm}::", @@ -18,16 +20,18 @@ -"darwin64-ppc-cc","cc:-arch ppc64 -O3 -DB_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc64_asm}:osx64:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -"darwin-i386-cc","cc:-arch i386 -O3 -fomit-frame-pointer -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:".eval{my $asm=$x86_asm;$asm=~s/cast\-586\.o//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -"debug-darwin-i386-cc","cc:-arch i386 -g3 -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:${x86_asm}:macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", --"darwin64-x86_64-cc","cc:-arch x86_64 -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +-"darwin64-x86_64-cc","cc:-arch x86_64 -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +-"debug-darwin64-x86_64-cc","cc:-arch x86_64 -ggdb -g2 -O0 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"darwin-ppc-cc","cc:-arch ppc -isysroot \$(OSX_SDK) -O3 -DB_ENDIAN -Wa,-force_cpusubtype_ALL::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc32_asm}:osx32:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"darwin64-ppc-cc","cc:-arch ppc64 -isysroot \$(OSX_SDK) -O3 -DB_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc64_asm}:osx64:dlfcn:darwin-shared:-fPIC -fno-common:-arch ppc64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"darwin-i386-cc","cc:-arch i386 -isysroot \$(OSX_SDK) -O3 -fomit-frame-pointer -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:".eval{my $asm=$x86_asm;$asm=~s/cast\-586\.o//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", +"debug-darwin-i386-cc","cc:-arch i386 -isysroot \$(OSX_SDK) -g3 -DL_ENDIAN::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:BN_LLONG RC4_INT RC4_CHUNK DES_UNROLL BF_PTR:${x86_asm}:macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch i386 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -+"darwin64-x86_64-cc","cc:-arch x86_64 -isysroot \$(OSX_SDK) -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", ++"darwin64-x86_64-cc","cc:-arch x86_64 -isysroot \$(OSX_SDK) -O3 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", ++"debug-darwin64-x86_64-cc","cc:-arch x86_64 -isysroot \$(OSX_SDK) -ggdb -g2 -O0 -DL_ENDIAN -Wall::-D_REENTRANT:MACOSX:-Wl,-search_paths_first%:SIXTY_FOUR_BIT_LONG RC4_CHUNK DES_INT DES_UNROLL:".eval{my $asm=$x86_64_asm;$asm=~s/rc4\-[^:]+//;$asm}.":macosx:dlfcn:darwin-shared:-fPIC -fno-common:-arch x86_64 -dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", "debug-darwin-ppc-cc","cc:-DBN_DEBUG -DREF_CHECK -DCONF_DEBUG -DCRYPTO_MDEBUG -DB_ENDIAN -g -Wall -O::-D_REENTRANT:MACOSX::BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${ppc32_asm}:osx32:dlfcn:darwin-shared:-fPIC:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", # iPhoneOS/iOS "iphoneos-cross","llvm-gcc:-O3 -isysroot \$(CROSS_TOP)/SDKs/\$(CROSS_SDK) -fomit-frame-pointer -fno-common::-D_REENTRANT:iOS:-Wl,-search_paths_first%:BN_LLONG RC4_CHAR RC4_CHUNK DES_UNROLL BF_PTR:${no_asm}:dlfcn:darwin-shared:-fPIC -fno-common:-dynamiclib:.\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", -@@ -1629,7 +1629,7 @@ +@@ -1685,7 +1685,7 @@ s/^CC=.*$/CC= $cc/; s/^AR=\s*ar/AR= $ar/; s/^RANLIB=.*/RANLIB= $ranlib/; @@ -36,9 +40,9 @@ } s/^CFLAG=.*$/CFLAG= $cflags/; s/^DEPFLAG=.*$/DEPFLAG=$depflags/; -diff -r 99ae439a07f1 util/domd ---- a/util/domd Fri Jan 09 12:50:43 2015 -0800 -+++ b/util/domd Fri Jan 09 12:53:52 2015 -0800 +diff -r 973741cf5a04 util/domd +--- a/util/domd Thu Mar 05 13:45:59 2015 -0800 ++++ b/util/domd Thu Mar 05 14:00:20 2015 -0800 @@ -14,7 +14,7 @@ cp Makefile Makefile.save # fake the presence of Kerberos diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -71,6 +71,8 @@ - Issue #23445: pydebug builds now use "gcc -Og" where possible, to make the resulting executable faster. +- Issue #23594: Update OS X 10.5 installer build to use OpenSSL 1.0.2. + What's New in Python 3.4.3? =========================== -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 02:45:42 2015 From: python-checkins at python.org (ned.deily) Date: Fri, 06 Mar 2015 01:45:42 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNTkz?= =?utf-8?q?=3A_fix_Misc/NEWS_entries?= Message-ID: <20150306014542.1775.79228@psf.io> https://hg.python.org/cpython/rev/da3fe5fda078 changeset: 94877:da3fe5fda078 branch: 3.4 parent: 94874:b2f3a44dbe1b user: Ned Deily date: Thu Mar 05 17:44:10 2015 -0800 summary: Issue #23593: fix Misc/NEWS entries files: Misc/NEWS | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -71,7 +71,7 @@ - Issue #23445: pydebug builds now use "gcc -Og" where possible, to make the resulting executable faster. -- Issue #23594: Update OS X 10.5 installer build to use OpenSSL 1.0.2. +- Issue #23593: Update OS X 10.5 installer build to use OpenSSL 1.0.2. What's New in Python 3.4.3? -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 02:45:42 2015 From: python-checkins at python.org (ned.deily) Date: Fri, 06 Mar 2015 01:45:42 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323593=3A_fix_Misc/NEWS_entries?= Message-ID: <20150306014542.1777.45310@psf.io> https://hg.python.org/cpython/rev/8ef4f75a8018 changeset: 94878:8ef4f75a8018 parent: 94875:73aa2f168c5a parent: 94877:da3fe5fda078 user: Ned Deily date: Thu Mar 05 17:45:03 2015 -0800 summary: Issue #23593: fix Misc/NEWS entries files: Misc/NEWS | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -108,7 +108,7 @@ - Issue #23445: pydebug builds now use "gcc -Og" where possible, to make the resulting executable faster. -- Issue #23594: Update OS X 10.5 installer build to use OpenSSL 1.0.2. +- Issue #23593: Update OS X 10.5 installer build to use OpenSSL 1.0.2. C API ----- -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 02:45:42 2015 From: python-checkins at python.org (ned.deily) Date: Fri, 06 Mar 2015 01:45:42 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIzNTkz?= =?utf-8?q?=3A_fix_Misc/NEWS_entries?= Message-ID: <20150306014542.7559.57328@psf.io> https://hg.python.org/cpython/rev/e74e2ce81a1c changeset: 94876:e74e2ce81a1c branch: 2.7 parent: 94873:62c3742eb25f user: Ned Deily date: Thu Mar 05 17:43:26 2015 -0800 summary: Issue #23593: fix Misc/NEWS entries files: Misc/NEWS | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -177,7 +177,7 @@ - Issue #23032: Fix installer build failures on OS X 10.4 Tiger by disabling assembly code in the OpenSSL build. -- Issue #23594: Update OS X 10.5 installer build to use OpenSSL 1.0.2. +- Issue #23593: Update OS X 10.5 installer build to use OpenSSL 1.0.2. C API ----- -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 11:17:40 2015 From: python-checkins at python.org (berker.peksag) Date: Fri, 06 Mar 2015 10:17:40 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2317911=3A_Tweak_tr?= =?utf-8?q?aceback_documentation=2E?= Message-ID: <20150306101734.16645.60860@psf.io> https://hg.python.org/cpython/rev/648b35f22b91 changeset: 94879:648b35f22b91 user: Berker Peksag date: Fri Mar 06 12:18:06 2015 +0200 summary: Issue #17911: Tweak traceback documentation. Changes: * Fixed reSt markup * Fixed cross references * Fixed a couple of typos files: Doc/library/traceback.rst | 166 ++++++++++++++----------- 1 files changed, 95 insertions(+), 71 deletions(-) diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -138,16 +138,16 @@ .. function:: walk_stack(f) - Walk a stack following f.f_back from the given frame, yielding the frame and - line number for each frame. If f is None, the current stack is used. - This helper is used with *Stack.extract*. + Walk a stack following ``f.f_back`` from the given frame, yielding the frame + and line number for each frame. If *f* is ``None``, the current stack is + used. This helper is used with :meth:`StackSummary.extract`. .. versionadded:: 3.5 .. function:: walk_tb(tb) - Walk a traceback following tb_next yielding the frame and line number for - each frame. This helper is used with *Stack.extract*. + Walk a traceback following ``tb_next`` yielding the frame and line number + for each frame. This helper is used with :meth:`StackSummary.extract`. .. versionadded:: 3.5 @@ -156,118 +156,142 @@ :class:`TracebackException` Objects ----------------------------------- -:class:`.TracebackException` objects are created from actual exceptions to +.. versionadded:: 3.5 + +:class:`TracebackException` objects are created from actual exceptions to capture data for later printing in a lightweight fashion. .. class:: TracebackException(exc_type, exc_value, exc_traceback, *, limit=None, lookup_lines=True, capture_locals=False) - Capture an exception for later rendering. limit, lookup_lines and - capture_locals=False are as for the :class:`.StackSummary` class. + Capture an exception for later rendering. *limit*, *lookup_lines* and + *capture_locals* are as for the :class:`StackSummary` class. Note that when locals are captured, they are also shown in the traceback. - .. versionadded:: 3.5 + .. attribute:: __cause__ -.. classmethod:: `.from_exception`(exc, *, limit=None, lookup_lines=True, capture_locals=False) + A :class:`TracebackException` of the original ``__cause__``. - Capture an exception for later rendering. limit, lookup_lines and - capture_locals=False are as for the :class:`.StackSummary` class. + .. attribute:: __context__ - Note that when locals are captured, they are also shown in the traceback. + A :class:`TracebackException` of the original ``__context__``. - .. versionadded:: 3.5 + .. attribute:: __suppress_context__ -.. attribute:: `.__cause__` A TracebackException of the original *__cause__*. + The ``__suppress_context__`` value from the original exception. -.. attribute:: `.__context__` A TracebackException of the original *__context__*. -.. attribute:: `.__suppress_context__` The *__suppress_context__* value from the - original exception. -.. attribute:: `.stack` A `StackSummary` representing the traceback. -.. attribute:: `.exc_type` The class of the original traceback. -.. attribute:: `.filename` For syntax errors - the filename where the error - occured. -.. attribute:: `.lineno` For syntax errors - the linenumber where the error - occured. -.. attribute:: `.text` For syntax errors - the text where the error - occured. -.. attribute:: `.offset` For syntax errors - the offset into the text where the - error occured. -.. attribute:: `.msg` For syntax errors - the compiler error message. + .. attribute:: stack -.. method:: TracebackException.format(*, chain=True) + A :class:`StackSummary` representing the traceback. - Format the exception. + .. attribute:: exc_type - If chain is not *True*, *__cause__* and *__context__* will not be formatted. + The class of the original traceback. - The return value is a generator of strings, each ending in a newline and - some containing internal newlines. `print_exception` is a wrapper around - this method which just prints the lines to a file. + .. attribute:: filename - The message indicating which exception occurred is always the last - string in the output. + For syntax errors - the file name where the error occurred. - .. versionadded:: 3.5 + .. attribute:: lineno -.. method:: TracebackException.format_exception_only() + For syntax errors - the line number where the error occurred. - Format the exception part of the traceback. + .. attribute:: text - The return value is a generator of strings, each ending in a newline. + For syntax errors - the text where the error occurred. - Normally, the generator emits a single string; however, for - SyntaxError exceptions, it emites several lines that (when - printed) display detailed information about where the syntax - error occurred. + .. attribute:: offset - The message indicating which exception occurred is always the last - string in the output. + For syntax errors - the offset into the text where the error occurred. - .. versionadded:: 3.5 + .. attribute:: msg + + For syntax errors - the compiler error message. + + .. classmethod:: from_exception(exc, *, limit=None, lookup_lines=True, capture_locals=False) + + Capture an exception for later rendering. *limit*, *lookup_lines* and + *capture_locals* are as for the :class:`StackSummary` class. + + Note that when locals are captured, they are also shown in the traceback. + + .. method:: format(*, chain=True) + + Format the exception. + + If *chain* is not ``True``, ``__cause__`` and ``__context__`` will not + be formatted. + + The return value is a generator of strings, each ending in a newline and + some containing internal newlines. :func:`~traceback.print_exception` + is a wrapper around this method which just prints the lines to a file. + + The message indicating which exception occurred is always the last + string in the output. + + .. method:: format_exception_only() + + Format the exception part of the traceback. + + The return value is a generator of strings, each ending in a newline. + + Normally, the generator emits a single string; however, for + :exc:`SyntaxError` exceptions, it emits several lines that (when + printed) display detailed information about where the syntax + error occurred. + + The message indicating which exception occurred is always the last + string in the output. :class:`StackSummary` Objects ----------------------------- -:class:`.StackSummary` objects represent a call stack ready for formatting. +.. versionadded:: 3.5 -.. classmethod:: StackSummary.extract(frame_gen, *, limit=None, lookup_lines=True, capture_locals=False) +:class:`StackSummary` objects represent a call stack ready for formatting. - Construct a StackSummary object from a frame generator (such as is returned by - `walk_stack` or `walk_tb`. +.. class:: StackSummary - If limit is supplied, only this many frames are taken from frame_gen. - If lookup_lines is False, the returned FrameSummary objects will not have read - their lines in yet, making the cost of creating the StackSummary cheaper (which - may be valuable if it may not actually get formatted). - If capture_locals is True the local variables in each *FrameSummary* are - captured as object representations. + .. classmethod:: extract(frame_gen, *, limit=None, lookup_lines=True, capture_locals=False) - .. versionadded:: 3.5 + Construct a :class:`StackSummary` object from a frame generator (such as + is returned by :func:`~traceback.walk_stack` or + :func:`~traceback.walk_tb`). -.. classmethod:: StackSummary.from_list(a_list) + If *limit* is supplied, only this many frames are taken from *frame_gen*. + If *lookup_lines* is ``False``, the returned :class:`FrameSummary` + objects will not have read their lines in yet, making the cost of + creating the :class:`StackSummary` cheaper (which may be valuable if it + may not actually get formatted). If *capture_locals* is ``True`` the + local variables in each :class:`FrameSummary` are captured as object + representations. - Construct a StackSummary object from a supplied old-style list of tuples. Each - tuple should be a 4-tuple with filename, lineno, name, line as the elements. + .. classmethod:: from_list(a_list) - .. versionadded:: 3.5 + Construct a :class:`StackSummary` object from a supplied old-style list + of tuples. Each tuple should be a 4-tuple with filename, lineno, name, + line as the elements. + :class:`FrameSummary` Objects ----------------------------- -FrameSummary objects represent a single frame in a traceback. +.. versionadded:: 3.5 + +:class:`FrameSummary` objects represent a single frame in a traceback. .. class:: FrameSummary(filename, lineno, name, lookup_line=True, locals=None, line=None) - :noindex: Represent a single frame in the traceback or stack that is being formatted or printed. It may optionally have a stringified version of the frames - locals included in it. If *lookup_line* is False, the source code is not - looked up until the FrameSummary has the :attr:`line` attribute accessed (which - also happens when casting it to a tuple). *line* may be directly provided, and - will prevent line lookups happening at all. *locals* is an optional local variable - dictionary, and if supplied the variable representations are stored in the summary - for later display. + locals included in it. If *lookup_line* is ``False``, the source code is not + looked up until the :class:`FrameSummary` has the :attr:`~FrameSummary.line` + attribute accessed (which also happens when casting it to a tuple). + :attr:`~FrameSummary.line` may be directly provided, and will prevent line + lookups happening at all. *locals* is an optional local variable + dictionary, and if supplied the variable representations are stored in the + summary for later display. .. _traceback-example: -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Fri Mar 6 12:04:00 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 06 Mar 2015 12:04:00 +0100 Subject: [Python-checkins] Daily reference leaks (1821bc784a23): sum=3 Message-ID: results for 1821bc784a23 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/reflogkhRI56', '-x', 'test_eintr'] From python-checkins at python.org Fri Mar 6 15:10:50 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 06 Mar 2015 14:10:50 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjM1OTAp?= Message-ID: <20150306140950.9688.39837@psf.io> https://hg.python.org/cpython/rev/4d9594018edd changeset: 94882:4d9594018edd parent: 94879:648b35f22b91 parent: 94880:30925a3b2324 user: Benjamin Peterson date: Fri Mar 06 09:09:46 2015 -0500 summary: merge 3.4 (#23590) files: Lib/test/test_float.py | 6 ++++++ Objects/floatobject.c | 1 + 2 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -6,6 +6,7 @@ import math from math import isinf, isnan, copysign, ldexp import operator +import time import random, fractions INF = float("inf") @@ -129,6 +130,11 @@ self.assertRaises(TypeError, float, Foo4(42)) self.assertAlmostEqual(float(FooStr('8')), 9.) + class Foo5: + def __float__(self): + return "" + self.assertRaises(TypeError, time.sleep, Foo5()) + def test_is_integer(self): self.assertFalse((1.1).is_integer()) self.assertTrue((1.).is_integer()) diff --git a/Objects/floatobject.c b/Objects/floatobject.c --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -220,6 +220,7 @@ if (fo == NULL) return -1; if (!PyFloat_Check(fo)) { + Py_DECREF(fo); PyErr_SetString(PyExc_TypeError, "nb_float should return float object"); return -1; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 15:10:50 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 06 Mar 2015 14:10:50 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_fix_potential_?= =?utf-8?q?refleak_in_PyFloat=5FAsDouble_=28closes_=2323590=29?= Message-ID: <20150306140950.9688.96901@psf.io> https://hg.python.org/cpython/rev/f31b91b6683a changeset: 94881:f31b91b6683a branch: 2.7 parent: 94876:e74e2ce81a1c user: Benjamin Peterson date: Fri Mar 06 09:08:44 2015 -0500 summary: fix potential refleak in PyFloat_AsDouble (closes #23590) files: Lib/test/test_float.py | 6 ++++++ Objects/floatobject.c | 1 + 2 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -8,6 +8,7 @@ import random import fractions import sys +import time INF = float("inf") NAN = float("nan") @@ -164,6 +165,11 @@ self.assertAlmostEqual(float(FooUnicode('8')), 9.) self.assertAlmostEqual(float(FooStr('8')), 9.) + class Foo5: + def __float__(self): + return "" + self.assertRaises(TypeError, time.sleep, Foo5()) + def test_is_integer(self): self.assertFalse((1.1).is_integer()) self.assertTrue((1.).is_integer()) diff --git a/Objects/floatobject.c b/Objects/floatobject.c --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -271,6 +271,7 @@ if (fo == NULL) return -1; if (!PyFloat_Check(fo)) { + Py_DECREF(fo); PyErr_SetString(PyExc_TypeError, "nb_float should return float object"); return -1; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 15:10:50 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 06 Mar 2015 14:10:50 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_fix_potential_?= =?utf-8?q?refleak_in_PyFloat=5FAsDouble_=28closes_=2323590=29?= Message-ID: <20150306140950.1771.66893@psf.io> https://hg.python.org/cpython/rev/30925a3b2324 changeset: 94880:30925a3b2324 branch: 3.4 parent: 94877:da3fe5fda078 user: Benjamin Peterson date: Fri Mar 06 09:08:44 2015 -0500 summary: fix potential refleak in PyFloat_AsDouble (closes #23590) files: Lib/test/test_float.py | 6 ++++++ Objects/floatobject.c | 1 + 2 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -6,6 +6,7 @@ import math from math import isinf, isnan, copysign, ldexp import operator +import time import random, fractions INF = float("inf") @@ -129,6 +130,11 @@ self.assertRaises(TypeError, float, Foo4(42)) self.assertAlmostEqual(float(FooStr('8')), 9.) + class Foo5: + def __float__(self): + return "" + self.assertRaises(TypeError, time.sleep, Foo5()) + def test_is_integer(self): self.assertFalse((1.1).is_integer()) self.assertTrue((1.).is_integer()) diff --git a/Objects/floatobject.c b/Objects/floatobject.c --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -220,6 +220,7 @@ if (fo == NULL) return -1; if (!PyFloat_Check(fo)) { + Py_DECREF(fo); PyErr_SetString(PyExc_TypeError, "nb_float should return float object"); return -1; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 15:10:50 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 06 Mar 2015 14:10:50 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_remove_redunda?= =?utf-8?q?nt_test?= Message-ID: <20150306141049.7573.10059@psf.io> https://hg.python.org/cpython/rev/72b08d351a9b changeset: 94883:72b08d351a9b branch: 3.4 parent: 94880:30925a3b2324 user: Benjamin Peterson date: Fri Mar 06 09:10:26 2015 -0500 summary: remove redundant test files: Lib/test/test_float.py | 5 ----- 1 files changed, 0 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -94,10 +94,6 @@ def test_floatconversion(self): # Make sure that calls to __float__() work properly - class Foo0: - def __float__(self): - return 42. - class Foo1(object): def __float__(self): return 42. @@ -123,7 +119,6 @@ def __float__(self): return float(str(self)) + 1 - self.assertAlmostEqual(float(Foo0()), 42.) self.assertAlmostEqual(float(Foo1()), 42.) self.assertAlmostEqual(float(Foo2()), 42.) self.assertAlmostEqual(float(Foo3(21)), 42.) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 15:10:51 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 06 Mar 2015 14:10:51 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <20150306141050.16655.64118@psf.io> https://hg.python.org/cpython/rev/b3d4fd17e96d changeset: 94884:b3d4fd17e96d parent: 94882:4d9594018edd parent: 94883:72b08d351a9b user: Benjamin Peterson date: Fri Mar 06 09:10:45 2015 -0500 summary: merge 3.4 files: Lib/test/test_float.py | 5 ----- 1 files changed, 0 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -94,10 +94,6 @@ def test_floatconversion(self): # Make sure that calls to __float__() work properly - class Foo0: - def __float__(self): - return 42. - class Foo1(object): def __float__(self): return 42. @@ -123,7 +119,6 @@ def __float__(self): return float(str(self)) + 1 - self.assertAlmostEqual(float(Foo0()), 42.) self.assertAlmostEqual(float(Foo1()), 42.) self.assertAlmostEqual(float(Foo2()), 42.) self.assertAlmostEqual(float(Foo3(21)), 42.) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 17:31:05 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 06 Mar 2015 16:31:05 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_Add_PEP_488=3A_elimination_of?= =?utf-8?q?_PYO_files?= Message-ID: <20150306162922.9672.39798@psf.io> https://hg.python.org/peps/rev/5ff20cda4c34 changeset: 5721:5ff20cda4c34 user: Brett Cannon date: Fri Mar 06 11:29:18 2015 -0500 summary: Add PEP 488: elimination of PYO files files: pep-0488.txt | 303 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 303 insertions(+), 0 deletions(-) diff --git a/pep-0488.txt b/pep-0488.txt new file mode 100644 --- /dev/null +++ b/pep-0488.txt @@ -0,0 +1,303 @@ +PEP: 488 +Title: Elimination of PYO files +Version: $Revision$ +Last-Modified: $Date$ +Author: Brett Cannon +Status: Draft +Type: Standards Track +Content-Type: text/x-rst +Created: 20-Feb-2015 +Post-History: + 2015-03-06 + +Abstract +======== + +This PEP proposes eliminating the concept of PYO files from Python. +To continue the support of the separation of bytecode files based on +their optimization level, this PEP proposes extending the PYC file +name to include the optimization level in bytecode repository +directory (i.e., the ``__pycache__`` directory). + + +Rationale +========= + +As of today, bytecode files come in two flavours: PYC and PYO. A PYC +file is the bytecode file generated and read from when no +optimization level is specified at interpreter startup (i.e., ``-O`` +is not specified). A PYO file represents the bytecode file that is +read/written when **any** optimization level is specified (i.e., when +``-O`` is specified, including ``-OO``). This means that while PYC +files clearly delineate the optimization level used when they were +generated -- namely no optimizations beyond the peepholer -- the same +is not true for PYO files. Put in terms of optimization levels and +the file extension: + + - 0: ``.pyc`` + - 1 (``-O``): ``.pyo`` + - 2 (``-OO``): ``.pyo`` + +The reuse of the ``.pyo`` file extension for both level 1 and 2 +optimizations means that there is no clear way to tell what +optimization level was used to generate the bytecode file. In terms +of reading PYO files, this can lead to an interpreter using a mixture +of optimization levels with its code if the user was not careful to +make sure all PYO files were generated using the same optimization +level (typically done by blindly deleting all PYO files and then +using the `compileall` module to compile all-new PYO files [1]_). +This issue is only compounded when people optimize Python code beyond +what the interpreter natively supports, e.g., using the astoptimizer +project [2]_. + +In terms of writing PYO files, the need to delete all PYO files +every time one either changes the optimization level they want to use +or are unsure of what optimization was used the last time PYO files +were generated leads to unnecessary file churn. The change proposed +by this PEP also allows for **all** optimization levels to be +pre-compiled for bytecode files ahead of time, something that is +currently impossible thanks to the reuse of the ``.pyo`` file +extension for multiple optimization levels. + +As for distributing bytecode-only modules, having to distribute both +``.pyc`` and ``.pyo`` files is unnecessary for the common use-case +of code obfuscation and smaller file deployments. + + +Proposal +======== + +To eliminate the ambiguity that PYO files present, this PEP proposes +eliminating the concept of PYO files and their accompanying ``.pyo`` +file extension. To allow for the optimization level to be unambiguous +as well as to avoid having to regenerate optimized bytecode files +needlessly in the `__pycache__` directory, the optimization level +used to generate a PYC file will be incorporated into the bytecode +file name. Currently bytecode file names are created by +``importlib.util.cache_from_source()``, approximately using the +following expression defined by PEP 3147 [3]_, [4]_, [5]_:: + + '{name}.{cache_tag}.pyc'.format(name=module_name, + cache_tag=sys.implementation.cache_tag) + +This PEP proposes to change the expression to:: + + '{name}.{cache_tag}.opt-{optimization}.pyc'.format( + name=module_name, + cache_tag=sys.implementation.cache_tag, + optimization=str(sys.flags.optimize)) + +The "opt-" prefix was chosen so as to provide a visual separator +from the cache tag. The placement of the optimization level after +the cache tag was chosen to preserve lexicographic sort order of +bytecode file names based on module name and cache tag which will +not vary for a single interpreter. The "opt-" prefix was chosen over +"o" so as to be somewhat self-documenting. The "opt-" prefix was +chosen over "O" so as to not have any confusion with "0" while being +so close to the interpreter version number. + +A period was chosen over a hyphen as a separator so as to distinguish +clearly that the optimization level is not part of the interpreter +version as specified by the cache tag. It also lends to the use of +the period in the file name to delineate semantically different +concepts. + +For example, the bytecode file name of ``importlib.cpython-35.pyc`` +would become ``importlib.cpython-35.opt-0.pyc``. If ``-OO`` had been +passed to the interpreter then instead of +``importlib.cpython-35.pyo`` the file name would be +``importlib.cpython-35.opt-2.pyc``. + + +Implementation +============== + +importlib +--------- + +As ``importlib.util.cache_from_source()`` is the API that exposes +bytecode file paths as while as being directly used by importlib, it +requires the most critical change. As of Python 3.4, the function's +signature is:: + + importlib.util.cache_from_source(path, debug_override=None) + +This PEP proposes changing the signature in Python 3.5 to:: + + importlib.util.cache_from_source(path, debug_override=None, *, optimization=None) + +The introduced ``optimization`` keyword-only parameter will control +what optimization level is specified in the file name. If the +argument is ``None`` then the current optimization level of the +interpreter will be assumed. Any argument given for ``optimization`` +will be passed to ``str()`` and must have ``str.isalnum()`` be true, +else ``ValueError`` will be raised (this prevents invalid characters +being used in the file name). If the empty string is passed in for +``optimization`` then the addition of the optimization will be +suppressed, reverting to the file name format which predates this +PEP. + +It is expected that beyond Python's own +0-2 optimization levels, third-party code will use a hash of +optimization names to specify the optimization level, e.g. +``hashlib.sha256(','.join(['dead code elimination', 'constant folding'])).hexdigest()``. +While this might lead to long file names, it is assumed that most +users never look at the contents of the __pycache__ directory and so +this won't be an issue. + +The ``debug_override`` parameter will be deprecated. As the parameter +expects a boolean, the integer value of the boolean will be used as +if it had been provided as the argument to ``optimization`` (a +``None`` argument will mean the same as for ``optimization``). A +deprecation warning will be raised when ``debug_override`` is given a +value other than ``None``, but there are no plans for the complete +removal of the parameter as this time (but removal will be no later +than Python 4). + +The various module attributes for importlib.machinery which relate to +bytecode file suffixes will be updated [7]_. The +``DEBUG_BYTECODE_SUFFIXES`` and ``OPTIMIZED_BYTECODE_SUFFIXES`` will +both be documented as deprecated and set to the same value as +``BYTECODE_SUFFIXES`` (removal of ``DEBUG_BYTECODE_SUFFIXES`` and +``OPTIMIZED_BYTECODE_SUFFIXES`` is not currently planned, but will be +not later than Python 4). + +All various finders and loaders will also be updated as necessary, +but updating the previous mentioned parts of importlib should be all +that is required. + + +Rest of the standard library +---------------------------- + +The various functions exposed by the ``py_compile`` and +``compileall`` functions will be updated as necessary to make sure +they follow the new bytecode file name semantics [6]_, [1]_. The CLI +for the ``compileall`` module will not be directly affected (the +``-b`` flag will be implicitly as it will no longer generate ``.pyo`` +files when ``-O`` is specified). + + +Compatibility Considerations +============================ + +Any code directly manipulating bytecode files from Python 3.2 on +will need to consider the impact of this change on their code (prior +to Python 3.2 -- including all of Python 2 -- there was no +__pycache__ which already necessitates bifurcating bytecode file +handling support). If code was setting the ``debug_override`` +argument to ``importlib.util.cache_from_source()`` then care will be +needed if they want the path to a bytecode file with an optimization +level of 2. Otherwise only code **not** using +``importlib.util.cache_from_source()`` will need updating. + +As for people who distribute bytecode-only modules (i.e., use a +bytecode file instead of a source file), they will have to choose +which optimization level they want their bytecode files to be since +distributing a ``.pyo`` file with a ``.pyc`` file will no longer be +of any use. Since people typically only distribute bytecode files for +code obfuscation purposes or smaller distribution size then only +having to distribute a single ``.pyc`` should actually be beneficial +to these use-cases. And since the magic number for bytecode files +changed in Python 3.5 to support PEP 465 there is no need to support +pre-existing ``.pyo`` files [8]_. + + +Rejected Ideas +============== + +N/A + + +Open Issues +=========== + +Formatting of the optimization level in the file name +----------------------------------------------------- + +Using the "opt-" prefix and placing the optimization level between +the cache tag and file extension is not critical. All options which +have been considered are: + +* ``importlib.cpython-35.opt-0.pyc`` +* ``importlib.cpython-35.opt0.pyc`` +* ``importlib.cpython-35.o0.pyc`` +* ``importlib.cpython-35.O0.pyc`` +* ``importlib.cpython-35.0.pyc`` +* ``importlib.cpython-35-O0.pyc`` +* ``importlib.O0.cpython-35.pyc`` +* ``importlib.o0.cpython-35.pyc`` +* ``importlib.0.cpython-35.pyc`` + +These were initially rejected either because they would change the +sort order of bytecode files, possible ambiguity with the cache tag, +or were not self-documenting enough. + + +Not specifying the optimization level when it is at 0 +----------------------------------------------------- + +It has been suggested that for the common case of when the +optimizations are at level 0 that the entire part of the file name +relating to the optimization level be left out. This would allow for +file names of ``.pyc`` files to go unchanged, potentially leading to +less backwards-compatibility issues (although Python 3.5 introduces a +new magic number for bytecode so all bytecode files will have to be +regenerated regardless of the outcome of this PEP). + +It would also allow a potentially redundant bit of information to be +left out of the file name if an implementation of Python did not +allow for optimizing bytecode. This would only occur, though, if the +interpreter didn't support ``-O`` **and** didn't implement the ast +module, else users could implement their own optimizations. + +Arguments against allow this special case is "explicit is better than +implicit" and "special cases aren't special enough to break the +rules". There are also currently no Python 3 interpreters that don't +support ``-O``, so a potential Python 3 implementation which doesn't +allow bytecode optimization is entirely theoretical at the moment. + + +References +========== + +.. [1] The compileall module + (https://docs.python.org/3/library/compileall.html#module-compileall) + +.. [2] The astoptimizer project + (https://pypi.python.org/pypi/astoptimizer) + +.. [3] ``importlib.util.cache_from_source()`` + (https://docs.python.org/3.5/library/importlib.html#importlib.util.cache_from_source) + +.. [4] Implementation of ``importlib.util.cache_from_source()`` from CPython 3.4.3rc1 + (https://hg.python.org/cpython/file/038297948389/Lib/importlib/_bootstrap.py#l437) + +.. [5] PEP 3147, PYC Repository Directories, Warsaw + (http://www.python.org/dev/peps/pep-3147) + +.. [6] The py_compile module + (https://docs.python.org/3/library/compileall.html#module-compileall) + +.. [7] The importlib.machinery module + (https://docs.python.org/3/library/importlib.html#module-importlib.machinery) + +.. [8] ``importlib.util.MAGIC_NUMBER`` + (https://docs.python.org/3/library/importlib.html#importlib.util.MAGIC_NUMBER) + + +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: https://hg.python.org/peps From python-checkins at python.org Fri Mar 6 19:18:06 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 06 Mar 2015 18:18:06 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_Explicitly_state_that_PEP_488?= =?utf-8?q?_doesn=27t_influence_import_performance?= Message-ID: <20150306181658.1779.26566@psf.io> https://hg.python.org/peps/rev/767472263e7c changeset: 5722:767472263e7c user: Brett Cannon date: Fri Mar 06 13:16:54 2015 -0500 summary: Explicitly state that PEP 488 doesn't influence import performance files: pep-0488.txt | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/pep-0488.txt b/pep-0488.txt --- a/pep-0488.txt +++ b/pep-0488.txt @@ -108,6 +108,15 @@ ``importlib.cpython-35.pyo`` the file name would be ``importlib.cpython-35.opt-2.pyc``. +It should be noted that this change in no way affects the performance +of import. Since the import system looks for a single bytecode file +based on the optimization level of the interpreter already and +generates a new bytecode file if it doesn't exist, the introduction +of potentially more bytecode files in the ``__pycache__`` directory +has no effect. The interpreter will continue to look for only a +single bytecode file based on the optimization level and thus no +increase in stat calls will occur. + Implementation ============== -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Fri Mar 6 21:34:31 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 06 Mar 2015 20:34:31 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIyODUz?= =?utf-8?q?=3A_Fixed_a_deadlock_when_use_multiprocessing=2EQueue_at_import?= =?utf-8?q?_time=2E?= Message-ID: <20150306203345.117225.18062@psf.io> https://hg.python.org/cpython/rev/069c13ca7a70 changeset: 94885:069c13ca7a70 branch: 2.7 parent: 94881:f31b91b6683a user: Serhiy Storchaka date: Fri Mar 06 22:17:25 2015 +0200 summary: Issue #22853: Fixed a deadlock when use multiprocessing.Queue at import time. Patch by Florian Finkernagel and Davin Potts. files: Lib/multiprocessing/queues.py | 10 ++++------ Lib/test/test_multiprocessing.py | 20 ++++++++++++++++++++ Misc/ACKS | 2 ++ Misc/NEWS | 3 +++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Lib/multiprocessing/queues.py b/Lib/multiprocessing/queues.py --- a/Lib/multiprocessing/queues.py +++ b/Lib/multiprocessing/queues.py @@ -44,10 +44,10 @@ from Queue import Empty, Full import _multiprocessing -from multiprocessing import Pipe -from multiprocessing.synchronize import Lock, BoundedSemaphore, Semaphore, Condition -from multiprocessing.util import debug, info, Finalize, register_after_fork -from multiprocessing.forking import assert_spawning +from . import Pipe +from .synchronize import Lock, BoundedSemaphore, Semaphore, Condition +from .util import debug, info, Finalize, register_after_fork, is_exiting +from .forking import assert_spawning # # Queue type using a pipe, buffer and thread @@ -229,8 +229,6 @@ @staticmethod def _feed(buffer, notempty, send, writelock, close): debug('starting thread to feed data to pipe') - from .util import is_exiting - nacquire = notempty.acquire nrelease = notempty.release nwait = notempty.wait diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -620,6 +620,26 @@ for p in workers: p.join() + def test_no_import_lock_contention(self): + with test_support.temp_cwd(): + module_name = 'imported_by_an_imported_module' + with open(module_name + '.py', 'w') as f: + f.write("""if 1: + import multiprocessing + + q = multiprocessing.Queue() + q.put('knock knock') + q.get(timeout=3) + q.close() + """) + + with test_support.DirsOnSysPath(os.getcwd()): + try: + __import__(module_name) + except Queue.Empty: + self.fail("Probable regression on import lock contention;" + " see Issue #22853") + # # # diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -411,6 +411,7 @@ Anastasia Filatova Tomer Filiba Jeffrey Finkelstein +Florian Finkernagel Russell Finn Dan Finnie Nils Fischbeck @@ -1073,6 +1074,7 @@ Iustin Pop Claudiu Popa John Popplewell +Davin Potts Guillaume Pratte Amrit Prem Paul Prescod diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,9 @@ Library ------- +- Issue #22853: Fixed a deadlock when use multiprocessing.Queue at import time. + Patch by Florian Finkernagel and Davin Potts. + - Issue #23476: In the ssl module, enable OpenSSL's X509_V_FLAG_TRUSTED_FIRST flag on certificate stores when it is available. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 22:35:36 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 06 Mar 2015 21:35:36 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2322853=3A_Added_regression_test_for_using_multip?= =?utf-8?q?rocessing=2EQueue_at_import?= Message-ID: <20150306213429.11177.49437@psf.io> https://hg.python.org/cpython/rev/dcd6d41f2c9a changeset: 94887:dcd6d41f2c9a parent: 94884:b3d4fd17e96d parent: 94886:cf12856bde17 user: Serhiy Storchaka date: Fri Mar 06 23:33:51 2015 +0200 summary: Issue #22853: Added regression test for using multiprocessing.Queue at import time. Patch by Davin Potts. files: Lib/test/_test_multiprocessing.py | 21 +++++++++++++++++++ 1 files changed, 21 insertions(+), 0 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -713,6 +713,27 @@ for p in workers: p.join() + def test_no_import_lock_contention(self): + with test.support.temp_cwd(): + module_name = 'imported_by_an_imported_module' + with open(module_name + '.py', 'w') as f: + f.write("""if 1: + import multiprocessing + + q = multiprocessing.Queue() + q.put('knock knock') + q.get(timeout=3) + q.close() + del q + """) + + with test.support.DirsOnSysPath(os.getcwd()): + try: + __import__(module_name) + except pyqueue.Empty: + self.fail("Probable regression on import lock contention;" + " see Issue #22853") + def test_timeout(self): q = multiprocessing.Queue() start = time.time() -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 22:35:36 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 06 Mar 2015 21:35:36 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIyODUz?= =?utf-8?q?=3A_Added_regression_test_for_using_multiprocessing=2EQueue_at_?= =?utf-8?q?import?= Message-ID: <20150306213429.1192.48864@psf.io> https://hg.python.org/cpython/rev/cf12856bde17 changeset: 94886:cf12856bde17 branch: 3.4 parent: 94883:72b08d351a9b user: Serhiy Storchaka date: Fri Mar 06 23:32:54 2015 +0200 summary: Issue #22853: Added regression test for using multiprocessing.Queue at import time. Patch by Davin Potts. files: Lib/test/_test_multiprocessing.py | 21 +++++++++++++++++++ 1 files changed, 21 insertions(+), 0 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -713,6 +713,27 @@ for p in workers: p.join() + def test_no_import_lock_contention(self): + with test.support.temp_cwd(): + module_name = 'imported_by_an_imported_module' + with open(module_name + '.py', 'w') as f: + f.write("""if 1: + import multiprocessing + + q = multiprocessing.Queue() + q.put('knock knock') + q.get(timeout=3) + q.close() + del q + """) + + with test.support.DirsOnSysPath(os.getcwd()): + try: + __import__(module_name) + except pyqueue.Empty: + self.fail("Probable regression on import lock contention;" + " see Issue #22853") + def test_timeout(self): q = multiprocessing.Queue() start = time.time() -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 23:47:33 2015 From: python-checkins at python.org (steve.dower) Date: Fri, 06 Mar 2015 22:47:33 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323524=3A_Replace_?= =?utf-8?q?=5FPyVerify=5Ffd_function_with_calling?= Message-ID: <20150306224733.19079.91487@psf.io> https://hg.python.org/cpython/rev/75aadb4450fd changeset: 94888:75aadb4450fd user: Steve Dower date: Fri Mar 06 14:47:02 2015 -0800 summary: Issue #23524: Replace _PyVerify_fd function with calling _set_thread_local_invalid_parameter_handler on every thread. files: Include/fileobject.h | 11 - Include/fileutils.h | 12 + Modules/_io/fileio.c | 2 +- Modules/posixmodule.c | 92 +------------- PC/invalid_parameter_handler.c | 22 +++ PCbuild/pythoncore.vcxproj | 11 +- PCbuild/pythoncore.vcxproj.filters | 3 + Python/fileutils.c | 108 ++++++++++++++++- Python/pystate.c | 11 + 9 files changed, 160 insertions(+), 112 deletions(-) diff --git a/Include/fileobject.h b/Include/fileobject.h --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -32,17 +32,6 @@ #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) PyFile_NewStdPrinter(int); PyAPI_DATA(PyTypeObject) PyStdPrinter_Type; - -#if defined _MSC_VER && _MSC_VER >= 1400 -/* A routine to check if a file descriptor is valid on Windows. Returns 0 - * and sets errno to EBADF if it isn't. This is to avoid Assertions - * from various functions in the Windows CRT beginning with - * Visual Studio 2005 - */ -int _PyVerify_fd(int fd); -#else -#define _PyVerify_fd(A) (1) /* dummy */ -#endif #endif /* Py_LIMITED_API */ /* A routine to check if a file descriptor can be select()-ed. */ diff --git a/Include/fileutils.h b/Include/fileutils.h --- a/Include/fileutils.h +++ b/Include/fileutils.h @@ -108,6 +108,18 @@ PyAPI_FUNC(int) _Py_set_blocking(int fd, int blocking); #endif /* !MS_WINDOWS */ +#if defined _MSC_VER && _MSC_VER >= 1400 +/* A routine to check if a file descriptor is valid on Windows. Returns 0 + * and sets errno to EBADF if it isn't. This is to avoid Assertions + * from various functions in the Windows CRT beginning with + * Visual Studio 2005 + */ +int _PyVerify_fd(int fd); + +#else +#define _PyVerify_fd(A) (1) /* dummy */ +#endif + #endif /* Py_LIMITED_API */ #ifdef __cplusplus diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -182,7 +182,7 @@ { #if defined(HAVE_FSTAT) || defined(MS_WINDOWS) struct _Py_stat_struct buf; - if (!_PyVerify_fd(fd) || (_Py_fstat(fd, &buf) < 0 && errno == EBADF)) { + if (_Py_fstat(fd, &buf) < 0 && errno == EBADF) { PyObject *exc; char *msg = strerror(EBADF); exc = PyObject_CallFunction(PyExc_OSError, "(is)", diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1051,99 +1051,16 @@ } -#if defined _MSC_VER && _MSC_VER >= 1400 -/* Microsoft CRT in VS2005 and higher will verify that a filehandle is - * valid and raise an assertion if it isn't. - * Normally, an invalid fd is likely to be a C program error and therefore - * an assertion can be useful, but it does contradict the POSIX standard - * which for write(2) states: - * "Otherwise, -1 shall be returned and errno set to indicate the error." - * "[EBADF] The fildes argument is not a valid file descriptor open for - * writing." - * Furthermore, python allows the user to enter any old integer - * as a fd and should merely raise a python exception on error. - * The Microsoft CRT doesn't provide an official way to check for the - * validity of a file descriptor, but we can emulate its internal behaviour - * by using the exported __pinfo data member and knowledge of the - * internal structures involved. - * The structures below must be updated for each version of visual studio - * according to the file internal.h in the CRT source, until MS comes - * up with a less hacky way to do this. - * (all of this is to avoid globally modifying the CRT behaviour using - * _set_invalid_parameter_handler() and _CrtSetReportMode()) +#if defined _MSC_VER && _MSC_VER >= 1400 && _MSC_VER < 1900 +/* Legacy implementation of _PyVerify_fd_dup2 while transitioning to + * MSVC 14.0. This should eventually be removed. (issue23524) */ -/* The actual size of the structure is determined at runtime. - * Only the first items must be present. - */ - -#if _MSC_VER >= 1900 - -typedef struct { - CRITICAL_SECTION lock; - intptr_t osfhnd; - __int64 startpos; - char osfile; -} my_ioinfo; - -#define IOINFO_L2E 6 -#define IOINFO_ARRAYS 128 - -#else - -typedef struct { - intptr_t osfhnd; - char osfile; -} my_ioinfo; - #define IOINFO_L2E 5 #define IOINFO_ARRAYS 64 - -#endif - -extern __declspec(dllimport) char * __pioinfo[]; #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) #define _NHANDLE_ (IOINFO_ARRAYS * IOINFO_ARRAY_ELTS) -#define FOPEN 0x01 #define _NO_CONSOLE_FILENO (intptr_t)-2 -/* This function emulates what the windows CRT does to validate file handles */ -int -_PyVerify_fd(int fd) -{ - const int i1 = fd >> IOINFO_L2E; - const int i2 = fd & ((1 << IOINFO_L2E) - 1); - - static size_t sizeof_ioinfo = 0; - - /* Determine the actual size of the ioinfo structure, - * as used by the CRT loaded in memory - */ - if (sizeof_ioinfo == 0 && __pioinfo[0] != NULL) { - sizeof_ioinfo = _msize(__pioinfo[0]) / IOINFO_ARRAY_ELTS; - } - if (sizeof_ioinfo == 0) { - /* This should not happen... */ - goto fail; - } - - /* See that it isn't a special CLEAR fileno */ - if (fd != _NO_CONSOLE_FILENO) { - /* Microsoft CRT would check that 0<=fd<_nhandle but we can't do that. Instead - * we check pointer validity and other info - */ - if (0 <= i1 && i1 < IOINFO_ARRAYS && __pioinfo[i1] != NULL) { - /* finally, check that the file is open */ - my_ioinfo* info = (my_ioinfo*)(__pioinfo[i1] + i2 * sizeof_ioinfo); - if (info->osfile & FOPEN) { - return 1; - } - } - } - fail: - errno = EBADF; - return 0; -} - /* the special case of checking dup2. The target fd must be in a sensible range */ static int _PyVerify_fd_dup2(int fd1, int fd2) @@ -1158,8 +1075,7 @@ return 0; } #else -/* dummy version. _PyVerify_fd() is already defined in fileobject.h */ -#define _PyVerify_fd_dup2(A, B) (1) +#define _PyVerify_fd_dup2(fd1, fd2) (_PyVerify_fd(fd1) && (fd2) >= 0) #endif #ifdef MS_WINDOWS diff --git a/PC/invalid_parameter_handler.c b/PC/invalid_parameter_handler.c new file mode 100644 --- /dev/null +++ b/PC/invalid_parameter_handler.c @@ -0,0 +1,22 @@ +#ifdef _MSC_VER + +#include + +#if _MSC_VER >= 1900 +/* pyconfig.h uses this function in the _Py_BEGIN/END_SUPPRESS_IPH + * macros. It does not need to be defined when building using MSVC + * earlier than 14.0 (_MSC_VER == 1900). + */ + +static void __cdecl _silent_invalid_parameter_handler( + wchar_t const* expression, + wchar_t const* function, + wchar_t const* file, + unsigned int line, + uintptr_t pReserved) { } + +void *_Py_silent_invalid_parameter_handler = + (void*)_silent_invalid_parameter_handler; +#endif + +#endif diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -333,6 +333,7 @@ + @@ -394,25 +395,21 @@ - - - - - + + + $([System.IO.File]::ReadAllText('$(IntDir)hgbranch.txt').Trim()) $([System.IO.File]::ReadAllText('$(IntDir)hgversion.txt').Trim()) $([System.IO.File]::ReadAllText('$(IntDir)hgtag.txt').Trim()) - HGVERSION="$(HgVersion)";HGTAG="$(HgTag)";HGBRANCH="$(HgBranch)";%(PreprocessorDefinitions) - diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -959,6 +959,9 @@ Modules + + PC + diff --git a/Python/fileutils.c b/Python/fileutils.c --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -3,6 +3,7 @@ #include #ifdef MS_WINDOWS +# include # include #endif @@ -636,14 +637,10 @@ else h = (HANDLE)_get_osfhandle(fd); - /* Protocol violation: we explicitly clear errno, instead of - setting it to a POSIX error. Callers should use GetLastError. */ errno = 0; if (h == INVALID_HANDLE_VALUE) { - /* This is really a C library error (invalid file handle). - We set the Win32 error to the closes one matching. */ - SetLastError(ERROR_INVALID_HANDLE); + errno = EBADF; return -1; } memset(result, 0, sizeof(*result)); @@ -652,6 +649,7 @@ if (type == FILE_TYPE_UNKNOWN) { DWORD error = GetLastError(); if (error != 0) { + errno = EINVAL; return -1; } /* else: valid but unknown file */ @@ -666,6 +664,7 @@ } if (!GetFileInformationByHandle(h, &info)) { + errno = EINVAL; return -1; } @@ -1267,3 +1266,102 @@ } #endif +#ifdef _MSC_VER +#if _MSC_VER >= 1900 + +/* This function lets the Windows CRT validate the file handle without + terminating the process if it's invalid. */ +int +_PyVerify_fd(int fd) +{ + intptr_t osh; + /* Fast check for the only condition we know */ + if (fd < 0) { + _set_errno(EBADF); + return 0; + } + osh = _get_osfhandle(fd); + return osh != (intptr_t)-1; +} + +#elif _MSC_VER >= 1400 +/* Legacy implementation of _PyVerify_fd while transitioning to + * MSVC 14.0. This should eventually be removed. (issue23524) + */ + +/* Microsoft CRT in VS2005 and higher will verify that a filehandle is + * valid and raise an assertion if it isn't. + * Normally, an invalid fd is likely to be a C program error and therefore + * an assertion can be useful, but it does contradict the POSIX standard + * which for write(2) states: + * "Otherwise, -1 shall be returned and errno set to indicate the error." + * "[EBADF] The fildes argument is not a valid file descriptor open for + * writing." + * Furthermore, python allows the user to enter any old integer + * as a fd and should merely raise a python exception on error. + * The Microsoft CRT doesn't provide an official way to check for the + * validity of a file descriptor, but we can emulate its internal behaviour + * by using the exported __pinfo data member and knowledge of the + * internal structures involved. + * The structures below must be updated for each version of visual studio + * according to the file internal.h in the CRT source, until MS comes + * up with a less hacky way to do this. + * (all of this is to avoid globally modifying the CRT behaviour using + * _set_invalid_parameter_handler() and _CrtSetReportMode()) + */ +/* The actual size of the structure is determined at runtime. + * Only the first items must be present. + */ +typedef struct { + intptr_t osfhnd; + char osfile; +} my_ioinfo; + +extern __declspec(dllimport) char * __pioinfo[]; +#define IOINFO_L2E 5 +#define IOINFO_ARRAYS 64 +#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) +#define _NHANDLE_ (IOINFO_ARRAYS * IOINFO_ARRAY_ELTS) +#define FOPEN 0x01 +#define _NO_CONSOLE_FILENO (intptr_t)-2 + +/* This function emulates what the windows CRT does to validate file handles */ +int +_PyVerify_fd(int fd) +{ + const int i1 = fd >> IOINFO_L2E; + const int i2 = fd & ((1 << IOINFO_L2E) - 1); + + static size_t sizeof_ioinfo = 0; + + /* Determine the actual size of the ioinfo structure, + * as used by the CRT loaded in memory + */ + if (sizeof_ioinfo == 0 && __pioinfo[0] != NULL) { + sizeof_ioinfo = _msize(__pioinfo[0]) / IOINFO_ARRAY_ELTS; + } + if (sizeof_ioinfo == 0) { + /* This should not happen... */ + goto fail; + } + + /* See that it isn't a special CLEAR fileno */ + if (fd != _NO_CONSOLE_FILENO) { + /* Microsoft CRT would check that 0<=fd<_nhandle but we can't do that. Instead + * we check pointer validity and other info + */ + if (0 <= i1 && i1 < IOINFO_ARRAYS && __pioinfo[i1] != NULL) { + /* finally, check that the file is open */ + my_ioinfo* info = (my_ioinfo*)(__pioinfo[i1] + i2 * sizeof_ioinfo); + if (info->osfile & FOPEN) { + return 1; + } + } + } + fail: + errno = EBADF; + return 0; +} + +#endif /* _MSC_VER >= 1900 || _MSC_VER >= 1400 */ +#endif /* defined _MSC_VER */ diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -22,6 +22,12 @@ #endif #endif +#if defined _MSC_VER && _MSC_VER >= 1900 +/* Issue #23524: Temporary fix to disable termination due to invalid parameters */ +PyAPI_DATA(void*) _Py_silent_invalid_parameter_handler; +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -222,6 +228,11 @@ tstate->next->prev = tstate; interp->tstate_head = tstate; HEAD_UNLOCK(); + +#if defined _MSC_VER && _MSC_VER >= 1900 + /* Issue #23524: Temporary fix to disable termination due to invalid parameters */ + _set_thread_local_invalid_parameter_handler((_invalid_parameter_handler)_Py_silent_invalid_parameter_handler); +#endif } return tstate; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 6 23:53:55 2015 From: python-checkins at python.org (victor.stinner) Date: Fri, 06 Mar 2015 22:53:55 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323571=3A_PyObject?= =?utf-8?b?X0NhbGwoKSwgUHlDRnVuY3Rpb25fQ2FsbCgpIGFuZCBjYWxsX2Z1bmN0aW9u?= =?utf-8?b?KCkgbm93?= Message-ID: <20150306225339.11173.78760@psf.io> https://hg.python.org/cpython/rev/97ef38236dc1 changeset: 94889:97ef38236dc1 user: Victor Stinner date: Fri Mar 06 23:35:27 2015 +0100 summary: Issue #23571: PyObject_Call(), PyCFunction_Call() and call_function() now raise a SystemError if a function returns a result and raises an exception. The SystemError is chained to the previous exception. Refactor also PyObject_Call() and PyCFunction_Call() to make them more readable. Remove some checks which became useless (duplicate checks). Change reviewed by Serhiy Storchaka. files: Include/abstract.h | 5 + Misc/NEWS | 4 + Modules/_io/bufferedio.c | 4 - Modules/_sqlite/cursor.c | 4 - Objects/abstract.c | 73 +++++++++++++----- Objects/methodobject.c | 103 +++++++++++++------------- Python/ceval.c | 30 ++---- 7 files changed, 126 insertions(+), 97 deletions(-) diff --git a/Include/abstract.h b/Include/abstract.h --- a/Include/abstract.h +++ b/Include/abstract.h @@ -266,6 +266,11 @@ PyAPI_FUNC(PyObject *) PyObject_Call(PyObject *callable_object, PyObject *args, PyObject *kw); +#ifndef Py_LIMITED_API + PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *obj, + const char *func_name); +#endif + /* Call a callable Python object, callable_object, with arguments and keywords arguments. The 'args' argument can not be diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #23571: PyObject_Call() and PyCFunction_Call() now raise a SystemError + if a function returns a result and raises an exception. The SystemError is + chained to the previous exception. + Library ------- diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -680,11 +680,7 @@ _set_BlockingIOError(char *msg, Py_ssize_t written) { PyObject *err; -#ifdef Py_DEBUG - /* in debug mode, PyEval_EvalFrameEx() fails with an assertion error - if an exception is set when it is called */ PyErr_Clear(); -#endif err = PyObject_CallFunction(PyExc_BlockingIOError, "isn", errno, msg, written); if (err) diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -334,11 +334,7 @@ if (self->connection->text_factory == (PyObject*)&PyUnicode_Type) { converted = PyUnicode_FromStringAndSize(val_str, nbytes); if (!converted) { -#ifdef Py_DEBUG - /* in debug mode, type_call() fails with an assertion - error if an exception is set when it is called */ PyErr_Clear(); -#endif colname = sqlite3_column_name(self->statement->st, i); if (!colname) { colname = ""; diff --git a/Objects/abstract.c b/Objects/abstract.c --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2073,37 +2073,70 @@ return PyEval_CallObjectWithKeywords(o, a, NULL); } +PyObject* +_Py_CheckFunctionResult(PyObject *result, const char *func_name) +{ + int err_occurred = (PyErr_Occurred() != NULL); + +#ifdef NDEBUG + /* In debug mode: abort() with an assertion error. Use two different + assertions, so if an assertion fails, it's possible to know + if result was set or not and if an exception was raised or not. */ + if (result != NULL) + assert(!err_occurred); + else + assert(err_occurred); +#endif + + if (result == NULL) { + if (!err_occurred) { + PyErr_Format(PyExc_SystemError, + "NULL result without error in %s", func_name); + return NULL; + } + } + else { + if (err_occurred) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + + Py_DECREF(result); + + PyErr_Format(PyExc_SystemError, + "result with error in %s", func_name); + _PyErr_ChainExceptions(exc, val, tb); + return NULL; + } + } + return result; +} + PyObject * PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) { ternaryfunc call; + PyObject *result; /* PyObject_Call() must not be called with an exception set, because it may clear it (directly or indirectly) and so the caller looses its exception */ assert(!PyErr_Occurred()); - if ((call = func->ob_type->tp_call) != NULL) { - PyObject *result; - if (Py_EnterRecursiveCall(" while calling a Python object")) - return NULL; - result = (*call)(func, arg, kw); - Py_LeaveRecursiveCall(); -#ifdef NDEBUG - if (result == NULL && !PyErr_Occurred()) { - PyErr_SetString( - PyExc_SystemError, - "NULL result without error in PyObject_Call"); - } -#else - assert((result != NULL && !PyErr_Occurred()) - || (result == NULL && PyErr_Occurred())); -#endif - return result; + call = func->ob_type->tp_call; + if (call == NULL) { + PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", + func->ob_type->tp_name); + return NULL; } - PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", - func->ob_type->tp_name); - return NULL; + + if (Py_EnterRecursiveCall(" while calling a Python object")) + return NULL; + + result = (*call)(func, arg, kw); + + Py_LeaveRecursiveCall(); + + return _Py_CheckFunctionResult(result, "PyObject_Call"); } static PyObject* diff --git a/Objects/methodobject.c b/Objects/methodobject.c --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -78,68 +78,71 @@ } PyObject * -PyCFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) +PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) { -#define CHECK_RESULT(res) assert(res != NULL || PyErr_Occurred()) - PyCFunctionObject* f = (PyCFunctionObject*)func; PyCFunction meth = PyCFunction_GET_FUNCTION(func); PyObject *self = PyCFunction_GET_SELF(func); - PyObject *res; + PyObject *arg, *res; Py_ssize_t size; + int flags; - switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)) { - case METH_VARARGS: - if (kw == NULL || PyDict_Size(kw) == 0) { - res = (*meth)(self, arg); - CHECK_RESULT(res); - return res; - } - break; - case METH_VARARGS | METH_KEYWORDS: - res = (*(PyCFunctionWithKeywords)meth)(self, arg, kw); - CHECK_RESULT(res); - return res; - case METH_NOARGS: - if (kw == NULL || PyDict_Size(kw) == 0) { - size = PyTuple_GET_SIZE(arg); - if (size == 0) { - res = (*meth)(self, NULL); - CHECK_RESULT(res); - return res; - } - PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%zd given)", - f->m_ml->ml_name, size); + /* PyCFunction_Call() must not be called with an exception set, + because it may clear it (directly or indirectly) and so the + caller looses its exception */ + assert(!PyErr_Occurred()); + + flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + + if (flags == (METH_VARARGS | METH_KEYWORDS)) { + res = (*(PyCFunctionWithKeywords)meth)(self, args, kwds); + } + else { + if (kwds != NULL && PyDict_Size(kwds) != 0) { + PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", + f->m_ml->ml_name); return NULL; } - break; - case METH_O: - if (kw == NULL || PyDict_Size(kw) == 0) { - size = PyTuple_GET_SIZE(arg); - if (size == 1) { - res = (*meth)(self, PyTuple_GET_ITEM(arg, 0)); - CHECK_RESULT(res); - return res; + + switch (flags) { + case METH_VARARGS: + res = (*meth)(self, args); + break; + + case METH_NOARGS: + size = PyTuple_GET_SIZE(args); + if (size != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", + f->m_ml->ml_name, size); + return NULL; } - PyErr_Format(PyExc_TypeError, - "%.200s() takes exactly one argument (%zd given)", - f->m_ml->ml_name, size); + + res = (*meth)(self, NULL); + break; + + case METH_O: + size = PyTuple_GET_SIZE(args); + if (size != 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + f->m_ml->ml_name, size); + return NULL; + } + + arg = PyTuple_GET_ITEM(args, 0); + res = (*meth)(self, arg); + break; + + default: + PyErr_SetString(PyExc_SystemError, + "Bad call flags in PyCFunction_Call. " + "METH_OLDARGS is no longer supported!"); return NULL; } - break; - default: - PyErr_SetString(PyExc_SystemError, "Bad call flags in " - "PyCFunction_Call. METH_OLDARGS is no " - "longer supported!"); + } - return NULL; - } - PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - f->m_ml->ml_name); - return NULL; - -#undef CHECK_RESULT + return _Py_CheckFunctionResult(res, "PyCFunction_Call"); } /* Methods (the standard built-in methods, that is) */ diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3192,8 +3192,7 @@ if (why != WHY_RETURN) retval = NULL; - assert((retval != NULL && !PyErr_Occurred()) - || (retval == NULL && PyErr_Occurred())); + assert((retval != NULL) ^ (PyErr_Occurred() != NULL)); fast_yield: if (co->co_flags & CO_GENERATOR) { @@ -3254,7 +3253,7 @@ f->f_executing = 0; tstate->frame = f->f_back; - return retval; + return _Py_CheckFunctionResult(retval, "PyEval_EvalFrameEx"); } static void @@ -4119,13 +4118,6 @@ { PyObject *result; -#ifdef Py_DEBUG - /* PyEval_CallObjectWithKeywords() must not be called with an exception - set, because it may clear it (directly or indirectly) - and so the caller looses its exception */ - assert(!PyErr_Occurred()); -#endif - if (arg == NULL) { arg = PyTuple_New(0); if (arg == NULL) @@ -4149,8 +4141,6 @@ result = PyObject_Call(func, arg, kw); Py_DECREF(arg); - assert((result != NULL && !PyErr_Occurred()) - || (result == NULL && PyErr_Occurred())); return result; } @@ -4253,11 +4243,15 @@ PyObject *self = PyCFunction_GET_SELF(func); if (flags & METH_NOARGS && na == 0) { C_TRACE(x, (*meth)(self,NULL)); + + x = _Py_CheckFunctionResult(x, "call_function"); } else if (flags & METH_O && na == 1) { PyObject *arg = EXT_POP(*pp_stack); C_TRACE(x, (*meth)(self,arg)); Py_DECREF(arg); + + x = _Py_CheckFunctionResult(x, "call_function"); } else { err_args(func, flags, na); @@ -4277,7 +4271,8 @@ x = NULL; } } - } else { + } + else { if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { /* optimize access to bound methods */ PyObject *self = PyMethod_GET_SELF(func); @@ -4299,9 +4294,9 @@ x = do_call(func, pp_stack, na, nk); READ_TIMESTAMP(*pintr1); Py_DECREF(func); + + assert((x != NULL) ^ (PyErr_Occurred() != NULL)); } - assert((x != NULL && !PyErr_Occurred()) - || (x == NULL && PyErr_Occurred())); /* Clear the stack of the function object. Also removes the arguments in case they weren't consumed already @@ -4313,8 +4308,7 @@ PCALL(PCALL_POP); } - assert((x != NULL && !PyErr_Occurred()) - || (x == NULL && PyErr_Occurred())); + assert((x != NULL) ^ (PyErr_Occurred() != NULL)); return x; } @@ -4601,8 +4595,6 @@ Py_XDECREF(callargs); Py_XDECREF(kwdict); Py_XDECREF(stararg); - assert((result != NULL && !PyErr_Occurred()) - || (result == NULL && PyErr_Occurred())); return result; } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 7 10:52:38 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 07 Mar 2015 09:52:38 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2321793=3A_BaseHTTP?= =?utf-8?q?RequestHandler_again_logs_response_code_as_numeric=2C?= Message-ID: <20150307095238.11193.85885@psf.io> https://hg.python.org/cpython/rev/ad64b6a7c0e2 changeset: 94890:ad64b6a7c0e2 user: Serhiy Storchaka date: Sat Mar 07 11:51:37 2015 +0200 summary: Issue #21793: BaseHTTPRequestHandler again logs response code as numeric, not as stringified enum. Patch by Demian Brecht. files: Lib/http/server.py | 3 +- Lib/test/test_httpservers.py | 140 +++++++++++++++------- Misc/NEWS | 3 + 3 files changed, 97 insertions(+), 49 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -517,7 +517,8 @@ This is called by send_response(). """ - + if isinstance(code, HTTPStatus): + code = code.value self.log_message('"%s" %s %s', self.requestline, str(code), str(size)) 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 @@ -6,7 +6,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer, \ SimpleHTTPRequestHandler, CGIHTTPRequestHandler -from http import server +from http import server, HTTPStatus import os import sys @@ -79,13 +79,13 @@ default_request_version = 'HTTP/1.1' def do_TEST(self): - self.send_response(204) + self.send_response(HTTPStatus.NO_CONTENT) self.send_header('Content-Type', 'text/html') self.send_header('Connection', 'close') self.end_headers() def do_KEEP(self): - self.send_response(204) + self.send_response(HTTPStatus.NO_CONTENT) self.send_header('Content-Type', 'text/html') self.send_header('Connection', 'keep-alive') self.end_headers() @@ -94,7 +94,7 @@ self.send_error(999) def do_NOTFOUND(self): - self.send_error(404) + self.send_error(HTTPStatus.NOT_FOUND) def do_EXPLAINERROR(self): self.send_error(999, "Short Message", @@ -122,35 +122,35 @@ def test_command(self): self.con.request('GET', '/') res = self.con.getresponse() - self.assertEqual(res.status, 501) + self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) def test_request_line_trimming(self): self.con._http_vsn_str = 'HTTP/1.1\n' self.con.putrequest('XYZBOGUS', '/') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, 501) + self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) def test_version_bogus(self): self.con._http_vsn_str = 'FUBAR' self.con.putrequest('GET', '/') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, 400) + self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) def test_version_digits(self): self.con._http_vsn_str = 'HTTP/9.9.9' self.con.putrequest('GET', '/') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, 400) + self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) def test_version_none_get(self): self.con._http_vsn_str = '' self.con.putrequest('GET', '/') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, 501) + self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) def test_version_none(self): # Test that a valid method is rejected when not HTTP/1.x @@ -158,7 +158,7 @@ self.con.putrequest('CUSTOM', '/') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, 400) + self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) def test_version_invalid(self): self.con._http_vsn = 99 @@ -166,21 +166,21 @@ self.con.putrequest('GET', '/') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, 505) + self.assertEqual(res.status, HTTPStatus.HTTP_VERSION_NOT_SUPPORTED) def test_send_blank(self): self.con._http_vsn_str = '' self.con.putrequest('', '') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, 400) + self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) def test_header_close(self): self.con.putrequest('GET', '/') self.con.putheader('Connection', 'close') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, 501) + self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) def test_head_keep_alive(self): self.con._http_vsn_str = 'HTTP/1.1' @@ -188,12 +188,12 @@ self.con.putheader('Connection', 'keep-alive') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, 501) + self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) def test_handler(self): self.con.request('TEST', '/') res = self.con.getresponse() - self.assertEqual(res.status, 204) + self.assertEqual(res.status, HTTPStatus.NO_CONTENT) def test_return_header_keep_alive(self): self.con.request('KEEP', '/') @@ -230,11 +230,48 @@ # Issue #16088: standard error responses should have a content-length self.con.request('NOTFOUND', '/') res = self.con.getresponse() - self.assertEqual(res.status, 404) + self.assertEqual(res.status, HTTPStatus.NOT_FOUND) + data = res.read() self.assertEqual(int(res.getheader('Content-Length')), len(data)) +class RequestHandlerLoggingTestCase(BaseTestCase): + class request_handler(BaseHTTPRequestHandler): + protocol_version = 'HTTP/1.1' + default_request_version = 'HTTP/1.1' + + def do_GET(self): + self.send_response(HTTPStatus.OK) + self.end_headers() + + def do_ERROR(self): + self.send_error(HTTPStatus.NOT_FOUND, 'File not found') + + def test_get(self): + self.con = http.client.HTTPConnection(self.HOST, self.PORT) + self.con.connect() + + with support.captured_stderr() as err: + self.con.request('GET', '/') + self.con.getresponse() + + self.assertTrue( + err.getvalue().endswith('"GET / HTTP/1.1" 200 -\n')) + + def test_err(self): + self.con = http.client.HTTPConnection(self.HOST, self.PORT) + self.con.connect() + + with support.captured_stderr() as err: + self.con.request('ERROR', '/') + self.con.getresponse() + + lines = err.getvalue().split('\n') + self.assertTrue(lines[0].endswith('code 404, message File not found')) + self.assertTrue(lines[1].endswith('"ERROR / HTTP/1.1" 404 -')) + + class SimpleHTTPServerTestCase(BaseTestCase): class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler): pass @@ -285,52 +322,52 @@ if name != 'test': # Ignore a filename created in setUp(). filename = name break - body = self.check_status_and_reason(response, 200) + body = self.check_status_and_reason(response, HTTPStatus.OK) quotedname = urllib.parse.quote(filename, errors='surrogatepass') self.assertIn(('href="%s"' % quotedname) .encode(enc, 'surrogateescape'), body) self.assertIn(('>%s<' % html.escape(filename)) .encode(enc, 'surrogateescape'), body) response = self.request(self.tempdir_name + '/' + quotedname) - self.check_status_and_reason(response, 200, + self.check_status_and_reason(response, HTTPStatus.OK, data=support.TESTFN_UNDECODABLE) def test_get(self): #constructs the path relative to the root directory of the HTTPServer response = self.request(self.tempdir_name + '/test') - self.check_status_and_reason(response, 200, data=self.data) + self.check_status_and_reason(response, HTTPStatus.OK, data=self.data) # check for trailing "/" which should return 404. See Issue17324 response = self.request(self.tempdir_name + '/test/') - self.check_status_and_reason(response, 404) + self.check_status_and_reason(response, HTTPStatus.NOT_FOUND) response = self.request(self.tempdir_name + '/') - self.check_status_and_reason(response, 200) + self.check_status_and_reason(response, HTTPStatus.OK) response = self.request(self.tempdir_name) - self.check_status_and_reason(response, 301) + self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) response = self.request(self.tempdir_name + '/?hi=2') - self.check_status_and_reason(response, 200) + self.check_status_and_reason(response, HTTPStatus.OK) response = self.request(self.tempdir_name + '?hi=1') - self.check_status_and_reason(response, 301) + self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) self.assertEqual(response.getheader("Location"), self.tempdir_name + "/?hi=1") response = self.request('/ThisDoesNotExist') - self.check_status_and_reason(response, 404) + self.check_status_and_reason(response, HTTPStatus.NOT_FOUND) response = self.request('/' + 'ThisDoesNotExist' + '/') - self.check_status_and_reason(response, 404) + self.check_status_and_reason(response, HTTPStatus.NOT_FOUND) with open(os.path.join(self.tempdir_name, 'index.html'), 'w') as f: response = self.request('/' + self.tempdir_name + '/') - self.check_status_and_reason(response, 200) + self.check_status_and_reason(response, HTTPStatus.OK) # chmod() doesn't work as expected on Windows, and filesystem # permissions are ignored by root on Unix. if os.name == 'posix' and os.geteuid() != 0: os.chmod(self.tempdir, 0) response = self.request(self.tempdir_name + '/') - self.check_status_and_reason(response, 404) + self.check_status_and_reason(response, HTTPStatus.NOT_FOUND) os.chmod(self.tempdir, 0o755) def test_head(self): response = self.request( self.tempdir_name + '/test', method='HEAD') - self.check_status_and_reason(response, 200) + self.check_status_and_reason(response, HTTPStatus.OK) self.assertEqual(response.getheader('content-length'), str(len(self.data))) self.assertEqual(response.getheader('content-type'), @@ -338,12 +375,12 @@ def test_invalid_requests(self): response = self.request('/', method='FOO') - self.check_status_and_reason(response, 501) + self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED) # requests must be case sensitive,so this should fail too response = self.request('/', method='custom') - self.check_status_and_reason(response, 501) + self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED) response = self.request('/', method='GETs') - self.check_status_and_reason(response, 501) + self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED) cgi_file1 = """\ @@ -490,12 +527,13 @@ def test_headers_and_content(self): res = self.request('/cgi-bin/file1.py') - self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), - (res.read(), res.getheader('Content-type'), res.status)) + self.assertEqual( + (res.read(), res.getheader('Content-type'), res.status), + (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK)) def test_issue19435(self): res = self.request('///////////nocgi.py/../cgi-bin/nothere.sh') - self.assertEqual(res.status, 404) + self.assertEqual(res.status, HTTPStatus.NOT_FOUND) def test_post(self): params = urllib.parse.urlencode( @@ -508,38 +546,43 @@ def test_invaliduri(self): res = self.request('/cgi-bin/invalid') res.read() - self.assertEqual(res.status, 404) + self.assertEqual(res.status, HTTPStatus.NOT_FOUND) def test_authorization(self): headers = {b'Authorization' : b'Basic ' + base64.b64encode(b'username:pass')} res = self.request('/cgi-bin/file1.py', 'GET', headers=headers) - self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), - (res.read(), res.getheader('Content-type'), res.status)) + self.assertEqual( + (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), + (res.read(), res.getheader('Content-type'), res.status)) def test_no_leading_slash(self): # http://bugs.python.org/issue2254 res = self.request('cgi-bin/file1.py') - self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), - (res.read(), res.getheader('Content-type'), res.status)) + self.assertEqual( + (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), + (res.read(), res.getheader('Content-type'), res.status)) def test_os_environ_is_not_altered(self): signature = "Test CGI Server" os.environ['SERVER_SOFTWARE'] = signature res = self.request('/cgi-bin/file1.py') - self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), - (res.read(), res.getheader('Content-type'), res.status)) + self.assertEqual( + (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), + (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)) + self.assertEqual( + (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), + (res.read(), res.getheader('Content-type'), res.status)) def test_nested_cgi_path_issue21323(self): res = self.request('/cgi-bin/child-dir/file3.py') - self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200), - (res.read(), res.getheader('Content-type'), res.status)) + self.assertEqual( + (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), + (res.read(), res.getheader('Content-type'), res.status)) class SocketlessRequestHandler(SimpleHTTPRequestHandler): @@ -549,7 +592,7 @@ def do_GET(self): self.get_called = True - self.send_response(200) + self.send_response(HTTPStatus.OK) self.send_header('Content-Type', 'text/html') self.end_headers() self.wfile.write(b'Data\r\n') @@ -559,7 +602,7 @@ class RejectingSocketlessRequestHandler(SocketlessRequestHandler): def handle_expect_100(self): - self.send_error(417) + self.send_error(HTTPStatus.EXPECTATION_FAILED) return False @@ -816,6 +859,7 @@ cwd = os.getcwd() try: support.run_unittest( + RequestHandlerLoggingTestCase, BaseHTTPRequestHandlerTestCase, BaseHTTPServerTestCase, SimpleHTTPServerTestCase, diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -17,6 +17,9 @@ Library ------- +- Issue #21793: BaseHTTPRequestHandler again logs response code as numeric, + not as stringified enum. Patch by Demian Brecht. + - Issue #23476: In the ssl module, enable OpenSSL's X509_V_FLAG_TRUSTED_FIRST flag on certificate stores when it is available. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 7 15:35:03 2015 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 07 Mar 2015 14:35:03 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_replace_Amazon?= =?utf-8?q?_links_in_the_documentation_=28closes_=2323579=29?= Message-ID: <20150307143503.19097.84606@psf.io> https://hg.python.org/cpython/rev/5903ab233a1d changeset: 94891:5903ab233a1d branch: 3.4 parent: 94886:cf12856bde17 user: Benjamin Peterson date: Sat Mar 07 09:34:16 2015 -0500 summary: replace Amazon links in the documentation (closes #23579) Patch by Sayan Chowdhury. files: Doc/library/othergui.rst | 2 +- Doc/library/tkinter.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/othergui.rst b/Doc/library/othergui.rst --- a/Doc/library/othergui.rst +++ b/Doc/library/othergui.rst @@ -50,7 +50,7 @@ low-level device context drawing, drag and drop, system clipboard access, an XML-based resource format and more, including an ever growing library of user-contributed modules. wxPython has a book, `wxPython in Action - `_, by Noel Rappin and + `_, by Noel Rappin and Robin Dunn. PyGTK, PyQt, and wxPython, all have a modern look and feel and more diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -31,13 +31,13 @@ `Tcl/Tk manual `_ Official manual for the latest tcl/tk version. - `Programming Python `_ + `Programming Python `_ Book by Mark Lutz, has excellent coverage of Tkinter. `Modern Tkinter for Busy Python Developers `_ Book by Mark Rozerman about building attractive and modern graphical user interfaces with Python and Tkinter. - `Python and Tkinter Programming `_ + `Python and Tkinter Programming `_ The book by John Grayson (ISBN 1-884777-81-3). @@ -182,7 +182,7 @@ `Tcl and the Tk Toolkit `_ The book by John Ousterhout, the inventor of Tcl. - `Practical Programming in Tcl and Tk `_ + `Practical Programming in Tcl and Tk `_ Brent Welch's encyclopedic book. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 7 15:35:26 2015 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 07 Mar 2015 14:35:26 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <20150307143504.117165.40857@psf.io> https://hg.python.org/cpython/rev/76732273a8ac changeset: 94893:76732273a8ac parent: 94890:ad64b6a7c0e2 parent: 94891:5903ab233a1d user: Benjamin Peterson date: Sat Mar 07 09:34:49 2015 -0500 summary: merge 3.4 files: Doc/library/othergui.rst | 2 +- Doc/library/tkinter.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/othergui.rst b/Doc/library/othergui.rst --- a/Doc/library/othergui.rst +++ b/Doc/library/othergui.rst @@ -50,7 +50,7 @@ low-level device context drawing, drag and drop, system clipboard access, an XML-based resource format and more, including an ever growing library of user-contributed modules. wxPython has a book, `wxPython in Action - `_, by Noel Rappin and + `_, by Noel Rappin and Robin Dunn. PyGTK, PyQt, and wxPython, all have a modern look and feel and more diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -31,13 +31,13 @@ `Tcl/Tk manual `_ Official manual for the latest tcl/tk version. - `Programming Python `_ + `Programming Python `_ Book by Mark Lutz, has excellent coverage of Tkinter. `Modern Tkinter for Busy Python Developers `_ Book by Mark Rozerman about building attractive and modern graphical user interfaces with Python and Tkinter. - `Python and Tkinter Programming `_ + `Python and Tkinter Programming `_ The book by John Grayson (ISBN 1-884777-81-3). @@ -182,7 +182,7 @@ `Tcl and the Tk Toolkit `_ The book by John Ousterhout, the inventor of Tcl. - `Practical Programming in Tcl and Tk `_ + `Practical Programming in Tcl and Tk `_ Brent Welch's encyclopedic book. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 7 15:35:26 2015 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 07 Mar 2015 14:35:26 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_replace_Amazon?= =?utf-8?q?_links_in_the_documentation_=28closes_=2323579=29?= Message-ID: <20150307143504.11169.4188@psf.io> https://hg.python.org/cpython/rev/4ad1ea911fd0 changeset: 94892:4ad1ea911fd0 branch: 2.7 parent: 94885:069c13ca7a70 user: Benjamin Peterson date: Sat Mar 07 09:34:16 2015 -0500 summary: replace Amazon links in the documentation (closes #23579) Patch by Sayan Chowdhury. files: Doc/library/othergui.rst | 2 +- Doc/library/tkinter.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/othergui.rst b/Doc/library/othergui.rst --- a/Doc/library/othergui.rst +++ b/Doc/library/othergui.rst @@ -39,7 +39,7 @@ low-level device context drawing, drag and drop, system clipboard access, an XML-based resource format and more, including an ever growing library of user-contributed modules. wxPython has a book, `wxPython in Action - `_, by Noel Rappin and + `_, by Noel Rappin and Robin Dunn. PyGTK, PyQt, and wxPython, all have a modern look and feel and more diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -35,13 +35,13 @@ `Tcl/Tk manual `_ Official manual for the latest tcl/tk version. - `Programming Python `_ + `Programming Python `_ Book by Mark Lutz, has excellent coverage of Tkinter. `Modern Tkinter for Busy Python Developers `_ Book by Mark Rozerman about building attractive and modern graphical user interfaces with Python and Tkinter. - `Python and Tkinter Programming `_ + `Python and Tkinter Programming `_ The book by John Grayson (ISBN 1-884777-81-3). @@ -190,7 +190,7 @@ `Tcl and the Tk Toolkit `_ The book by John Ousterhout, the inventor of Tcl. - `Practical Programming in Tcl and Tk `_ + `Practical Programming in Tcl and Tk `_ Brent Welch's encyclopedic book. -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Sat Mar 7 18:39:19 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 07 Mar 2015 18:39:19 +0100 Subject: [Python-checkins] Daily reference leaks (97ef38236dc1): sum=3 Message-ID: results for 97ef38236dc1 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/reflogpzfHaT', '-x', 'test_eintr'] From python-checkins at python.org Sat Mar 7 19:09:13 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 07 Mar 2015 18:09:13 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323103=3A_Reduced_?= =?utf-8?q?the_memory_consumption_of_IPv4Address_and_IPv6Address=2E?= Message-ID: <20150307180913.8069.66789@psf.io> https://hg.python.org/cpython/rev/88a5c1698ca4 changeset: 94894:88a5c1698ca4 user: Serhiy Storchaka date: Sat Mar 07 20:08:34 2015 +0200 summary: Issue #23103: Reduced the memory consumption of IPv4Address and IPv6Address. files: Lib/ipaddress.py | 25 ++++++++++++------------- Lib/test/test_ipaddress.py | 7 +++++++ Misc/NEWS | 2 ++ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -386,6 +386,8 @@ """The mother class.""" + __slots__ = () + @property def exploded(self): """Return the longhand version of the IP address as a string.""" @@ -543,6 +545,8 @@ used by single IP addresses. """ + __slots__ = () + def __int__(self): return self._ip @@ -1051,6 +1055,8 @@ """ + __slots__ = () + _version = 4 # Equivalent to 255.255.255.255 or 32 bits of 1's. _ALL_ONES = (2**IPV4LENGTH) - 1 _DECIMAL_DIGITS = frozenset('0123456789') @@ -1063,9 +1069,6 @@ # when constructed (see _make_netmask()). _netmask_cache = {} - def __init__(self, address): - self._version = 4 - def _explode_shorthand_ip_string(self): return str(self) @@ -1243,6 +1246,8 @@ """Represent and manipulate single IPv4 Addresses.""" + __slots__ = ('_ip', '__weakref__') + def __init__(self, address): """ @@ -1259,8 +1264,6 @@ AddressValueError: If ipaddress isn't a valid IPv4 address. """ - _BaseV4.__init__(self, address) - # Efficient constructor from integer. if isinstance(address, int): self._check_int_address(address) @@ -1485,8 +1488,6 @@ supplied. """ - - _BaseV4.__init__(self, address) _BaseNetwork.__init__(self, address) # Constructing from a packed address or integer @@ -1590,6 +1591,8 @@ """ + __slots__ = () + _version = 6 _ALL_ONES = (2**IPV6LENGTH) - 1 _HEXTET_COUNT = 8 _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef') @@ -1599,9 +1602,6 @@ # when constructed (see _make_netmask()). _netmask_cache = {} - def __init__(self, address): - self._version = 6 - @classmethod def _make_netmask(cls, arg): """Make a (netmask, prefix_len) tuple from the given argument. @@ -1870,6 +1870,8 @@ """Represent and manipulate single IPv6 Addresses.""" + __slots__ = ('_ip', '__weakref__') + def __init__(self, address): """Instantiate a new IPv6 address object. @@ -1887,8 +1889,6 @@ AddressValueError: If address isn't a valid IPv6 address. """ - _BaseV6.__init__(self, address) - # Efficient constructor from integer. if isinstance(address, int): self._check_int_address(address) @@ -2180,7 +2180,6 @@ supplied. """ - _BaseV6.__init__(self, address) _BaseNetwork.__init__(self, address) # Efficient constructor from integer or packed address diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -11,6 +11,7 @@ import operator import pickle import ipaddress +import weakref class BaseTestCase(unittest.TestCase): @@ -259,6 +260,9 @@ def test_pickle(self): self.pickle_test('192.0.2.1') + def test_weakref(self): + weakref.ref(self.factory('192.0.2.1')) + class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6): factory = ipaddress.IPv6Address @@ -394,6 +398,9 @@ def test_pickle(self): self.pickle_test('2001:db8::') + def test_weakref(self): + weakref.ref(self.factory('2001:db8::')) + class NetmaskTestMixin_v4(CommonTestMixin_v4): """Input validation on interfaces and networks is very similar""" diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -17,6 +17,8 @@ Library ------- +- Issue #23103: Reduced the memory consumption of IPv4Address and IPv6Address. + - Issue #21793: BaseHTTPRequestHandler again logs response code as numeric, not as stringified enum. Patch by Demian Brecht. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 00:18:35 2015 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 07 Mar 2015 23:18:35 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIwODc2?= =?utf-8?q?=3A_correctly_close_temporary_file_in?= Message-ID: <20150307231835.7561.93530@psf.io> https://hg.python.org/cpython/rev/40e8a8e83ed0 changeset: 94895:40e8a8e83ed0 branch: 3.4 parent: 94891:5903ab233a1d user: Antoine Pitrou date: Sun Mar 08 00:15:05 2015 +0100 summary: Issue #20876: correctly close temporary file in test.support.fs_is_case_insensitive() files: Lib/test/support/__init__.py | 19 +++++++++---------- 1 files changed, 9 insertions(+), 10 deletions(-) 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 @@ -2133,16 +2133,15 @@ def fs_is_case_insensitive(directory): """Detects if the file system for the specified directory is case-insensitive.""" - base_fp, base_path = tempfile.mkstemp(dir=directory) - case_path = base_path.upper() - if case_path == base_path: - case_path = base_path.lower() - try: - return os.path.samefile(base_path, case_path) - except FileNotFoundError: - return False - finally: - os.unlink(base_path) + with tempfile.NamedTemporaryFile(dir=directory) as base: + base_path = base.name + case_path = base_path.upper() + if case_path == base_path: + case_path = base_path.lower() + try: + return os.path.samefile(base_path, case_path) + except FileNotFoundError: + return False class SuppressCrashReport: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 00:18:35 2015 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 07 Mar 2015 23:18:35 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320876=3A_correctly_close_temporary_file_in?= Message-ID: <20150307231835.9686.98197@psf.io> https://hg.python.org/cpython/rev/5406ed13bd6e changeset: 94896:5406ed13bd6e parent: 94894:88a5c1698ca4 parent: 94895:40e8a8e83ed0 user: Antoine Pitrou date: Sun Mar 08 00:18:29 2015 +0100 summary: Issue #20876: correctly close temporary file in test.support.fs_is_case_insensitive() files: Lib/test/support/__init__.py | 19 +++++++++---------- 1 files changed, 9 insertions(+), 10 deletions(-) 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 @@ -2134,16 +2134,15 @@ def fs_is_case_insensitive(directory): """Detects if the file system for the specified directory is case-insensitive.""" - base_fp, base_path = tempfile.mkstemp(dir=directory) - case_path = base_path.upper() - if case_path == base_path: - case_path = base_path.lower() - try: - return os.path.samefile(base_path, case_path) - except FileNotFoundError: - return False - finally: - os.unlink(base_path) + with tempfile.NamedTemporaryFile(dir=directory) as base: + base_path = base.name + case_path = base_path.upper() + if case_path == base_path: + case_path = base_path.lower() + try: + return os.path.samefile(base_path, case_path) + except FileNotFoundError: + return False class SuppressCrashReport: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 02:09:35 2015 From: python-checkins at python.org (victor.stinner) Date: Sun, 08 Mar 2015 01:09:35 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2322524=3A_New_os?= =?utf-8?q?=2Escandir=28=29_function=2C_part_of_the_PEP_471=3A_=22os=2Esca?= =?utf-8?b?bmRpcigp?= Message-ID: <20150308010935.7561.48934@psf.io> https://hg.python.org/cpython/rev/d04d5b9c29f6 changeset: 94897:d04d5b9c29f6 user: Victor Stinner date: Sun Mar 08 01:58:04 2015 +0100 summary: Issue #22524: New os.scandir() function, part of the PEP 471: "os.scandir() function -- a better and faster directory iterator". Patch written by Ben Hoyt. files: Doc/library/os.rst | 177 ++++++ Doc/whatsnew/3.5.rst | 19 + Lib/test/test_os.py | 224 ++++++++ Misc/NEWS | 4 + Modules/posixmodule.c | 818 +++++++++++++++++++++++++++++- 5 files changed, 1222 insertions(+), 20 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1601,6 +1601,11 @@ Availability: Unix, Windows. + .. seealso:: + + The :func:`scandir` function returns the directory entries with more + information than just the name. + .. versionchanged:: 3.2 The *path* parameter became optional. @@ -1893,6 +1898,178 @@ The *dir_fd* parameter. +.. function:: scandir(path='.') + + Return an iterator of :class:`DirEntry` objects corresponding to the entries + in the directory given by *path*. The entries are yielded in arbitrary + order, and the special entries ``'.'`` and ``'..'`` are not included. + + On Windows, *path* must of type :class:`str`. On POSIX, *path* can be of + type :class:`str` or :class:`bytes`. If *path* is of type :class:`bytes`, + the :attr:`~DirEntry.name` and :attr:`~DirEntry.path` attributes of + :class:`DirEntry` are also of type ``bytes``. Use :func:`~os.fsencode` and + :func:`~os.fsdecode` to encode and decode paths. + + The :func:`scandir` function is recommended, instead of :func:`listdir`, + when the file type of entries is used. In most cases, the file type of a + :class:`DirEntry` is retrieved directly by :func:`scandir`, no system call + is required. If only the name of entries is used, :func:`listdir` can + be more efficient than :func:`scandir`. + + The following example shows a simple use of :func:`scandir` to display all + the files excluding directories in the given *path* that don't start with + ``'.'``:: + + for entry in os.scandir(path): + if not entry.name.startswith('.') and entry.is_file(): + print(entry.name) + + .. note:: + + On Unix-based systems, :func:`scandir` uses the system's + `opendir() `_ + and + `readdir() `_ + functions. On Windows, it uses the Win32 + `FindFirstFileW `_ + and + `FindNextFileW `_ + functions. + + .. seealso:: + + The :func:`listdir` function returns the names of the directory entries. + + .. versionadded:: 3.5 + + +.. class:: DirEntry + + Object yielded by :func:`scandir` to expose the file path and other file + attributes of a directory entry. + + :func:`scandir` will provide as much of this information as possible without + making additional system calls. When a ``stat()`` or ``lstat()`` system call + is made, the ``DirEntry`` object cache the result . + + ``DirEntry`` instances are not intended to be stored in long-lived data + structures; if you know the file metadata has changed or if a long time has + elapsed since calling :func:`scandir`, call ``os.stat(entry.path)`` to fetch + up-to-date information. + + Because the ``DirEntry`` methods can make operating system calls, they may + also raise :exc:`OSError`. For example, if a file is deleted between calling + :func:`scandir` and calling :func:`DirEntry.stat`, a + :exc:`FileNotFoundError` exception can be raised. Unfortunately, the + behaviour on errors depends on the platform. If you need very fine-grained + control over errors, you can catch :exc:`OSError` when calling one of the + ``DirEntry`` methods and handle as appropriate. + + Attributes and methods on a ``DirEntry`` instance are as follows: + + .. attribute:: name + + The entry's base filename, relative to the :func:`scandir` *path* + argument. + + The :attr:`name` type is :class:`str`. On POSIX, it can be of type + :class:`bytes` if the type of the :func:`scandir` *path* argument is also + :class:`bytes`. Use :func:`~os.fsdecode` to decode the name. + + .. attribute:: path + + The entry's full path name: equivalent to ``os.path.join(scandir_path, + entry.name)`` where *scandir_path* is the :func:`scandir` *path* + argument. The path is only absolute if the :func:`scandir` *path* + argument is absolute. + + The :attr:`name` type is :class:`str`. On POSIX, it can be of type + :class:`bytes` if the type of the :func:`scandir` *path* argument is also + :class:`bytes`. Use :func:`~os.fsdecode` to decode the path. + + .. method:: inode() + + Return the inode number of the entry. + + The result is cached in the object, use ``os.stat(entry.path, + follow_symlinks=False).st_ino`` to fetch up-to-date information. + + On POSIX, no system call is required. + + .. method:: is_dir(\*, follow_symlinks=True) + + If *follow_symlinks* is ``True`` (the default), return ``True`` if the + entry is a directory or a symbolic link pointing to a directory, + return ``False`` if it points to another kind of file, if it doesn't + exist anymore or if it is a broken symbolic link. + + If *follow_symlinks* is ``False``, return ``True`` only if this entry + is a directory, return ``False`` if it points to a symbolic link or + another kind of file, if the entry doesn't exist anymore or if it is a + broken symbolic link + + The result is cached in the object. Call :func:`stat.S_ISDIR` with + :func:`os.stat` to fetch up-to-date information. + + The method can raise :exc:`OSError`, such as :exc:`PermissionError`, + but :exc:`FileNotFoundError` is catched. + + In most cases, no system call is required. + + .. method:: is_file(\*, follow_symlinks=True) + + If *follow_symlinks* is ``True`` (the default), return ``True`` if the + entry is a regular file or a symbolic link pointing to a regular file, + return ``False`` if it points to another kind of file, if it doesn't + exist anymore or if it is a broken symbolic link. + + If *follow_symlinks* is ``False``, return ``True`` only if this entry + is a regular file, return ``False`` if it points to a symbolic link or + another kind of file, if it doesn't exist anymore or if it is a broken + symbolic link. + + The result is cached in the object. Call :func:`stat.S_ISREG` with + :func:`os.stat` to fetch up-to-date information. + + The method can raise :exc:`OSError`, such as :exc:`PermissionError`, + but :exc:`FileNotFoundError` is catched. + + In most cases, no system call is required. + + .. method:: is_symlink() + + Return ``True`` if this entry is a symbolic link or a broken symbolic + link, return ``False`` if it points to a another kind of file or if the + entry doesn't exist anymore. + + The result is cached in the object. Call :func:`os.path.islink` to fetch + up-to-date information. + + The method can raise :exc:`OSError`, such as :exc:`PermissionError`, + but :exc:`FileNotFoundError` is catched. + + In most cases, no system call is required. + + .. method:: stat(\*, follow_symlinks=True) + + Return a :class:`stat_result` object for this entry. This function + normally follows symbolic links; to stat a symbolic link add the + argument ``follow_symlinks=False``. + + On Windows, the ``st_ino``, ``st_dev`` and ``st_nlink`` attributes of the + :class:`stat_result` are always set to zero. Call :func:`os.stat` to + get these attributes. + + The result is cached in the object. Call :func:`os.stat` to fetch + up-to-date information. + + On Windows, ``DirEntry.stat(follow_symlinks=False)`` doesn't require a + system call. ``DirEntry.stat()`` requires a system call if the entry is a + symbolic link. + + .. versionadded:: 3.5 + + .. function:: stat(path, \*, dir_fd=None, follow_symlinks=True) Get the status of a file or a file descriptor. Perform the equivalent of a 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 @@ -111,6 +111,19 @@ PEP written by Carl Meyer + +PEP 471 - os.scandir() function -- a better and faster directory iterator +------------------------------------------------------------------------- + +:pep:`471` includes a new directory iteration function, :func:`os.scandir`, +in the standard library. + +.. seealso:: + + :pep:`471` -- os.scandir() function -- a better and faster directory + iterator. + + PEP 475: Retry system calls failing with EINTR ---------------------------------------------- @@ -118,6 +131,8 @@ this means that user code doesn't have to deal with EINTR or InterruptedError manually, and should make it more robust against asynchronous signal reception. +PEP and implementation written by Ben Hoyt with the help of Victor Stinner. + .. seealso:: :pep:`475` -- Retry system calls failing with EINTR @@ -279,6 +294,10 @@ os -- +* New :func:`os.scandir` function: Return an iterator of :class:`os.DirEntry` + objects corresponding to the entries in the directory given by *path*. + (Implementation written by Ben Hoyt with the help of Victor Stinner.) + * :class:`os.stat_result` now has a :attr:`~os.stat_result.st_file_attributes` attribute on Windows. (Contributed by Ben Hoyt in :issue:`21719`.) 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 @@ -2698,5 +2698,229 @@ self.assertIn('walk', os.__all__) +class TestScandir(unittest.TestCase): + def setUp(self): + self.path = os.path.realpath(support.TESTFN) + self.addCleanup(support.rmtree, self.path) + os.mkdir(self.path) + + def create_file(self, name="file.txt"): + filename = os.path.join(self.path, name) + with open(filename, "wb") as fp: + fp.write(b'python') + return filename + + def get_entries(self, names): + entries = dict((entry.name, entry) + for entry in os.scandir(self.path)) + self.assertEqual(sorted(entries.keys()), names) + return entries + + def assert_stat_equal(self, stat1, stat2, skip_fields): + if skip_fields: + for attr in dir(stat1): + if not attr.startswith("st_"): + continue + if attr in ("st_dev", "st_ino", "st_nlink"): + continue + self.assertEqual(getattr(stat1, attr), + getattr(stat2, attr), + (stat1, stat2, attr)) + else: + self.assertEqual(stat1, stat2) + + def check_entry(self, entry, name, is_dir, is_file, is_symlink): + self.assertEqual(entry.name, name) + self.assertEqual(entry.path, os.path.join(self.path, name)) + self.assertEqual(entry.inode(), + os.stat(entry.path, follow_symlinks=False).st_ino) + + entry_stat = os.stat(entry.path) + self.assertEqual(entry.is_dir(), + stat.S_ISDIR(entry_stat.st_mode)) + self.assertEqual(entry.is_file(), + stat.S_ISREG(entry_stat.st_mode)) + self.assertEqual(entry.is_symlink(), + os.path.islink(entry.path)) + + entry_lstat = os.stat(entry.path, follow_symlinks=False) + self.assertEqual(entry.is_dir(follow_symlinks=False), + stat.S_ISDIR(entry_lstat.st_mode)) + self.assertEqual(entry.is_file(follow_symlinks=False), + stat.S_ISREG(entry_lstat.st_mode)) + + self.assert_stat_equal(entry.stat(), + entry_stat, + os.name == 'nt' and not is_symlink) + self.assert_stat_equal(entry.stat(follow_symlinks=False), + entry_lstat, + os.name == 'nt') + + def test_attributes(self): + link = hasattr(os, 'link') + symlink = support.can_symlink() + + dirname = os.path.join(self.path, "dir") + os.mkdir(dirname) + filename = self.create_file("file.txt") + if link: + os.link(filename, os.path.join(self.path, "link_file.txt")) + if symlink: + os.symlink(dirname, os.path.join(self.path, "symlink_dir"), + target_is_directory=True) + os.symlink(filename, os.path.join(self.path, "symlink_file.txt")) + + names = ['dir', 'file.txt'] + if link: + names.append('link_file.txt') + if symlink: + names.extend(('symlink_dir', 'symlink_file.txt')) + entries = self.get_entries(names) + + entry = entries['dir'] + self.check_entry(entry, 'dir', True, False, False) + + entry = entries['file.txt'] + self.check_entry(entry, 'file.txt', False, True, False) + + if link: + entry = entries['link_file.txt'] + self.check_entry(entry, 'link_file.txt', False, True, False) + + if symlink: + entry = entries['symlink_dir'] + self.check_entry(entry, 'symlink_dir', True, False, True) + + entry = entries['symlink_file.txt'] + self.check_entry(entry, 'symlink_file.txt', False, True, True) + + def get_entry(self, name): + entries = list(os.scandir(self.path)) + self.assertEqual(len(entries), 1) + + entry = entries[0] + self.assertEqual(entry.name, name) + return entry + + def create_file_entry(self): + filename = self.create_file() + return self.get_entry(os.path.basename(filename)) + + def test_current_directory(self): + filename = self.create_file() + old_dir = os.getcwd() + try: + os.chdir(self.path) + + # call scandir() without parameter: it must list the content + # of the current directory + entries = dict((entry.name, entry) for entry in os.scandir()) + self.assertEqual(sorted(entries.keys()), + [os.path.basename(filename)]) + finally: + os.chdir(old_dir) + + def test_repr(self): + entry = self.create_file_entry() + self.assertEqual(repr(entry), "") + + def test_removed_dir(self): + path = os.path.join(self.path, 'dir') + + os.mkdir(path) + entry = self.get_entry('dir') + os.rmdir(path) + + # On POSIX, is_dir() result depends if scandir() filled d_type or not + if os.name == 'nt': + self.assertTrue(entry.is_dir()) + self.assertFalse(entry.is_file()) + self.assertFalse(entry.is_symlink()) + if os.name == 'nt': + self.assertRaises(FileNotFoundError, entry.inode) + # don't fail + entry.stat() + entry.stat(follow_symlinks=False) + else: + self.assertGreater(entry.inode(), 0) + self.assertRaises(FileNotFoundError, entry.stat) + self.assertRaises(FileNotFoundError, entry.stat, follow_symlinks=False) + + def test_removed_file(self): + entry = self.create_file_entry() + os.unlink(entry.path) + + self.assertFalse(entry.is_dir()) + # On POSIX, is_dir() result depends if scandir() filled d_type or not + if os.name == 'nt': + self.assertTrue(entry.is_file()) + self.assertFalse(entry.is_symlink()) + if os.name == 'nt': + self.assertRaises(FileNotFoundError, entry.inode) + # don't fail + entry.stat() + entry.stat(follow_symlinks=False) + else: + self.assertGreater(entry.inode(), 0) + self.assertRaises(FileNotFoundError, entry.stat) + self.assertRaises(FileNotFoundError, entry.stat, follow_symlinks=False) + + def test_broken_symlink(self): + if not support.can_symlink(): + return self.skipTest('cannot create symbolic link') + + filename = self.create_file("file.txt") + os.symlink(filename, + os.path.join(self.path, "symlink.txt")) + entries = self.get_entries(['file.txt', 'symlink.txt']) + entry = entries['symlink.txt'] + os.unlink(filename) + + self.assertGreater(entry.inode(), 0) + self.assertFalse(entry.is_dir()) + self.assertFalse(entry.is_file()) # broken symlink returns False + self.assertFalse(entry.is_dir(follow_symlinks=False)) + self.assertFalse(entry.is_file(follow_symlinks=False)) + self.assertTrue(entry.is_symlink()) + self.assertRaises(FileNotFoundError, entry.stat) + # don't fail + entry.stat(follow_symlinks=False) + + def test_bytes(self): + if os.name == "nt": + # On Windows, os.scandir(bytes) must raise an exception + self.assertRaises(TypeError, os.scandir, b'.') + return + + self.create_file("file.txt") + + path_bytes = os.fsencode(self.path) + entries = list(os.scandir(path_bytes)) + self.assertEqual(len(entries), 1, entries) + entry = entries[0] + + self.assertEqual(entry.name, b'file.txt') + self.assertEqual(entry.path, + os.fsencode(os.path.join(self.path, 'file.txt'))) + + def test_empty_path(self): + self.assertRaises(FileNotFoundError, os.scandir, '') + + def test_consume_iterator_twice(self): + self.create_file("file.txt") + iterator = os.scandir(self.path) + + entries = list(iterator) + self.assertEqual(len(entries), 1, entries) + + # check than consuming the iterator twice doesn't raise exception + entries2 = list(iterator) + self.assertEqual(len(entries2), 0, entries2) + + def test_bad_path_type(self): + for obj in [1234, 1.234, {}, []]: + self.assertRaises(TypeError, os.scandir, obj) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -17,6 +17,10 @@ Library ------- +- Issue #22524: New os.scandir() function, part of the PEP 471: "os.scandir() + function -- a better and faster directory iterator". Patch written by Ben + Hoyt. + - Issue #23103: Reduced the memory consumption of IPv4Address and IPv6Address. - Issue #21793: BaseHTTPRequestHandler again logs response code as numeric, diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -25,6 +25,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "structmember.h" #ifndef MS_WINDOWS #include "posixmodule.h" #else @@ -373,6 +374,14 @@ #define DWORD_MAX 4294967295U #ifdef MS_WINDOWS +#define INITFUNC PyInit_nt +#define MODNAME "nt" +#else +#define INITFUNC PyInit_posix +#define MODNAME "posix" +#endif + +#ifdef MS_WINDOWS /* defined in fileutils.c */ PyAPI_FUNC(void) _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *); PyAPI_FUNC(void) _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *, @@ -1396,6 +1405,25 @@ return TRUE; } +static void +find_data_to_file_info_w(WIN32_FIND_DATAW *pFileData, + BY_HANDLE_FILE_INFORMATION *info, + ULONG *reparse_tag) +{ + memset(info, 0, sizeof(*info)); + info->dwFileAttributes = pFileData->dwFileAttributes; + info->ftCreationTime = pFileData->ftCreationTime; + info->ftLastAccessTime = pFileData->ftLastAccessTime; + info->ftLastWriteTime = pFileData->ftLastWriteTime; + info->nFileSizeHigh = pFileData->nFileSizeHigh; + info->nFileSizeLow = pFileData->nFileSizeLow; +/* info->nNumberOfLinks = 1; */ + if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + *reparse_tag = pFileData->dwReserved0; + else + *reparse_tag = 0; +} + static BOOL attributes_from_dir_w(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *reparse_tag) { @@ -1405,17 +1433,7 @@ if (hFindFile == INVALID_HANDLE_VALUE) return FALSE; FindClose(hFindFile); - memset(info, 0, sizeof(*info)); - *reparse_tag = 0; - info->dwFileAttributes = FileData.dwFileAttributes; - info->ftCreationTime = FileData.ftCreationTime; - info->ftLastAccessTime = FileData.ftLastAccessTime; - info->ftLastWriteTime = FileData.ftLastWriteTime; - info->nFileSizeHigh = FileData.nFileSizeHigh; - info->nFileSizeLow = FileData.nFileSizeLow; -/* info->nNumberOfLinks = 1; */ - if (FileData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) - *reparse_tag = FileData.dwReserved0; + find_data_to_file_info_w(&FileData, info, reparse_tag); return TRUE; } @@ -16330,6 +16348,766 @@ #endif /* !MS_WINDOWS */ +PyDoc_STRVAR(posix_scandir__doc__, +"scandir(path='.') -> iterator of DirEntry objects for given path"); + +static char *follow_symlinks_keywords[] = {"follow_symlinks", NULL}; + +typedef struct { + PyObject_HEAD + PyObject *name; + PyObject *path; + PyObject *stat; + PyObject *lstat; +#ifdef MS_WINDOWS + struct _Py_stat_struct win32_lstat; + __int64 win32_file_index; + int got_file_index; +#else /* POSIX */ + unsigned char d_type; + ino_t d_ino; +#endif +} DirEntry; + +static void +DirEntry_dealloc(DirEntry *entry) +{ + Py_XDECREF(entry->name); + Py_XDECREF(entry->path); + Py_XDECREF(entry->stat); + Py_XDECREF(entry->lstat); + Py_TYPE(entry)->tp_free((PyObject *)entry); +} + +/* Forward reference */ +static int +DirEntry_test_mode(DirEntry *self, int follow_symlinks, unsigned short mode_bits); + +/* Set exception and return -1 on error, 0 for False, 1 for True */ +static int +DirEntry_is_symlink(DirEntry *self) +{ +#ifdef MS_WINDOWS + return (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK; +#else /* POSIX */ + if (self->d_type != DT_UNKNOWN) + return self->d_type == DT_LNK; + else + return DirEntry_test_mode(self, 0, S_IFLNK); +#endif +} + +static PyObject * +DirEntry_py_is_symlink(DirEntry *self) +{ + int result; + + result = DirEntry_is_symlink(self); + if (result == -1) + return NULL; + return PyBool_FromLong(result); +} + +static PyObject * +DirEntry_fetch_stat(DirEntry *self, int follow_symlinks) +{ + int result; + struct _Py_stat_struct st; + +#ifdef MS_WINDOWS + wchar_t *path; + + path = PyUnicode_AsUnicode(self->path); + if (!path) + return NULL; + + if (follow_symlinks) + result = win32_stat_w(path, &st); + else + result = win32_lstat_w(path, &st); + + if (result != 0) { + return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, + 0, self->path); + } +#else /* POSIX */ + PyObject *bytes; + char *path; + + if (!PyUnicode_FSConverter(self->path, &bytes)) + return NULL; + path = PyBytes_AS_STRING(bytes); + + if (follow_symlinks) + result = STAT(path, &st); + else + result = LSTAT(path, &st); + Py_DECREF(bytes); + + if (result != 0) + return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, self->path); +#endif + + return _pystat_fromstructstat(&st); +} + +static PyObject * +DirEntry_get_lstat(DirEntry *self) +{ + if (!self->lstat) { +#ifdef MS_WINDOWS + self->lstat = _pystat_fromstructstat(&self->win32_lstat); +#else /* POSIX */ + self->lstat = DirEntry_fetch_stat(self, 0); +#endif + } + Py_XINCREF(self->lstat); + return self->lstat; +} + +static PyObject * +DirEntry_get_stat(DirEntry *self, int follow_symlinks) +{ + if (!follow_symlinks) + return DirEntry_get_lstat(self); + + if (!self->stat) { + int result = DirEntry_is_symlink(self); + if (result == -1) + return NULL; + else if (result) + self->stat = DirEntry_fetch_stat(self, 1); + else + self->stat = DirEntry_get_lstat(self); + } + + Py_XINCREF(self->stat); + return self->stat; +} + +static PyObject * +DirEntry_stat(DirEntry *self, PyObject *args, PyObject *kwargs) +{ + int follow_symlinks = 1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|$p:DirEntry.stat", + follow_symlinks_keywords, &follow_symlinks)) + return NULL; + + return DirEntry_get_stat(self, follow_symlinks); +} + +/* Set exception and return -1 on error, 0 for False, 1 for True */ +static int +DirEntry_test_mode(DirEntry *self, int follow_symlinks, unsigned short mode_bits) +{ + PyObject *stat = NULL; + PyObject *st_mode = NULL; + long mode; + int result; + int is_symlink; + int need_stat; + _Py_IDENTIFIER(st_mode); +#ifdef MS_WINDOWS + unsigned long dir_bits; +#endif + +#ifdef MS_WINDOWS + is_symlink = (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK; + need_stat = follow_symlinks && is_symlink; +#else /* POSIX */ + is_symlink = self->d_type == DT_LNK; + need_stat = self->d_type == DT_UNKNOWN || (follow_symlinks && is_symlink); +#endif + + if (need_stat) { + stat = DirEntry_get_stat(self, follow_symlinks); + if (!stat) { + if (PyErr_ExceptionMatches(PyExc_FileNotFoundError)) { + /* If file doesn't exist (anymore), then return False + (i.e., say it's not a file/directory) */ + PyErr_Clear(); + return 0; + } + goto error; + } + st_mode = _PyObject_GetAttrId(stat, &PyId_st_mode); + if (!st_mode) + goto error; + + mode = PyLong_AsLong(st_mode); + if (mode == -1 && PyErr_Occurred()) + goto error; + Py_CLEAR(st_mode); + Py_CLEAR(stat); + result = (mode & S_IFMT) == mode_bits; + } + else if (is_symlink) { + assert(mode_bits != S_IFLNK); + result = 0; + } + else { + assert(mode_bits == S_IFDIR || mode_bits == S_IFREG); +#ifdef MS_WINDOWS + dir_bits = self->win32_lstat.st_file_attributes & FILE_ATTRIBUTE_DIRECTORY; + if (mode_bits == S_IFDIR) + result = dir_bits != 0; + else + result = dir_bits == 0; +#else /* POSIX */ + if (mode_bits == S_IFDIR) + result = self->d_type == DT_DIR; + else + result = self->d_type == DT_REG; +#endif + } + + return result; + +error: + Py_XDECREF(st_mode); + Py_XDECREF(stat); + return -1; +} + +static PyObject * +DirEntry_py_test_mode(DirEntry *self, int follow_symlinks, unsigned short mode_bits) +{ + int result; + + result = DirEntry_test_mode(self, follow_symlinks, mode_bits); + if (result == -1) + return NULL; + return PyBool_FromLong(result); +} + +static PyObject * +DirEntry_is_dir(DirEntry *self, PyObject *args, PyObject *kwargs) +{ + int follow_symlinks = 1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|$p:DirEntry.is_dir", + follow_symlinks_keywords, &follow_symlinks)) + return NULL; + + return DirEntry_py_test_mode(self, follow_symlinks, S_IFDIR); +} + +static PyObject * +DirEntry_is_file(DirEntry *self, PyObject *args, PyObject *kwargs) +{ + int follow_symlinks = 1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|$p:DirEntry.is_file", + follow_symlinks_keywords, &follow_symlinks)) + return NULL; + + return DirEntry_py_test_mode(self, follow_symlinks, S_IFREG); +} + +static PyObject * +DirEntry_inode(DirEntry *self) +{ +#ifdef MS_WINDOWS + if (!self->got_file_index) { + wchar_t *path; + struct _Py_stat_struct stat; + + path = PyUnicode_AsUnicode(self->path); + if (!path) + return NULL; + + if (win32_lstat_w(path, &stat) != 0) { + return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, + 0, self->path); + } + + self->win32_file_index = stat.st_ino; + self->got_file_index = 1; + } + return PyLong_FromLongLong((PY_LONG_LONG)self->win32_file_index); +#else /* POSIX */ +#ifdef HAVE_LARGEFILE_SUPPORT + return PyLong_FromLongLong((PY_LONG_LONG)self->d_ino); +#else + return PyLong_FromLong((long)self->d_ino); +#endif +#endif +} + +static PyObject * +DirEntry_repr(DirEntry *self) +{ + return PyUnicode_FromFormat("", self->name); +} + +static PyMemberDef DirEntry_members[] = { + {"name", T_OBJECT_EX, offsetof(DirEntry, name), READONLY, + "the entry's base filename, relative to scandir() \"path\" argument"}, + {"path", T_OBJECT_EX, offsetof(DirEntry, path), READONLY, + "the entry's full path name; equivalent to os.path.join(scandir_path, entry.name)"}, + {NULL} +}; + +static PyMethodDef DirEntry_methods[] = { + {"is_dir", (PyCFunction)DirEntry_is_dir, METH_VARARGS | METH_KEYWORDS, + "return True if the entry is a directory; cached per entry" + }, + {"is_file", (PyCFunction)DirEntry_is_file, METH_VARARGS | METH_KEYWORDS, + "return True if the entry is a file; cached per entry" + }, + {"is_symlink", (PyCFunction)DirEntry_py_is_symlink, METH_NOARGS, + "return True if the entry is a symbolic link; cached per entry" + }, + {"stat", (PyCFunction)DirEntry_stat, METH_VARARGS | METH_KEYWORDS, + "return stat_result object for the entry; cached per entry" + }, + {"inode", (PyCFunction)DirEntry_inode, METH_NOARGS, + "return inode of the entry; cached per entry", + }, + {NULL} +}; + +PyTypeObject DirEntryType = { + PyVarObject_HEAD_INIT(NULL, 0) + MODNAME ".DirEntry", /* tp_name */ + sizeof(DirEntry), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)DirEntry_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)DirEntry_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + DirEntry_methods, /* tp_methods */ + DirEntry_members, /* tp_members */ +}; + +#ifdef MS_WINDOWS + +static wchar_t * +join_path_filenameW(wchar_t *path_wide, wchar_t* filename) +{ + Py_ssize_t path_len; + Py_ssize_t size; + wchar_t *result; + wchar_t ch; + + if (!path_wide) { /* Default arg: "." */ + path_wide = L"."; + path_len = 1; + } + else { + path_len = wcslen(path_wide); + } + + /* The +1's are for the path separator and the NUL */ + size = path_len + 1 + wcslen(filename) + 1; + result = PyMem_New(wchar_t, size); + if (!result) { + PyErr_NoMemory(); + return NULL; + } + wcscpy(result, path_wide); + if (path_len > 0) { + ch = result[path_len - 1]; + if (ch != SEP && ch != ALTSEP && ch != L':') + result[path_len++] = SEP; + wcscpy(result + path_len, filename); + } + return result; +} + +static PyObject * +DirEntry_from_find_data(path_t *path, WIN32_FIND_DATAW *dataW) +{ + DirEntry *entry; + BY_HANDLE_FILE_INFORMATION file_info; + ULONG reparse_tag; + wchar_t *joined_path; + + entry = PyObject_New(DirEntry, &DirEntryType); + if (!entry) + return NULL; + entry->name = NULL; + entry->path = NULL; + entry->stat = NULL; + entry->lstat = NULL; + entry->got_file_index = 0; + + entry->name = PyUnicode_FromWideChar(dataW->cFileName, -1); + if (!entry->name) + goto error; + + joined_path = join_path_filenameW(path->wide, dataW->cFileName); + if (!joined_path) + goto error; + + entry->path = PyUnicode_FromWideChar(joined_path, -1); + PyMem_Free(joined_path); + if (!entry->path) + goto error; + + find_data_to_file_info_w(dataW, &file_info, &reparse_tag); + _Py_attribute_data_to_stat(&file_info, reparse_tag, &entry->win32_lstat); + + return (PyObject *)entry; + +error: + Py_DECREF(entry); + return NULL; +} + +#else /* POSIX */ + +static char * +join_path_filename(char *path_narrow, char* filename, Py_ssize_t filename_len) +{ + Py_ssize_t path_len; + Py_ssize_t size; + char *result; + + if (!path_narrow) { /* Default arg: "." */ + path_narrow = "."; + path_len = 1; + } + else { + path_len = strlen(path_narrow); + } + + if (filename_len == -1) + filename_len = strlen(filename); + + /* The +1's are for the path separator and the NUL */ + size = path_len + 1 + filename_len + 1; + result = PyMem_New(char, size); + if (!result) { + PyErr_NoMemory(); + return NULL; + } + strcpy(result, path_narrow); + if (path_len > 0 && result[path_len - 1] != '/') + result[path_len++] = '/'; + strcpy(result + path_len, filename); + return result; +} + +static PyObject * +DirEntry_from_posix_info(path_t *path, char *name, Py_ssize_t name_len, + unsigned char d_type, ino_t d_ino) +{ + DirEntry *entry; + char *joined_path; + + entry = PyObject_New(DirEntry, &DirEntryType); + if (!entry) + return NULL; + entry->name = NULL; + entry->path = NULL; + entry->stat = NULL; + entry->lstat = NULL; + + joined_path = join_path_filename(path->narrow, name, name_len); + if (!joined_path) + goto error; + + if (!path->narrow || !PyBytes_Check(path->object)) { + entry->name = PyUnicode_DecodeFSDefaultAndSize(name, name_len); + entry->path = PyUnicode_DecodeFSDefault(joined_path); + } + else { + entry->name = PyBytes_FromStringAndSize(name, name_len); + entry->path = PyBytes_FromString(joined_path); + } + PyMem_Free(joined_path); + if (!entry->name || !entry->path) + goto error; + + entry->d_type = d_type; + entry->d_ino = d_ino; + + return (PyObject *)entry; + +error: + Py_XDECREF(entry); + return NULL; +} + +#endif + + +typedef struct { + PyObject_HEAD + path_t path; +#ifdef MS_WINDOWS + HANDLE handle; + WIN32_FIND_DATAW file_data; + int first_time; +#else /* POSIX */ + DIR *dirp; +#endif +} ScandirIterator; + +#ifdef MS_WINDOWS + +static void +ScandirIterator_close(ScandirIterator *iterator) +{ + if (iterator->handle == INVALID_HANDLE_VALUE) + return; + + Py_BEGIN_ALLOW_THREADS + FindClose(iterator->handle); + Py_END_ALLOW_THREADS + iterator->handle = INVALID_HANDLE_VALUE; +} + +static PyObject * +ScandirIterator_iternext(ScandirIterator *iterator) +{ + WIN32_FIND_DATAW *file_data = &iterator->file_data; + BOOL success; + + /* Happens if the iterator is iterated twice */ + if (iterator->handle == INVALID_HANDLE_VALUE) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + while (1) { + if (!iterator->first_time) { + Py_BEGIN_ALLOW_THREADS + success = FindNextFileW(iterator->handle, file_data); + Py_END_ALLOW_THREADS + if (!success) { + if (GetLastError() != ERROR_NO_MORE_FILES) + return path_error(&iterator->path); + /* No more files found in directory, stop iterating */ + break; + } + } + iterator->first_time = 0; + + /* Skip over . and .. */ + if (wcscmp(file_data->cFileName, L".") != 0 && + wcscmp(file_data->cFileName, L"..") != 0) + return DirEntry_from_find_data(&iterator->path, file_data); + + /* Loop till we get a non-dot directory or finish iterating */ + } + + ScandirIterator_close(iterator); + + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +#else /* POSIX */ + +static void +ScandirIterator_close(ScandirIterator *iterator) +{ + if (!iterator->dirp) + return; + + Py_BEGIN_ALLOW_THREADS + closedir(iterator->dirp); + Py_END_ALLOW_THREADS + iterator->dirp = NULL; + return; +} + +static PyObject * +ScandirIterator_iternext(ScandirIterator *iterator) +{ + struct dirent *direntp; + Py_ssize_t name_len; + int is_dot; + unsigned char d_type; + + /* Happens if the iterator is iterated twice */ + if (!iterator->dirp) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + while (1) { + errno = 0; + Py_BEGIN_ALLOW_THREADS + direntp = readdir(iterator->dirp); + Py_END_ALLOW_THREADS + + if (!direntp) { + if (errno != 0) + return path_error(&iterator->path); + /* No more files found in directory, stop iterating */ + break; + } + + /* Skip over . and .. */ + name_len = NAMLEN(direntp); + is_dot = direntp->d_name[0] == '.' && + (name_len == 1 || (direntp->d_name[1] == '.' && name_len == 2)); + if (!is_dot) { +#if defined(__GLIBC__) && !defined(_DIRENT_HAVE_D_TYPE) + d_type = DT_UNKNOWN; /* System doesn't support d_type */ +#else + d_type = direntp->d_type; +#endif + return DirEntry_from_posix_info(&iterator->path, direntp->d_name, + name_len, d_type, direntp->d_ino); + } + + /* Loop till we get a non-dot directory or finish iterating */ + } + + ScandirIterator_close(iterator); + + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +#endif + +static void +ScandirIterator_dealloc(ScandirIterator *iterator) +{ + ScandirIterator_close(iterator); + Py_XDECREF(iterator->path.object); + path_cleanup(&iterator->path); + Py_TYPE(iterator)->tp_free((PyObject *)iterator); +} + +PyTypeObject ScandirIteratorType = { + PyVarObject_HEAD_INIT(NULL, 0) + MODNAME ".ScandirIterator", /* tp_name */ + sizeof(ScandirIterator), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)ScandirIterator_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)ScandirIterator_iternext, /* tp_iternext */ +}; + +static PyObject * +posix_scandir(PyObject *self, PyObject *args, PyObject *kwargs) +{ + ScandirIterator *iterator; + static char *keywords[] = {"path", NULL}; +#ifdef MS_WINDOWS + wchar_t *path_strW; +#else + char *path; +#endif + + iterator = PyObject_New(ScandirIterator, &ScandirIteratorType); + if (!iterator) + return NULL; + memset(&iterator->path, 0, sizeof(path_t)); + iterator->path.function_name = "scandir"; + iterator->path.nullable = 1; + +#ifdef MS_WINDOWS + iterator->handle = INVALID_HANDLE_VALUE; +#else + iterator->dirp = NULL; +#endif + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&:scandir", keywords, + path_converter, &iterator->path)) + goto error; + + /* path_converter doesn't keep path.object around, so do it + manually for the lifetime of the iterator here (the refcount + is decremented in ScandirIterator_dealloc) + */ + Py_XINCREF(iterator->path.object); + +#ifdef MS_WINDOWS + if (iterator->path.narrow) { + PyErr_SetString(PyExc_TypeError, + "os.scandir() doesn't support bytes path on Windows, use Unicode instead"); + goto error; + } + iterator->first_time = 1; + + path_strW = join_path_filenameW(iterator->path.wide, L"*.*"); + if (!path_strW) + goto error; + + Py_BEGIN_ALLOW_THREADS + iterator->handle = FindFirstFileW(path_strW, &iterator->file_data); + Py_END_ALLOW_THREADS + + PyMem_Free(path_strW); + + if (iterator->handle == INVALID_HANDLE_VALUE) { + path_error(&iterator->path); + goto error; + } +#else /* POSIX */ + if (iterator->path.narrow) + path = iterator->path.narrow; + else + path = "."; + + errno = 0; + Py_BEGIN_ALLOW_THREADS + iterator->dirp = opendir(path); + Py_END_ALLOW_THREADS + + if (!iterator->dirp) { + path_error(&iterator->path); + goto error; + } +#endif + + return (PyObject *)iterator; + +error: + Py_DECREF(iterator); + return NULL; +} + + /*[clinic input] dump buffer [clinic start generated code]*/ @@ -17002,6 +17780,9 @@ {"get_blocking", posix_get_blocking, METH_VARARGS, get_blocking__doc__}, {"set_blocking", posix_set_blocking, METH_VARARGS, set_blocking__doc__}, #endif + {"scandir", (PyCFunction)posix_scandir, + METH_VARARGS | METH_KEYWORDS, + posix_scandir__doc__}, {NULL, NULL} /* Sentinel */ }; @@ -17444,15 +18225,6 @@ } -#ifdef MS_WINDOWS -#define INITFUNC PyInit_nt -#define MODNAME "nt" - -#else -#define INITFUNC PyInit_posix -#define MODNAME "posix" -#endif - static struct PyModuleDef posixmodule = { PyModuleDef_HEAD_INIT, MODNAME, @@ -17673,6 +18445,12 @@ if (PyStructSequence_InitType2(&TerminalSizeType, &TerminalSize_desc) < 0) return NULL; + + /* initialize scandir types */ + if (PyType_Ready(&ScandirIteratorType) < 0) + return NULL; + if (PyType_Ready(&DirEntryType) < 0) + return NULL; } #if defined(HAVE_WAITID) && !defined(__APPLE__) Py_INCREF((PyObject*) &WaitidResultType); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 03:01:26 2015 From: python-checkins at python.org (victor.stinner) Date: Sun, 08 Mar 2015 02:01:26 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2322524=3A_Fix_os?= =?utf-8?q?=2Escandir=28=29_for_platforms_which_don=27t_have_a_d=5Ftype_fi?= =?utf-8?q?eld_in?= Message-ID: <20150308020125.1317.71356@psf.io> https://hg.python.org/cpython/rev/60e5c34ec53a changeset: 94898:60e5c34ec53a user: Victor Stinner date: Sun Mar 08 02:59:09 2015 +0100 summary: Issue #22524: Fix os.scandir() for platforms which don't have a d_type field in the dirent structure (ex: OpenIndiana). files: Modules/posixmodule.c | 42 +++++++++++++++++++++--------- configure | 32 +++++++++++++++++++++++ configure.ac | 20 ++++++++++++++ pyconfig.h.in | 3 ++ 4 files changed, 84 insertions(+), 13 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -16364,7 +16364,9 @@ __int64 win32_file_index; int got_file_index; #else /* POSIX */ +#ifdef HAVE_DIRENT_D_TYPE unsigned char d_type; +#endif ino_t d_ino; #endif } DirEntry; @@ -16389,11 +16391,15 @@ { #ifdef MS_WINDOWS return (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK; -#else /* POSIX */ +#elif defined(HAVE_DIRENT_D_TYPE) + /* POSIX */ if (self->d_type != DT_UNKNOWN) return self->d_type == DT_LNK; else return DirEntry_test_mode(self, 0, S_IFLNK); +#else + /* POSIX without d_type */ + return DirEntry_test_mode(self, 0, S_IFLNK); #endif } @@ -16505,22 +16511,26 @@ PyObject *st_mode = NULL; long mode; int result; +#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) int is_symlink; int need_stat; +#endif +#ifdef MS_WINDOWS + unsigned long dir_bits; +#endif _Py_IDENTIFIER(st_mode); -#ifdef MS_WINDOWS - unsigned long dir_bits; -#endif #ifdef MS_WINDOWS is_symlink = (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK; need_stat = follow_symlinks && is_symlink; -#else /* POSIX */ +#elif defined(HAVE_DIRENT_D_TYPE) is_symlink = self->d_type == DT_LNK; need_stat = self->d_type == DT_UNKNOWN || (follow_symlinks && is_symlink); #endif +#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) if (need_stat) { +#endif stat = DirEntry_get_stat(self, follow_symlinks); if (!stat) { if (PyErr_ExceptionMatches(PyExc_FileNotFoundError)) { @@ -16541,6 +16551,7 @@ Py_CLEAR(st_mode); Py_CLEAR(stat); result = (mode & S_IFMT) == mode_bits; +#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) } else if (is_symlink) { assert(mode_bits != S_IFLNK); @@ -16561,6 +16572,7 @@ result = self->d_type == DT_REG; #endif } +#endif return result; @@ -16812,7 +16824,11 @@ static PyObject * DirEntry_from_posix_info(path_t *path, char *name, Py_ssize_t name_len, - unsigned char d_type, ino_t d_ino) + ino_t d_ino +#ifdef HAVE_DIRENT_D_TYPE + , unsigned char d_type +#endif + ) { DirEntry *entry; char *joined_path; @@ -16841,7 +16857,9 @@ if (!entry->name || !entry->path) goto error; +#ifdef HAVE_DIRENT_D_TYPE entry->d_type = d_type; +#endif entry->d_ino = d_ino; return (PyObject *)entry; @@ -16941,7 +16959,6 @@ struct dirent *direntp; Py_ssize_t name_len; int is_dot; - unsigned char d_type; /* Happens if the iterator is iterated twice */ if (!iterator->dirp) { @@ -16967,13 +16984,12 @@ is_dot = direntp->d_name[0] == '.' && (name_len == 1 || (direntp->d_name[1] == '.' && name_len == 2)); if (!is_dot) { -#if defined(__GLIBC__) && !defined(_DIRENT_HAVE_D_TYPE) - d_type = DT_UNKNOWN; /* System doesn't support d_type */ -#else - d_type = direntp->d_type; -#endif return DirEntry_from_posix_info(&iterator->path, direntp->d_name, - name_len, d_type, direntp->d_ino); + name_len, direntp->d_ino +#ifdef HAVE_DIRENT_D_TYPE + , direntp->d_type +#endif + ); } /* Loop till we get a non-dot directory or finish iterating */ diff --git a/configure b/configure --- a/configure +++ b/configure @@ -15798,6 +15798,38 @@ $as_echo "$ENSUREPIP" >&6; } +# check if the dirent structure of a d_type field and DT_UNKNOWN is defined +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the dirent structure of a d_type field" >&5 +$as_echo_n "checking if the dirent structure of a d_type field... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + #include + + int main() { + struct dirent entry; + return entry.d_type == DT_UNKNOWN; + } + + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + have_dirent_d_type=yes +else + have_dirent_d_type=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_dirent_d_type" >&5 +$as_echo "$have_dirent_d_type" >&6; } + +if test "$have_dirent_d_type" = yes; then + +$as_echo "#define HAVE_DIRENT_D_TYPE 1" >>confdefs.h + +fi + # generate output files ac_config_files="$ac_config_files Makefile.pre Modules/Setup.config Misc/python.pc Misc/python-config.sh" diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -4944,6 +4944,26 @@ AC_MSG_RESULT($ENSUREPIP) AC_SUBST(ENSUREPIP) +# check if the dirent structure of a d_type field and DT_UNKNOWN is defined +AC_MSG_CHECKING(if the dirent structure of a d_type field) +AC_LINK_IFELSE( +[ + AC_LANG_SOURCE([[ + #include + + int main() { + struct dirent entry; + return entry.d_type == DT_UNKNOWN; + } + ]]) +],[have_dirent_d_type=yes],[have_dirent_d_type=no]) +AC_MSG_RESULT($have_dirent_d_type) + +if test "$have_dirent_d_type" = yes; then + AC_DEFINE(HAVE_DIRENT_D_TYPE, 1, + [Define to 1 if the dirent structure has a d_type field]) +fi + # generate output files AC_CONFIG_FILES(Makefile.pre Modules/Setup.config Misc/python.pc Misc/python-config.sh) AC_CONFIG_FILES([Modules/ld_so_aix], [chmod +x Modules/ld_so_aix]) diff --git a/pyconfig.h.in b/pyconfig.h.in --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -183,6 +183,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DIRECT_H +/* Define to 1 if the dirent structure has a d_type field */ +#undef HAVE_DIRENT_D_TYPE + /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_DIRENT_H -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 03:06:35 2015 From: python-checkins at python.org (victor.stinner) Date: Sun, 08 Mar 2015 02:06:35 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_PEP_471=3A_The_implementation?= =?utf-8?q?_has_been_merged=2C_set_status_to_Final?= Message-ID: <20150308020635.11177.81708@psf.io> https://hg.python.org/peps/rev/bb6d43d2c727 changeset: 5723:bb6d43d2c727 user: Victor Stinner date: Sun Mar 08 03:06:31 2015 +0100 summary: PEP 471: The implementation has been merged, set status to Final files: pep-0471.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pep-0471.txt b/pep-0471.txt --- a/pep-0471.txt +++ b/pep-0471.txt @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: Ben Hoyt BDFL-Delegate: Victor Stinner -Status: Accepted +Status: Final Type: Standards Track Content-Type: text/x-rst Created: 30-May-2014 -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Sun Mar 8 03:16:51 2015 From: python-checkins at python.org (steve.dower) Date: Sun, 08 Mar 2015 02:16:51 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323524=3A_Change_b?= =?utf-8?q?ack_to_using_Windows_errors_for_=5FPy=5Ffstat_instead_of_the?= Message-ID: <20150308021651.1217.96622@psf.io> https://hg.python.org/cpython/rev/d8e49a2795e7 changeset: 94899:d8e49a2795e7 user: Steve Dower date: Sat Mar 07 18:14:07 2015 -0800 summary: Issue #23524: Change back to using Windows errors for _Py_fstat instead of the errno shim. files: Modules/_io/fileio.c | 8 +++++++- Modules/signalmodule.c | 2 +- Python/fileutils.c | 8 +++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -182,7 +182,13 @@ { #if defined(HAVE_FSTAT) || defined(MS_WINDOWS) struct _Py_stat_struct buf; - if (_Py_fstat(fd, &buf) < 0 && errno == EBADF) { + if (_Py_fstat(fd, &buf) < 0 && +#ifdef MS_WINDOWS + GetLastError() == ERROR_INVALID_HANDLE +#else + errno == EBADF +#endif + ) { PyObject *exc; char *msg = strerror(EBADF); exc = PyObject_CallFunction(PyExc_OSError, "(is)", diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -560,7 +560,7 @@ } if (_Py_fstat(fd, &st) != 0) { - PyErr_SetFromErrno(PyExc_OSError); + PyErr_SetExcFromWindowsErr(PyExc_OSError, GetLastError()); return NULL; } diff --git a/Python/fileutils.c b/Python/fileutils.c --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -637,10 +637,14 @@ else h = (HANDLE)_get_osfhandle(fd); + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ errno = 0; if (h == INVALID_HANDLE_VALUE) { - errno = EBADF; + /* This is really a C library error (invalid file handle). + We set the Win32 error to the closes one matching. */ + SetLastError(ERROR_INVALID_HANDLE); return -1; } memset(result, 0, sizeof(*result)); @@ -649,7 +653,6 @@ if (type == FILE_TYPE_UNKNOWN) { DWORD error = GetLastError(); if (error != 0) { - errno = EINVAL; return -1; } /* else: valid but unknown file */ @@ -664,7 +667,6 @@ } if (!GetFileInformationByHandle(h, &info)) { - errno = EINVAL; return -1; } -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Sun Mar 8 04:53:29 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 08 Mar 2015 04:53:29 +0100 Subject: [Python-checkins] Daily reference leaks (d8e49a2795e7): sum=0 Message-ID: results for d8e49a2795e7 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogdi8RB5', '--timeout', '120', '-x', 'test_eintr'] From python-checkins at python.org Sun Mar 8 05:32:20 2015 From: python-checkins at python.org (steve.dower) Date: Sun, 08 Mar 2015 04:32:20 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Suppress_assert_dialogs_in?= =?utf-8?b?IHRlc3RfY21kX2xpbmUu?= Message-ID: <20150308043220.1195.72499@psf.io> https://hg.python.org/cpython/rev/275032693b00 changeset: 94900:275032693b00 user: Steve Dower date: Sat Mar 07 20:32:16 2015 -0800 summary: Suppress assert dialogs in test_cmd_line. files: Lib/test/test_cmd_line.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -340,7 +340,8 @@ # Issue #5319: if stdout.flush() fails at shutdown, an error should # be printed out. code = """if 1: - import os, sys + import os, sys, test.support + test.support.SuppressCrashReport().__enter__() sys.stdout.write('x') os.close(sys.stdout.fileno())""" rc, out, err = assert_python_ok('-c', code) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 06:25:57 2015 From: python-checkins at python.org (steve.dower) Date: Sun, 08 Mar 2015 05:25:57 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Suppress_assert_dialogs_in?= =?utf-8?q?_test=5Fos?= Message-ID: <20150308052557.7573.26022@psf.io> https://hg.python.org/cpython/rev/3e12444adf1e changeset: 94901:3e12444adf1e user: Steve Dower date: Sat Mar 07 21:25:54 2015 -0800 summary: Suppress assert dialogs in test_os files: Lib/test/test_os.py | 24 ++++++++++++++---------- 1 files changed, 14 insertions(+), 10 deletions(-) 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 @@ -1199,8 +1199,10 @@ code = """if 1: import os import sys + import test.support os.urandom(4) - os.closerange(3, 256) + with test.support.SuppressCrashReport(): + os.closerange(3, 256) sys.stdout.buffer.write(os.urandom(4)) """ rc, out, err = assert_python_ok('-Sc', code) @@ -1214,16 +1216,18 @@ code = """if 1: import os import sys + import test.support os.urandom(4) - for fd in range(3, 256): - try: - os.close(fd) - except OSError: - pass - else: - # Found the urandom fd (XXX hopefully) - break - os.closerange(3, 256) + with test.support.SuppressCrashReport(): + for fd in range(3, 256): + try: + os.close(fd) + except OSError: + pass + else: + # Found the urandom fd (XXX hopefully) + break + os.closerange(3, 256) with open({TESTFN!r}, 'rb') as f: os.dup2(f.fileno(), fd) sys.stdout.buffer.write(os.urandom(4)) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 08:18:37 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 08 Mar 2015 07:18:37 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2321619=3A_Cleaned_up_test=5Fbroken=5Fpipe=5Fclea?= =?utf-8?q?nup=2E?= Message-ID: <20150308071837.8067.9819@psf.io> https://hg.python.org/cpython/rev/41ce95a5b2d8 changeset: 94903:41ce95a5b2d8 parent: 94901:3e12444adf1e parent: 94902:4ea40dc3d26d user: Serhiy Storchaka date: Sun Mar 08 09:17:28 2015 +0200 summary: Issue #21619: Cleaned up test_broken_pipe_cleanup. Patch by Martin Panter. files: Lib/test/test_subprocess.py | 18 ++++++++---------- 1 files changed, 8 insertions(+), 10 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 @@ -2504,21 +2504,19 @@ def test_broken_pipe_cleanup(self): """Broken pipe error should not prevent wait() (Issue 21619)""" - args = [sys.executable, "-c", - "import sys;" - "sys.stdin.close();" - "sys.stdout.close();"] # Signals that input pipe is closed - proc = subprocess.Popen(args, + proc = subprocess.Popen([sys.executable, '-c', 'pass'], stdin=subprocess.PIPE, - stdout=subprocess.PIPE, bufsize=support.PIPE_MAX_SIZE*2) - proc.stdout.read() # Make sure subprocess has closed its input - proc.stdin.write(b"x" * support.PIPE_MAX_SIZE) + proc = proc.__enter__() + # Prepare to send enough data to overflow any OS pipe buffering and + # guarantee a broken pipe error. Data is held in BufferedWriter + # buffer until closed. + proc.stdin.write(b'x' * support.PIPE_MAX_SIZE) self.assertIsNone(proc.returncode) + # EPIPE expected under POSIX; EINVAL under Windows self.assertRaises(OSError, proc.__exit__, None, None, None) - self.assertEqual(0, proc.returncode) + self.assertEqual(proc.returncode, 0) self.assertTrue(proc.stdin.closed) - self.assertTrue(proc.stdout.closed) def test_main(): -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 08:18:37 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sun, 08 Mar 2015 07:18:37 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIxNjE5?= =?utf-8?q?=3A_Cleaned_up_test=5Fbroken=5Fpipe=5Fcleanup=2E?= Message-ID: <20150308071836.8069.45152@psf.io> https://hg.python.org/cpython/rev/4ea40dc3d26d changeset: 94902:4ea40dc3d26d branch: 3.4 parent: 94895:40e8a8e83ed0 user: Serhiy Storchaka date: Sun Mar 08 09:16:40 2015 +0200 summary: Issue #21619: Cleaned up test_broken_pipe_cleanup. Patch by Martin Panter. files: Lib/test/test_subprocess.py | 18 ++++++++---------- 1 files changed, 8 insertions(+), 10 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 @@ -2523,21 +2523,19 @@ def test_broken_pipe_cleanup(self): """Broken pipe error should not prevent wait() (Issue 21619)""" - args = [sys.executable, "-c", - "import sys;" - "sys.stdin.close();" - "sys.stdout.close();"] # Signals that input pipe is closed - proc = subprocess.Popen(args, + proc = subprocess.Popen([sys.executable, '-c', 'pass'], stdin=subprocess.PIPE, - stdout=subprocess.PIPE, bufsize=support.PIPE_MAX_SIZE*2) - proc.stdout.read() # Make sure subprocess has closed its input - proc.stdin.write(b"x" * support.PIPE_MAX_SIZE) + proc = proc.__enter__() + # Prepare to send enough data to overflow any OS pipe buffering and + # guarantee a broken pipe error. Data is held in BufferedWriter + # buffer until closed. + proc.stdin.write(b'x' * support.PIPE_MAX_SIZE) self.assertIsNone(proc.returncode) + # EPIPE expected under POSIX; EINVAL under Windows self.assertRaises(OSError, proc.__exit__, None, None, None) - self.assertEqual(0, proc.returncode) + self.assertEqual(proc.returncode, 0) self.assertTrue(proc.stdin.closed) - self.assertTrue(proc.stdout.closed) def test_main(): -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 14:26:11 2015 From: python-checkins at python.org (stefan.krah) Date: Sun, 08 Mar 2015 13:26:11 +0000 Subject: [Python-checkins] =?utf-8?q?devguide=3A_Add_myself_to_some_areas?= =?utf-8?q?=2E?= Message-ID: <20150308132611.1795.34519@psf.io> https://hg.python.org/devguide/rev/dc5663b88896 changeset: 731:dc5663b88896 user: Stefan Krah date: Sun Mar 08 14:25:47 2015 +0100 summary: Add myself to some areas. files: experts.rst | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/experts.rst b/experts.rst --- a/experts.rst +++ b/experts.rst @@ -50,6 +50,7 @@ __main__ gvanrossum, ncoghlan _dummy_thread brett.cannon _thread pitrou +_testbuffer skrah abc aifc r.david.murray argparse bethard @@ -93,7 +94,7 @@ curses datetime belopolsky dbm -decimal facundobatista, rhettinger, mark.dickinson +decimal facundobatista, rhettinger, mark.dickinson, skrah difflib tim.peters (inactive) dis ncoghlan* distutils eric.araujo, dstufft @@ -139,6 +140,7 @@ json bob.ippolito (inactive), ezio.melotti, rhettinger, pitrou keyword lib2to3 benjamin.peterson +libmpdec skrah linecache locale loewis, lemburg logging vinay.sajip @@ -148,6 +150,7 @@ mailcap marshal math mark.dickinson, rhettinger, stutzbach +memoryview skrah mimetypes mmap modulefinder theller (inactive), jvr -- Repository URL: https://hg.python.org/devguide From python-checkins at python.org Sun Mar 8 14:42:47 2015 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 08 Mar 2015 13:42:47 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40ICgjMjM2MDgp?= Message-ID: <20150308134247.7587.22127@psf.io> https://hg.python.org/cpython/rev/ce67f965a37d changeset: 94905:ce67f965a37d parent: 94903:41ce95a5b2d8 parent: 94904:8264652bce68 user: Benjamin Peterson date: Sun Mar 08 09:42:40 2015 -0400 summary: merge 3.4 (#23608) files: Doc/library/ssl.rst | 2 +- 1 files changed, 1 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 @@ -556,7 +556,7 @@ prefer trusted certificates when building the trust chain to validate a certificate. This flag is enabled by default. - .. versionadded:: 3.4.5 + .. versionadded:: 3.4.4 .. data:: PROTOCOL_SSLv23 -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 14:42:48 2015 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 08 Mar 2015 13:42:48 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_indicate_corre?= =?utf-8?q?ct_version_=28closes_=2323608=29?= Message-ID: <20150308134247.1351.99796@psf.io> https://hg.python.org/cpython/rev/8264652bce68 changeset: 94904:8264652bce68 branch: 3.4 parent: 94902:4ea40dc3d26d user: Benjamin Peterson date: Sun Mar 08 09:42:25 2015 -0400 summary: indicate correct version (closes #23608) files: Doc/library/ssl.rst | 2 +- 1 files changed, 1 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 @@ -535,7 +535,7 @@ prefer trusted certificates when building the trust chain to validate a certificate. This flag is enabled by default. - .. versionadded:: 3.4.5 + .. versionadded:: 3.4.4 .. data:: PROTOCOL_SSLv23 -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 20:48:49 2015 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 08 Mar 2015 19:48:49 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2322980=3A_Under_Li?= =?utf-8?q?nux=2C_C_extensions_now_include_bitness_in_the_file_name=2C?= Message-ID: <20150308194849.16663.61508@psf.io> https://hg.python.org/cpython/rev/25356d34b79b changeset: 94906:25356d34b79b user: Antoine Pitrou date: Sun Mar 08 20:43:10 2015 +0100 summary: Issue #22980: Under Linux, C extensions now include bitness in the file name, to make it easy to test 32-bit and 64-bit builds in the same working tree. files: Lib/test/test_sysconfig.py | 6 ++++++ Misc/NEWS | 4 ++++ configure | 10 +++++++++- configure.ac | 10 +++++++++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -389,6 +389,12 @@ self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) + @unittest.skipUnless(sys.platform == 'linux', 'Linux-specific test') + def test_bitness_in_ext_suffix(self): + suffix = sysconfig.get_config_var('EXT_SUFFIX') + bitness = '-32b' if sys.maxsize < 2**32 else '-64b' + self.assertTrue(suffix.endswith(bitness + '.so'), suffix) + class MakefileTests(unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #22980: Under Linux, C extensions now include bitness in the file + name, to make it easy to test 32-bit and 64-bit builds in the same + working tree. + - Issue #23571: PyObject_Call() and PyCFunction_Call() now raise a SystemError if a function returns a result and raises an exception. The SystemError is chained to the previous exception. diff --git a/configure b/configure --- a/configure +++ b/configure @@ -14200,7 +14200,15 @@ $as_echo "$ABIFLAGS" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking SOABI" >&5 $as_echo_n "checking SOABI... " >&6; } -SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS} + +case $ac_sys_system in + Linux*|GNU*) + BITNESS_SUFFIX=-$(($ac_cv_sizeof_void_p * 8))b;; + *) + BITNESS_SUFFIX=;; +esac +SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${BITNESS_SUFFIX} + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SOABI" >&5 $as_echo "$SOABI" >&6; } diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -4175,7 +4175,15 @@ AC_MSG_CHECKING(ABIFLAGS) AC_MSG_RESULT($ABIFLAGS) AC_MSG_CHECKING(SOABI) -SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS} + +case $ac_sys_system in + Linux*|GNU*) + BITNESS_SUFFIX=-$(($ac_cv_sizeof_void_p * 8))b;; + *) + BITNESS_SUFFIX=;; +esac +SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${BITNESS_SUFFIX} + AC_MSG_RESULT($SOABI) AC_SUBST(EXT_SUFFIX) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 20:59:27 2015 From: python-checkins at python.org (steve.dower) Date: Sun, 08 Mar 2015 19:59:27 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Minor_updates_to_Windows_i?= =?utf-8?q?nstaller_upload_script=2E?= Message-ID: <20150308195927.117223.58783@psf.io> https://hg.python.org/cpython/rev/3d67df9ccd18 changeset: 94907:3d67df9ccd18 user: Steve Dower date: Sun Mar 08 12:59:00 2015 -0700 summary: Minor updates to Windows installer upload script. files: Tools/msi/uploadrelease.bat | 3 ++- Tools/msi/uploadrelease.proj | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Tools/msi/uploadrelease.bat b/Tools/msi/uploadrelease.bat --- a/Tools/msi/uploadrelease.bat +++ b/Tools/msi/uploadrelease.bat @@ -30,9 +30,10 @@ echo Found pscp.exe at %PSCP% call "%PCBUILD%env.bat" > nul 2> nul +pushd "%D%" msbuild /v:m /nologo uploadrelease.proj /t:Upload /p:Platform=x86 msbuild /v:m /nologo uploadrelease.proj /t:Upload /p:Platform=x64 /p:IncludeDoc=false - +popd exit /B 0 :Help diff --git a/Tools/msi/uploadrelease.proj b/Tools/msi/uploadrelease.proj --- a/Tools/msi/uploadrelease.proj +++ b/Tools/msi/uploadrelease.proj @@ -41,14 +41,14 @@ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sun Mar 8 23:29:48 2015 From: python-checkins at python.org (steve.dower) Date: Sun, 08 Mar 2015 22:29:48 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323612=3A_Fixes_up?= =?utf-8?q?grade_code_and_version_for_Windows_installer=2E?= Message-ID: <20150308222948.7583.12977@psf.io> https://hg.python.org/cpython/rev/97e01e107591 changeset: 94908:97e01e107591 user: Steve Dower date: Sun Mar 08 15:29:39 2015 -0700 summary: Issue #23612: Fixes upgrade code and version for Windows installer. files: Tools/msi/bundle/Default.thm | 2 +- Tools/msi/bundle/bundle.wxs | 4 ++-- Tools/msi/msi.props | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tools/msi/bundle/Default.thm b/Tools/msi/bundle/Default.thm --- a/Tools/msi/bundle/Default.thm +++ b/Tools/msi/bundle/Default.thm @@ -115,7 +115,7 @@ #(loc.FailureHyperlinkLogText) - Failure Message + #(loc.FailureRestartText) diff --git a/Tools/msi/bundle/bundle.wxs b/Tools/msi/bundle/bundle.wxs --- a/Tools/msi/bundle/bundle.wxs +++ b/Tools/msi/bundle/bundle.wxs @@ -2,11 +2,11 @@ diff --git a/Tools/msi/msi.props b/Tools/msi/msi.props --- a/Tools/msi/msi.props +++ b/Tools/msi/msi.props @@ -62,7 +62,7 @@ 64-bit $(DefineConstants); - Version=$(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber).$(RevisionNumber); + Version=$(MajorVersionNumber).$(MinorVersionNumber).$(Field3Value).0; ShortVersion=$(MajorVersionNumber).$(MinorVersionNumber); LongVersion=$(PythonVersion); MajorVersionNumber=$(MajorVersionNumber); @@ -142,7 +142,7 @@ - <_Uuids>@(_Uuid->'("%(Identity)", "%(Uri)")',',') + <_Uuids>@(_Uuid->'("%(Identity)", "$(MajorVersionNumber).$(MinorVersionNumber)/%(Uri)")',',') <_GenerateCommand>import uuid; print('\n'.join('{}={}'.format(i, uuid.uuid5(uuid.UUID('c8d9733e-a70c-43ff-ab0c-e26456f11083'), '$(ReleaseUri)' + j)) for i,j in [$(_Uuids.Replace(`"`,`'`))])) -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Mon Mar 9 04:54:52 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 09 Mar 2015 04:54:52 +0100 Subject: [Python-checkins] Daily reference leaks (97e01e107591): sum=0 Message-ID: results for 97e01e107591 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflogcdzB4j', '--timeout', '120', '-x', 'test_eintr'] From python-checkins at python.org Mon Mar 9 09:56:08 2015 From: python-checkins at python.org (ned.deily) Date: Mon, 09 Mar 2015 08:56:08 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323617=3A_Correct_plurals_typo_noted_by_Kentrell?= =?utf-8?q?_Johnson=2E?= Message-ID: <20150309085608.1175.40874@psf.io> https://hg.python.org/cpython/rev/82df13f0ea9b changeset: 94911:82df13f0ea9b parent: 94908:97e01e107591 parent: 94910:e623283e4dd6 user: Ned Deily date: Mon Mar 09 01:55:35 2015 -0700 summary: Issue #23617: Correct plurals typo noted by Kentrell Johnson. files: Doc/library/index.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/index.rst b/Doc/library/index.rst --- a/Doc/library/index.rst +++ b/Doc/library/index.rst @@ -20,7 +20,7 @@ encourage and enhance the portability of Python programs by abstracting away platform-specifics into platform-neutral APIs. -The Python installers for the Windows platform usually includes +The Python installers for the Windows platform usually include the entire standard library and often also include many additional components. For Unix-like operating systems Python is normally provided as a collection of packages, so it may be necessary to use the packaging -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 09:56:08 2015 From: python-checkins at python.org (ned.deily) Date: Mon, 09 Mar 2015 08:56:08 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNjE3?= =?utf-8?q?=3A_Correct_plurals_typo_noted_by_Kentrell_Johnson=2E?= Message-ID: <20150309085608.9666.11964@psf.io> https://hg.python.org/cpython/rev/e623283e4dd6 changeset: 94910:e623283e4dd6 branch: 3.4 parent: 94904:8264652bce68 user: Ned Deily date: Mon Mar 09 01:55:02 2015 -0700 summary: Issue #23617: Correct plurals typo noted by Kentrell Johnson. files: Doc/library/index.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/index.rst b/Doc/library/index.rst --- a/Doc/library/index.rst +++ b/Doc/library/index.rst @@ -20,7 +20,7 @@ encourage and enhance the portability of Python programs by abstracting away platform-specifics into platform-neutral APIs. -The Python installers for the Windows platform usually includes +The Python installers for the Windows platform usually include the entire standard library and often also include many additional components. For Unix-like operating systems Python is normally provided as a collection of packages, so it may be necessary to use the packaging -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 09:56:08 2015 From: python-checkins at python.org (ned.deily) Date: Mon, 09 Mar 2015 08:56:08 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIzNjE3?= =?utf-8?q?=3A_Correct_plurals_typo_noted_by_Kentrell_Johnson=2E?= Message-ID: <20150309085608.11167.81414@psf.io> https://hg.python.org/cpython/rev/17253605eca4 changeset: 94909:17253605eca4 branch: 2.7 parent: 94892:4ad1ea911fd0 user: Ned Deily date: Mon Mar 09 01:54:25 2015 -0700 summary: Issue #23617: Correct plurals typo noted by Kentrell Johnson. files: Doc/library/index.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/index.rst b/Doc/library/index.rst --- a/Doc/library/index.rst +++ b/Doc/library/index.rst @@ -20,7 +20,7 @@ encourage and enhance the portability of Python programs by abstracting away platform-specifics into platform-neutral APIs. -The Python installers for the Windows platform usually includes +The Python installers for the Windows platform usually include the entire standard library and often also include many additional components. For Unix-like operating systems Python is normally provided as a collection of packages, so it may be necessary to use the packaging -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 10:40:37 2015 From: python-checkins at python.org (larry.hastings) Date: Mon, 09 Mar 2015 09:40:37 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?b?KTogTWVyZ2Uu?= Message-ID: <20150309094037.1791.72983@psf.io> https://hg.python.org/cpython/rev/421c3943dfc3 changeset: 94915:421c3943dfc3 parent: 94914:d0cdc70e1f7a parent: 94912:871afe5dab48 user: Larry Hastings date: Sun Mar 08 00:57:04 2015 -0800 summary: Merge. files: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 10:40:37 2015 From: python-checkins at python.org (larry.hastings) Date: Mon, 09 Mar 2015 09:40:37 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?b?KTogTWVyZ2Uu?= Message-ID: <20150309094037.9660.34154@psf.io> https://hg.python.org/cpython/rev/871afe5dab48 changeset: 94912:871afe5dab48 parent: 94565:af285e031bca parent: 94554:45ba5de2711b user: Larry Hastings date: Sat Feb 07 16:21:34 2015 -0800 summary: Merge. files: Tools/msi/buildrelease.bat | 6 + Tools/msi/bundle/bundle.targets | 2 +- Tools/msi/get_wix.py | 2 +- Tools/msi/testrelease.bat | 80 +++++++++++++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -22,6 +22,7 @@ set BUILDX86= set BUILDX64= set TARGET=Rebuild +set TESTTARGETDIR= :CheckOpts @@ -30,6 +31,7 @@ if "%1" EQU "-D" (set SKIPDOC=1) && shift && goto CheckOpts if "%1" EQU "-B" (set SKIPBUILD=1) && shift && goto CheckOpts if "%1" EQU "--download" (set DOWNLOAD_URL=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--test" (set TESTTARGETDIR=%~2) && shift && shift && goto CheckOpts if "%1" EQU "-b" (set TARGET=Build) && shift && goto CheckOpts if '%1' EQU '-x86' (set BUILDX86=1) && shift && goto CheckOpts if '%1' EQU '-x64' (set BUILDX64=1) && shift && goto CheckOpts @@ -66,6 +68,10 @@ if errorlevel 1 exit /B ) +if defined TESTTARGETDIR ( + call "%D%testrelease.bat" -t "%TESTTARGETDIR%" +) + exit /B 0 :build diff --git a/Tools/msi/bundle/bundle.targets b/Tools/msi/bundle/bundle.targets --- a/Tools/msi/bundle/bundle.targets +++ b/Tools/msi/bundle/bundle.targets @@ -14,7 +14,7 @@ $(OutputPath)en-us\ $(OutputPath) - $(DownloadUrlBase.TrimEnd(`/`))/$(PythonVersion)/$(ArchName)/{2} + $(DownloadUrlBase.TrimEnd(`/`))/$(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber)/$(ArchName)$(ReleaseLevelName)/{2} $(DefineConstants);DownloadUrl=$(DownloadUrl) $(DefineConstants);DownloadUrl={2} diff --git a/Tools/msi/get_wix.py b/Tools/msi/get_wix.py --- a/Tools/msi/get_wix.py +++ b/Tools/msi/get_wix.py @@ -22,7 +22,7 @@ print("Cannot find project root") sys.exit(1) -WIX_BINARIES_ZIP = 'http://wixtoolset.org/downloads/v3.10.1124.0/wix310-binaries.zip' +WIX_BINARIES_ZIP = 'http://wixtoolset.org/downloads/v3.10.0.1403/wix310-binaries.zip' TARGET_BIN_ZIP = EXTERNALS_DIR / "wix.zip" TARGET_BIN_DIR = EXTERNALS_DIR / "wix" diff --git a/Tools/msi/testrelease.bat b/Tools/msi/testrelease.bat new file mode 100644 --- /dev/null +++ b/Tools/msi/testrelease.bat @@ -0,0 +1,80 @@ + at setlocal + at echo off + +set D=%~dp0 +set PCBUILD=%D%..\..\PCBuild\ + +set TARGETDIR=%TEMP% +set TESTX86= +set TESTX64= +set TESTALLUSER= +set TESTPERUSER= + +:CheckOpts +if '%1' EQU '-x86' (set TESTX86=1) && shift && goto CheckOpts +if '%1' EQU '-x64' (set TESTX64=1) && shift && goto CheckOpts +if '%1' EQU '-t' (set TARGETDIR=%~2) && shift && shift && goto CheckOpts +if '%1' EQU '-a' (set TESTALLUSER=1) && shift && goto CheckOpts +if '%1' EQU '-p' (set TESTPERUSER=1) && shift && goto CheckOpts + +if not defined TESTX86 if not defined TESTX64 (set TESTX86=1) && (set TESTX64=1) +if not defined TESTALLUSER if not defined TESTPERUSER (set TESTALLUSER=1) && (set TESTPERUSER=1) + + +if defined TESTX86 ( + for %%f in ("%PCBUILD%win32\en-us\*.exe") do ( + if defined TESTALLUSER call :test "%%~ff" "%TARGETDIR%\%%~nf-alluser" InstallAllUsers=1 + if defined TESTPERUSER call :test "%%~ff" "%TARGETDIR%\%%~nf-peruser" InstallAllUsers=0 + if errorlevel 1 exit /B + ) +) + +if defined TESTX64 ( + for %%f in ("%PCBUILD%amd64\en-us\*.exe") do ( + if defined TESTALLUSER call :test "%%~ff" "%TARGETDIR%\%%~nf-alluser" InstallAllUsers=1 + if defined TESTPERUSER call :test "%%~ff" "%TARGETDIR%\%%~nf-peruser" InstallAllUsers=0 + if errorlevel 1 exit /B + ) +) + +exit /B 0 + +:test + at setlocal + at echo on + + at if not exist "%~1" exit /B 1 + + at set EXITCODE=0 + at echo Installing %1 into %2 +"%~1" /passive /log "%~2\install\log.txt" %~3 TargetDir="%~2\Python" Include_debug=1 Include_symbols=1 CompileAll=1 + + at if not errorlevel 1 ( + @echo Printing version + "%~2\Python\python.exe" -c "import sys; print(sys.version)" > "%~2\version.txt" 2>&1 +) + at if not errorlevel 1 ( + @echo Installing package + "%~2\Python\python.exe" -m pip install azure > "%~2\pip.txt" 2>&1 + @if not errorlevel 1 ( + "%~2\Python\python.exe" -m pip uninstall -y azure python-dateutil six > "%~2\pip.txt" 2>&1 + ) +) + at if not errorlevel 1 ( + @echo Testing Tcl/tk + @set TCL_LIBRARY=%~2\Python\tcl\tcl8.6 + "%~2\Python\python.exe" -m test -uall -v test_ttk_guionly test_tk test_idle > "%~2\tcltk.txt" 2>&1 + @set TCL_LIBRARY= +) + + at set EXITCODE=%ERRORLEVEL% + + at for /d %%f in ("%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Python*") do @dir "%%~ff\*.lnk" /s/b > "%~2\startmenu.txt" 2>&1 + at for /d %%f in ("%APPDATA%\Microsoft\Windows\Start Menu\Programs\Python*") do @dir "%%~ff\*.lnk" /s/b >> "%~2\startmenu.txt" 2>&1 + + at echo Result was %EXITCODE% + at echo Removing %1 +"%~1" /passive /uninstall /log "%~2\uninstall\log.txt" + + at echo off +exit /B %EXITCODE% -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 10:40:37 2015 From: python-checkins at python.org (larry.hastings) Date: Mon, 09 Mar 2015 09:40:37 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Release_bump_for_3=2E5=2E0?= =?utf-8?b?YTIu?= Message-ID: <20150309094037.19083.50426@psf.io> https://hg.python.org/cpython/rev/0337bd7ebcb6 changeset: 94913:0337bd7ebcb6 tag: v3.5.0a2 parent: 94903:41ce95a5b2d8 user: Larry Hastings date: Sun Mar 08 00:24:34 2015 -0800 summary: Release bump for 3.5.0a2. files: Include/patchlevel.h | 4 ++-- Lib/distutils/__init__.py | 2 +- Lib/idlelib/idlever.py | 2 +- Lib/pydoc_data/topics.py | 6 +++--- README | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 5 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 1 +#define PY_RELEASE_SERIAL 2 /* Version as a string */ -#define PY_VERSION "3.5.0a1+" +#define PY_VERSION "3.5.0a2" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py --- a/Lib/distutils/__init__.py +++ b/Lib/distutils/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a1" +__version__ = "3.5.0a2" #--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 = "3.5.0a1" +IDLE_VERSION = "3.5.0a2" 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,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Sat Feb 7 15:53:56 2015 +# Autogenerated by Sphinx on Sun Mar 8 00:21:33 2015 topics = {'assert': u'\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 to\n\n if __debug__:\n if not expression1: raise AssertionError(expression2)\n\nThese equivalences assume that "__debug__" and "AssertionError" refer\nto 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 (command\nline option -O). The current code generator emits no code for an\nassert statement when optimization is requested at compile time. Note\nthat it is unnecessary to include the source code for the expression\nthat failed in the error message; it will be displayed as part of the\nstack trace.\n\nAssignments to "__debug__" are illegal. The value for the built-in\nvariable is determined when the interpreter starts.\n', 'assignment': u'\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 | "*" target\n\n(See section *Primaries* for the syntax definitions for\n*attributeref*, *subscription*, and *slicing*.)\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, optionally enclosed in\nparentheses or square brackets, is recursively defined as follows.\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\n object must be an iterable with the same number of items as there\n are targets in the target list, and the items are assigned, from\n left to right, to the corresponding targets.\n\n * If the target list contains one target prefixed with an\n asterisk, called a "starred" target: The object must be a sequence\n with at least as many items as there are targets in the target\n list, minus one. The first items of the sequence are assigned,\n from left to right, to the targets before the starred target. The\n final items of the sequence are assigned to the targets after the\n starred target. A list of the remaining items in the sequence is\n then assigned to the starred target (the list can be empty).\n\n * Else: The object must be a sequence with the same number of\n items as there are targets in the target list, and the items are\n assigned, from left to 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" or "nonlocal" statement\n in the current code block: the name is bound to the object in the\n current local namespace.\n\n * Otherwise: the name is bound to the object in the global\n namespace or the outer namespace determined by "nonlocal",\n respectively.\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\n square brackets: The object must be an iterable with the same number\n of 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 always\n set as an instance attribute, creating it if necessary. Thus, the\n two occurrences of "a.x" do not necessarily refer to the same\n attribute: if the RHS expression refers to a class attribute, the\n 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 an integer. If it is negative, the sequence\'s\n length is added to it. The resulting value must be a nonnegative\n integer less than the sequence\'s length, and the sequence is asked\n to assign the assigned object to its item with that index. If the\n index is out of range, "IndexError" is raised (assignment to a\n subscripted sequence cannot add new items to a 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 For user-defined objects, the "__setitem__()" method is called with\n appropriate arguments.\n\n* If the target is a slicing: The primary expression in the\n reference is evaluated. It should yield a mutable sequence object\n (such as a list). The assigned object should be a sequence object\n of the same type. Next, the lower and upper bound expressions are\n evaluated, insofar they are present; defaults are zero and the\n sequence\'s length. The bounds should evaluate to integers. If\n either 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 target\n sequence 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\nAlthough the definition of assignment implies that overlaps between\nthe left-hand side and the right-hand side are \'simultanenous\' (for\nexample "a, b = b, a" swaps two variables), overlaps *within* the\ncollection of assigned-to variables occur left-to-right, sometimes\nresulting in confusion. For instance, the following program prints\n"[0, 2]":\n\n x = [0, 1]\n i = 0\n i, x[i] = 1, 2 # i is updated, then x[i] is updated\n print(x)\n\nSee also: **PEP 3132** - Extended Iterable Unpacking\n\n The specification for the "*target" feature.\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 of 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 the\naugmented version, "x" is only evaluated once. Also, when possible,\nthe actual operation is performed *in-place*, meaning that rather than\ncreating a new object and assigning that to the target, the old object\nis modified instead.\n\nUnlike normal assignments, augmented assignments evaluate the left-\nhand side *before* evaluating the right-hand side. For example, "a[i]\n+= f(x)" first looks-up "a[i]", then it evaluates "f(x)" and performs\nthe addition, and lastly, it writes the result back to "a[i]".\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': u'\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 "__spam"\noccurring in a class named "Ham" will be transformed to "_Ham__spam".\nThis transformation is independent of the syntactical context in which\nthe identifier is used. If the transformed name is extremely long\n(longer than 255 characters), implementation defined truncation may\nhappen. If the class name consists only of underscores, no\ntransformation is done.\n', @@ -18,7 +18,7 @@ 'callable-types': u'\nEmulating callable objects\n**************************\n\nobject.__call__(self[, args...])\n\n Called when the instance is "called" as a function; if this method\n is defined, "x(arg1, arg2, ...)" is a shorthand for\n "x.__call__(arg1, arg2, ...)".\n', 'calls': u'\nCalls\n*****\n\nA call calls a callable object (e.g., a *function*) with a possibly\nempty series of *arguments*:\n\n call ::= primary "(" [argument_list [","] | comprehension] ")"\n argument_list ::= positional_arguments ["," keyword_arguments]\n ["," "*" expression] ["," keyword_arguments]\n ["," "**" expression]\n | keyword_arguments ["," "*" expression]\n ["," keyword_arguments] ["," "**" expression]\n | "*" expression ["," keyword_arguments] ["," "**" expression]\n | "**" expression\n positional_arguments ::= expression ("," expression)*\n keyword_arguments ::= keyword_item ("," keyword_item)*\n keyword_item ::= identifier "=" expression\n\nAn optional trailing comma may be present after the positional and\nkeyword arguments but does not affect the semantics.\n\nThe primary must evaluate to a callable object (user-defined\nfunctions, built-in functions, methods of built-in objects, class\nobjects, methods of class instances, and all objects having a\n"__call__()" method are callable). All argument expressions are\nevaluated before the call is attempted. Please refer to section\n*Function definitions* for the syntax of formal *parameter* lists.\n\nIf keyword arguments are present, they are first converted to\npositional arguments, as follows. First, a list of unfilled slots is\ncreated for the formal parameters. If there are N positional\narguments, they are placed in the first N slots. Next, for each\nkeyword argument, the identifier is used to determine the\ncorresponding slot (if the identifier is the same as the first formal\nparameter name, the first slot is used, and so on). If the slot is\nalready filled, a "TypeError" exception is raised. Otherwise, the\nvalue of the argument is placed in the slot, filling it (even if the\nexpression is "None", it fills the slot). When all arguments have\nbeen processed, the slots that are still unfilled are filled with the\ncorresponding default value from the function definition. (Default\nvalues are calculated, once, when the function is defined; thus, a\nmutable object such as a list or dictionary used as default value will\nbe shared by all calls that don\'t specify an argument value for the\ncorresponding slot; this should usually be avoided.) If there are any\nunfilled slots for which no default value is specified, a "TypeError"\nexception is raised. Otherwise, the list of filled slots is used as\nthe argument list for the call.\n\n**CPython implementation detail:** An implementation may provide\nbuilt-in functions whose positional parameters do not have names, even\nif they are \'named\' for the purpose of documentation, and which\ntherefore cannot be supplied by keyword. In CPython, this is the case\nfor functions implemented in C that use "PyArg_ParseTuple()" to parse\ntheir arguments.\n\nIf there are more positional arguments than there are formal parameter\nslots, a "TypeError" exception is raised, unless a formal parameter\nusing the syntax "*identifier" is present; in this case, that formal\nparameter receives a tuple containing the excess positional arguments\n(or an empty tuple if there were no excess positional arguments).\n\nIf any keyword argument does not correspond to a formal parameter\nname, a "TypeError" exception is raised, unless a formal parameter\nusing the syntax "**identifier" is present; in this case, that formal\nparameter receives a dictionary containing the excess keyword\narguments (using the keywords as keys and the argument values as\ncorresponding values), or a (new) empty dictionary if there were no\nexcess keyword arguments.\n\nIf the syntax "*expression" appears in the function call, "expression"\nmust evaluate to an iterable. Elements from this iterable are treated\nas if they were additional positional arguments; if there are\npositional arguments *x1*, ..., *xN*, and "expression" evaluates to a\nsequence *y1*, ..., *yM*, this is equivalent to a call with M+N\npositional arguments *x1*, ..., *xN*, *y1*, ..., *yM*.\n\nA consequence of this is that although the "*expression" syntax may\nappear *after* some keyword arguments, it is processed *before* the\nkeyword arguments (and the "**expression" argument, if any -- see\nbelow). So:\n\n >>> def f(a, b):\n ... print(a, b)\n ...\n >>> f(b=1, *(2,))\n 2 1\n >>> f(a=1, *(2,))\n Traceback (most recent call last):\n File "", line 1, in ?\n TypeError: f() got multiple values for keyword argument \'a\'\n >>> f(1, *(2,))\n 1 2\n\nIt is unusual for both keyword arguments and the "*expression" syntax\nto be used in the same call, so in practice this confusion does not\narise.\n\nIf the syntax "**expression" appears in the function call,\n"expression" must evaluate to a mapping, the contents of which are\ntreated as additional keyword arguments. In the case of a keyword\nappearing in both "expression" and as an explicit keyword argument, a\n"TypeError" exception is raised.\n\nFormal parameters using the syntax "*identifier" or "**identifier"\ncannot be used as positional argument slots or as keyword argument\nnames.\n\nA call always returns some value, possibly "None", unless it raises an\nexception. How this value is computed depends on the type of the\ncallable object.\n\nIf it is---\n\na user-defined function:\n The code block for the function is executed, passing it the\n argument list. The first thing the code block will do is bind the\n formal parameters to the arguments; this is described in section\n *Function definitions*. When the code block executes a "return"\n statement, this specifies the return value of the function call.\n\na built-in function or method:\n The result is up to the interpreter; see *Built-in Functions* for\n the descriptions of built-in functions and methods.\n\na class object:\n A new instance of that class is returned.\n\na class instance method:\n The corresponding user-defined function is called, with an argument\n list that is one longer than the argument list of the call: the\n instance becomes the first argument.\n\na class instance:\n The class must define a "__call__()" method; the effect is then the\n same as if that method was called.\n', 'class': u'\nClass definitions\n*****************\n\nA class definition defines a class object (see section *The standard\ntype hierarchy*):\n\n classdef ::= [decorators] "class" classname [inheritance] ":" suite\n inheritance ::= "(" [parameter_list] ")"\n classname ::= identifier\n\nA class definition is an executable statement. The inheritance list\nusually gives a list of base classes (see *Customizing class creation*\nfor more advanced uses), so each item in the list should evaluate to a\nclass object which allows subclassing. Classes without an inheritance\nlist inherit, by default, from the base class "object"; hence,\n\n class Foo:\n pass\n\nis equivalent to\n\n class Foo(object):\n pass\n\nThe class\'s suite is then executed in a new execution frame (see\n*Naming and binding*), using a newly created local namespace and the\noriginal global namespace. (Usually, the suite contains mostly\nfunction definitions.) When the class\'s suite finishes execution, its\nexecution frame is discarded but its local namespace is saved. [4] A\nclass object is then created using the inheritance list for the base\nclasses and the saved local namespace for the attribute dictionary.\nThe class name is bound to this class object in the original local\nnamespace.\n\nClass creation can be customized heavily using *metaclasses*.\n\nClasses can also be decorated: just like when decorating functions,\n\n @f1(arg)\n @f2\n class Foo: pass\n\nis equivalent to\n\n class Foo: pass\n Foo = f1(arg)(f2(Foo))\n\nThe evaluation rules for the decorator expressions are the same as for\nfunction decorators. The result must be a class object, which is then\nbound to the class name.\n\n**Programmer\'s note:** Variables defined in the class definition are\nclass attributes; they are shared by instances. Instance attributes\ncan be set in a method with "self.name = value". Both class and\ninstance attributes are accessible through the notation ""self.name"",\nand an instance attribute hides a class attribute with the same name\nwhen accessed in this way. Class attributes can be used as defaults\nfor instance attributes, but using mutable values there can lead to\nunexpected results. *Descriptors* can be used to create instance\nvariables with different implementation details.\n\nSee also: **PEP 3115** - Metaclasses in Python 3 **PEP 3129** -\n Class Decorators\n\n-[ Footnotes ]-\n\n[1] The exception is propagated to the invocation stack unless\n there is a "finally" clause which happens to raise another\n exception. That new exception causes the old one to be lost.\n\n[2] Currently, control "flows off the end" except in the case of\n an exception or the execution of a "return", "continue", or\n "break" statement.\n\n[3] A string literal appearing as the first statement in the\n function body is transformed into the function\'s "__doc__"\n attribute and therefore the function\'s *docstring*.\n\n[4] A string literal appearing as the first statement in the class\n body is transformed into the namespace\'s "__doc__" item and\n therefore the class\'s *docstring*.\n', - 'comparisons': u'\nComparisons\n***********\n\nUnlike C, all comparison operations in Python have the same priority,\nwhich is lower than that of any arithmetic, shifting or bitwise\noperation. Also unlike C, expressions like "a < b < c" have the\ninterpretation that is conventional in mathematics:\n\n comparison ::= or_expr ( comp_operator or_expr )*\n comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="\n | "is" ["not"] | ["not"] "in"\n\nComparisons yield boolean values: "True" or "False".\n\nComparisons can be chained arbitrarily, e.g., "x < y <= z" is\nequivalent to "x < y and y <= z", except that "y" is evaluated only\nonce (but in both cases "z" is not evaluated at all when "x < y" is\nfound to be false).\n\nFormally, if *a*, *b*, *c*, ..., *y*, *z* are expressions and *op1*,\n*op2*, ..., *opN* are comparison operators, then "a op1 b op2 c ... y\nopN z" is equivalent to "a op1 b and b op2 c and ... y opN z", except\nthat each expression is evaluated at most once.\n\nNote that "a op1 b op2 c" doesn\'t imply any kind of comparison between\n*a* and *c*, so that, e.g., "x < y > z" is perfectly legal (though\nperhaps not pretty).\n\nThe operators "<", ">", "==", ">=", "<=", and "!=" compare the values\nof two objects. The objects need not have the same type. If both are\nnumbers, they are converted to a common type. Otherwise, the "==" and\n"!=" operators *always* consider objects of different types to be\nunequal, while the "<", ">", ">=" and "<=" operators raise a\n"TypeError" when comparing objects of different types that do not\nimplement these operators for the given pair of types. You can\ncontrol comparison behavior of objects of non-built-in types by\ndefining rich comparison methods like "__gt__()", described in section\n*Basic customization*.\n\nComparison of objects of the same type depends on the type:\n\n* Numbers are compared arithmetically.\n\n* The values "float(\'NaN\')" and "Decimal(\'NaN\')" are special. The\n are identical to themselves, "x is x" but are not equal to\n themselves, "x != x". Additionally, comparing any value to a\n not-a-number value will return "False". For example, both "3 <\n float(\'NaN\')" and "float(\'NaN\') < 3" will return "False".\n\n* Bytes objects are compared lexicographically using the numeric\n values of their elements.\n\n* Strings are compared lexicographically using the numeric\n equivalents (the result of the built-in function "ord()") of their\n characters. [3] String and bytes object can\'t be compared!\n\n* Tuples and lists are compared lexicographically using comparison\n of corresponding elements. This means that to compare equal, each\n element must compare equal and the two sequences must be of the same\n type and have the same length.\n\n If not equal, the sequences are ordered the same as their first\n differing elements. For example, "[1,2,x] <= [1,2,y]" has the same\n value as "x <= y". If the corresponding element does not exist, the\n shorter sequence is ordered first (for example, "[1,2] < [1,2,3]").\n\n* Mappings (dictionaries) compare equal if and only if they have the\n same "(key, value)" pairs. Order comparisons "(\'<\', \'<=\', \'>=\',\n \'>\')" raise "TypeError".\n\n* Sets and frozensets define comparison operators to mean subset and\n superset tests. Those relations do not define total orderings (the\n two sets "{1,2}" and {2,3} are not equal, nor subsets of one\n another, nor supersets of one another). Accordingly, sets are not\n appropriate arguments for functions which depend on total ordering.\n For example, "min()", "max()", and "sorted()" produce undefined\n results given a list of sets as inputs.\n\n* Most other objects of built-in types compare unequal unless they\n are the same object; the choice whether one object is considered\n smaller or larger than another one is made arbitrarily but\n consistently within one execution of a program.\n\nComparison of objects of differing types depends on whether either of\nthe types provide explicit support for the comparison. Most numeric\ntypes can be compared with one another. When cross-type comparison is\nnot supported, the comparison method returns "NotImplemented".\n\nThe operators "in" and "not in" test for membership. "x in s"\nevaluates to true if *x* is a member of *s*, and false otherwise. "x\nnot in s" returns the negation of "x in s". All built-in sequences\nand set types support this as well as dictionary, for which "in" tests\nwhether the dictionary has a given key. For container types such as\nlist, tuple, set, frozenset, dict, or collections.deque, the\nexpression "x in y" is equivalent to "any(x is e or x == e for e in\ny)".\n\nFor the string and bytes types, "x in y" is true if and only if *x* is\na substring of *y*. An equivalent test is "y.find(x) != -1". Empty\nstrings are always considered to be a substring of any other string,\nso """ in "abc"" will return "True".\n\nFor user-defined classes which define the "__contains__()" method, "x\nin y" is true if and only if "y.__contains__(x)" is true.\n\nFor user-defined classes which do not define "__contains__()" but do\ndefine "__iter__()", "x in y" is true if some value "z" with "x == z"\nis produced while iterating over "y". If an exception is raised\nduring the iteration, it is as if "in" raised that exception.\n\nLastly, the old-style iteration protocol is tried: if a class defines\n"__getitem__()", "x in y" is true if and only if there is a non-\nnegative integer index *i* such that "x == y[i]", and all lower\ninteger indices do not raise "IndexError" exception. (If any other\nexception is raised, it is as if "in" raised that exception).\n\nThe operator "not in" is defined to have the inverse true value of\n"in".\n\nThe operators "is" and "is not" test for object identity: "x is y" is\ntrue if and only if *x* and *y* are the same object. "x is not y"\nyields the inverse truth value. [4]\n', + 'comparisons': u'\nComparisons\n***********\n\nUnlike C, all comparison operations in Python have the same priority,\nwhich is lower than that of any arithmetic, shifting or bitwise\noperation. Also unlike C, expressions like "a < b < c" have the\ninterpretation that is conventional in mathematics:\n\n comparison ::= or_expr ( comp_operator or_expr )*\n comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="\n | "is" ["not"] | ["not"] "in"\n\nComparisons yield boolean values: "True" or "False".\n\nComparisons can be chained arbitrarily, e.g., "x < y <= z" is\nequivalent to "x < y and y <= z", except that "y" is evaluated only\nonce (but in both cases "z" is not evaluated at all when "x < y" is\nfound to be false).\n\nFormally, if *a*, *b*, *c*, ..., *y*, *z* are expressions and *op1*,\n*op2*, ..., *opN* are comparison operators, then "a op1 b op2 c ... y\nopN z" is equivalent to "a op1 b and b op2 c and ... y opN z", except\nthat each expression is evaluated at most once.\n\nNote that "a op1 b op2 c" doesn\'t imply any kind of comparison between\n*a* and *c*, so that, e.g., "x < y > z" is perfectly legal (though\nperhaps not pretty).\n\nThe operators "<", ">", "==", ">=", "<=", and "!=" compare the values\nof two objects. The objects need not have the same type. If both are\nnumbers, they are converted to a common type. Otherwise, the "==" and\n"!=" operators *always* consider objects of different types to be\nunequal, while the "<", ">", ">=" and "<=" operators raise a\n"TypeError" when comparing objects of different types that do not\nimplement these operators for the given pair of types. You can\ncontrol comparison behavior of objects of non-built-in types by\ndefining rich comparison methods like "__gt__()", described in section\n*Basic customization*.\n\nComparison of objects of the same type depends on the type:\n\n* Numbers are compared arithmetically.\n\n* The values "float(\'NaN\')" and "Decimal(\'NaN\')" are special. They\n are identical to themselves, "x is x" but are not equal to\n themselves, "x != x". Additionally, comparing any value to a\n not-a-number value will return "False". For example, both "3 <\n float(\'NaN\')" and "float(\'NaN\') < 3" will return "False".\n\n* Bytes objects are compared lexicographically using the numeric\n values of their elements.\n\n* Strings are compared lexicographically using the numeric\n equivalents (the result of the built-in function "ord()") of their\n characters. [3] String and bytes object can\'t be compared!\n\n* Tuples and lists are compared lexicographically using comparison\n of corresponding elements. This means that to compare equal, each\n element must compare equal and the two sequences must be of the same\n type and have the same length.\n\n If not equal, the sequences are ordered the same as their first\n differing elements. For example, "[1,2,x] <= [1,2,y]" has the same\n value as "x <= y". If the corresponding element does not exist, the\n shorter sequence is ordered first (for example, "[1,2] < [1,2,3]").\n\n* Mappings (dictionaries) compare equal if and only if they have the\n same "(key, value)" pairs. Order comparisons "(\'<\', \'<=\', \'>=\',\n \'>\')" raise "TypeError".\n\n* Sets and frozensets define comparison operators to mean subset and\n superset tests. Those relations do not define total orderings (the\n two sets "{1,2}" and {2,3} are not equal, nor subsets of one\n another, nor supersets of one another). Accordingly, sets are not\n appropriate arguments for functions which depend on total ordering.\n For example, "min()", "max()", and "sorted()" produce undefined\n results given a list of sets as inputs.\n\n* Most other objects of built-in types compare unequal unless they\n are the same object; the choice whether one object is considered\n smaller or larger than another one is made arbitrarily but\n consistently within one execution of a program.\n\nComparison of objects of differing types depends on whether either of\nthe types provide explicit support for the comparison. Most numeric\ntypes can be compared with one another. When cross-type comparison is\nnot supported, the comparison method returns "NotImplemented".\n\nThe operators "in" and "not in" test for membership. "x in s"\nevaluates to true if *x* is a member of *s*, and false otherwise. "x\nnot in s" returns the negation of "x in s". All built-in sequences\nand set types support this as well as dictionary, for which "in" tests\nwhether the dictionary has a given key. For container types such as\nlist, tuple, set, frozenset, dict, or collections.deque, the\nexpression "x in y" is equivalent to "any(x is e or x == e for e in\ny)".\n\nFor the string and bytes types, "x in y" is true if and only if *x* is\na substring of *y*. An equivalent test is "y.find(x) != -1". Empty\nstrings are always considered to be a substring of any other string,\nso """ in "abc"" will return "True".\n\nFor user-defined classes which define the "__contains__()" method, "x\nin y" is true if and only if "y.__contains__(x)" is true.\n\nFor user-defined classes which do not define "__contains__()" but do\ndefine "__iter__()", "x in y" is true if some value "z" with "x == z"\nis produced while iterating over "y". If an exception is raised\nduring the iteration, it is as if "in" raised that exception.\n\nLastly, the old-style iteration protocol is tried: if a class defines\n"__getitem__()", "x in y" is true if and only if there is a non-\nnegative integer index *i* such that "x == y[i]", and all lower\ninteger indices do not raise "IndexError" exception. (If any other\nexception is raised, it is as if "in" raised that exception).\n\nThe operator "not in" is defined to have the inverse true value of\n"in".\n\nThe operators "is" and "is not" test for object identity: "x is y" is\ntrue if and only if *x* and *y* are the same object. "x is not y"\nyields the inverse truth value. [4]\n', 'compound': u'\nCompound statements\n*******************\n\nCompound statements contain (groups of) other statements; they affect\nor control the execution of those other statements in some way. In\ngeneral, compound statements span multiple lines, although in simple\nincarnations a whole compound statement may be contained in one line.\n\nThe "if", "while" and "for" statements implement traditional control\nflow constructs. "try" specifies exception handlers and/or cleanup\ncode for a group of statements, while the "with" statement allows the\nexecution of initialization and finalization code around a block of\ncode. Function and class definitions are also syntactically compound\nstatements.\n\nA compound statement consists of one or more \'clauses.\' A clause\nconsists of a header and a \'suite.\' The clause headers of a\nparticular compound statement are all at the same indentation level.\nEach clause header begins with a uniquely identifying keyword and ends\nwith a colon. A suite is a group of statements controlled by a\nclause. A suite can be one or more semicolon-separated simple\nstatements on the same line as the header, following the header\'s\ncolon, or it can be one or more indented statements on subsequent\nlines. Only the latter form of a suite can contain nested compound\nstatements; the following is illegal, mostly because it wouldn\'t be\nclear to which "if" clause a following "else" clause would belong:\n\n if test1: if test2: print(x)\n\nAlso note that the semicolon binds tighter than the colon in this\ncontext, so that in the following example, either all or none of the\n"print()" calls are executed:\n\n if x < y < z: print(x); print(y); print(z)\n\nSummarizing:\n\n compound_stmt ::= if_stmt\n | while_stmt\n | for_stmt\n | try_stmt\n | with_stmt\n | funcdef\n | classdef\n suite ::= stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT\n statement ::= stmt_list NEWLINE | compound_stmt\n stmt_list ::= simple_stmt (";" simple_stmt)* [";"]\n\nNote that statements always end in a "NEWLINE" possibly followed by a\n"DEDENT". Also note that optional continuation clauses always begin\nwith a keyword that cannot start a statement, thus there are no\nambiguities (the \'dangling "else"\' problem is solved in Python by\nrequiring nested "if" statements to be indented).\n\nThe formatting of the grammar rules in the following sections places\neach clause on a separate line for clarity.\n\n\nThe "if" statement\n==================\n\nThe "if" statement is used for conditional execution:\n\n if_stmt ::= "if" expression ":" suite\n ( "elif" expression ":" suite )*\n ["else" ":" suite]\n\nIt selects exactly one of the suites by evaluating the expressions one\nby one until one is found to be true (see section *Boolean operations*\nfor the definition of true and false); then that suite is executed\n(and no other part of the "if" statement is executed or evaluated).\nIf all expressions are false, the suite of the "else" clause, if\npresent, is executed.\n\n\nThe "while" statement\n=====================\n\nThe "while" statement is used for repeated execution as long as an\nexpression is true:\n\n while_stmt ::= "while" expression ":" suite\n ["else" ":" suite]\n\nThis repeatedly tests the expression and, if it is true, executes the\nfirst suite; if the expression is false (which may be the first time\nit is tested) the suite of the "else" clause, if present, is executed\nand the loop terminates.\n\nA "break" statement executed in the first suite terminates the loop\nwithout executing the "else" clause\'s suite. A "continue" statement\nexecuted in the first suite skips the rest of the suite and goes back\nto testing the expression.\n\n\nThe "for" statement\n===================\n\nThe "for" statement is used to iterate over the elements of a sequence\n(such as a string, tuple or list) or other iterable object:\n\n for_stmt ::= "for" target_list "in" expression_list ":" suite\n ["else" ":" suite]\n\nThe expression list is evaluated once; it should yield an iterable\nobject. An iterator is created for the result of the\n"expression_list". The suite is then executed once for each item\nprovided by the iterator, in the order returned by the iterator. Each\nitem in turn is assigned to the target list using the standard rules\nfor assignments (see *Assignment statements*), and then the suite is\nexecuted. When the items are exhausted (which is immediately when the\nsequence is empty or an iterator raises a "StopIteration" exception),\nthe suite in the "else" clause, if present, is executed, and the loop\nterminates.\n\nA "break" statement executed in the first suite terminates the loop\nwithout executing the "else" clause\'s suite. A "continue" statement\nexecuted in the first suite skips the rest of the suite and continues\nwith the next item, or with the "else" clause if there is no next\nitem.\n\nThe for-loop makes assignments to the variables(s) in the target list.\nThis overwrites all previous assignments to those variables including\nthose made in the suite of the for-loop:\n\n for i in range(10):\n print(i)\n i = 5 # this will not affect the for-loop\n # because i will be overwritten with the next\n # index in the range\n\nNames in the target list are not deleted when the loop is finished,\nbut if the sequence is empty, they will not have been assigned to at\nall by the loop. Hint: the built-in function "range()" returns an\niterator of integers suitable to emulate the effect of Pascal\'s "for i\n:= a to b do"; e.g., "list(range(3))" returns the list "[0, 1, 2]".\n\nNote: There is a subtlety when the sequence is being modified by the\n loop (this can only occur for mutable sequences, i.e. lists). An\n internal counter is used to keep track of which item is used next,\n and this is incremented on each iteration. When this counter has\n reached the length of the sequence the loop terminates. This means\n that if the suite deletes the current (or a previous) item from the\n sequence, the next item will be skipped (since it gets the index of\n the current item which has already been treated). Likewise, if the\n suite inserts an item in the sequence before the current item, the\n current item will be treated again the next time through the loop.\n This can lead to nasty bugs that can be avoided by making a\n temporary copy using a slice of the whole sequence, e.g.,\n\n for x in a[:]:\n if x < 0: a.remove(x)\n\n\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" identifier]] ":" suite)+\n ["else" ":" suite]\n ["finally" ":" suite]\n try2_stmt ::= "try" ":" suite\n "finally" ":" suite\n\nThe "except" clause(s) specify one or more exception handlers. When no\nexception occurs in the "try" clause, no exception handler is\nexecuted. When an exception occurs in the "try" suite, a search for an\nexception handler is started. This search inspects the except clauses\nin turn until one is found that matches the exception. An expression-\nless except clause, if present, must be last; it matches any\nexception. For an except clause with an expression, that expression\nis evaluated, and the clause matches the exception if the resulting\nobject is "compatible" with the exception. An object is compatible\nwith an exception if it is the class or a base class of the exception\nobject or a tuple containing an item compatible with the exception.\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 raised\nthe exception).\n\nWhen a matching except clause is found, the exception is assigned to\nthe target specified after the "as" keyword in that except clause, if\npresent, and the except clause\'s suite is executed. All except\nclauses must have an executable block. When the end of this block is\nreached, execution continues normally after the entire try statement.\n(This means that if two nested handlers exist for the same exception,\nand the exception occurs in the try clause of the inner handler, the\nouter handler will not handle the exception.)\n\nWhen an exception has been assigned using "as target", it is cleared\nat the end of the except clause. This is as if\n\n except E as N:\n foo\n\nwas translated to\n\n except E as N:\n try:\n foo\n finally:\n del N\n\nThis means the exception must be assigned to a different name to be\nable to refer to it after the except clause. Exceptions are cleared\nbecause with the traceback attached to them, they form a reference\ncycle with the stack frame, keeping all locals in that frame alive\nuntil the next garbage collection occurs.\n\nBefore an except clause\'s suite is executed, details about the\nexception are stored in the "sys" module and can be accessed via\n"sys.exc_info()". "sys.exc_info()" returns a 3-tuple consisting of the\nexception class, the exception instance and a traceback object (see\nsection *The standard type hierarchy*) identifying the point in the\nprogram where the exception occurred. "sys.exc_info()" values are\nrestored to their previous values (before the call) when returning\nfrom a function that 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 are\nnot handled by the preceding "except" clauses.\n\nIf "finally" is present, it specifies a \'cleanup\' handler. The "try"\nclause is executed, including any "except" and "else" clauses. If an\nexception occurs in any of the clauses and is not handled, the\nexception is temporarily saved. The "finally" clause is executed. If\nthere is a saved exception it is re-raised at the end of the "finally"\nclause. If the "finally" clause raises another exception, the saved\nexception is set as the context of the new exception. If the "finally"\nclause executes a "return" or "break" statement, the saved exception\nis 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 the\n"try" suite of a "try"..."finally" statement, the "finally" clause is\nalso executed \'on the way out.\' A "continue" statement is illegal in\nthe "finally" clause. (The reason is a problem with the current\nimplementation --- this restriction may be lifted 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 the\nlast 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\n\nThe "with" statement\n====================\n\nThe "with" statement is used to wrap the execution of a block with\nmethods defined by a context manager (see section *With Statement\nContext Managers*). This allows common "try"..."except"..."finally"\nusage patterns to be encapsulated for convenient reuse.\n\n with_stmt ::= "with" with_item ("," with_item)* ":" suite\n with_item ::= expression ["as" target]\n\nThe execution of the "with" statement with one "item" proceeds as\nfollows:\n\n1. The context expression (the expression given in the "with_item")\n is evaluated to obtain a context manager.\n\n2. The context manager\'s "__exit__()" is loaded for later use.\n\n3. The context manager\'s "__enter__()" method is invoked.\n\n4. If a target was included in the "with" statement, the return\n value from "__enter__()" is assigned to it.\n\n Note: The "with" statement guarantees that if the "__enter__()"\n method returns without an error, then "__exit__()" will always be\n called. Thus, if an error occurs during the assignment to the\n target list, it will be treated the same as an error occurring\n within the suite would be. See step 6 below.\n\n5. The suite is executed.\n\n6. The context manager\'s "__exit__()" method is invoked. If an\n exception caused the suite to be exited, its type, value, and\n traceback are passed as arguments to "__exit__()". Otherwise, three\n "None" arguments are supplied.\n\n If the suite was exited due to an exception, and the return value\n from the "__exit__()" method was false, the exception is reraised.\n If the return value was true, the exception is suppressed, and\n execution continues with the statement following the "with"\n statement.\n\n If the suite was exited for any reason other than an exception, the\n return value from "__exit__()" is ignored, and execution proceeds\n at the normal location for the kind of exit that was taken.\n\nWith more than one item, the context managers are processed as if\nmultiple "with" statements were nested:\n\n with A() as a, B() as b:\n suite\n\nis equivalent to\n\n with A() as a:\n with B() as b:\n suite\n\nChanged in version 3.1: Support for multiple context expressions.\n\nSee also: **PEP 0343** - The "with" statement\n\n The specification, background, and examples for the Python "with"\n statement.\n\n\nFunction definitions\n====================\n\nA function definition defines a user-defined function object (see\nsection *The standard type hierarchy*):\n\n funcdef ::= [decorators] "def" funcname "(" [parameter_list] ")" ["->" expression] ":" suite\n decorators ::= decorator+\n decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE\n dotted_name ::= identifier ("." identifier)*\n parameter_list ::= (defparameter ",")*\n | "*" [parameter] ("," defparameter)* ["," "**" parameter]\n | "**" parameter\n | defparameter [","] )\n parameter ::= identifier [":" expression]\n defparameter ::= parameter ["=" expression]\n funcname ::= identifier\n\nA function definition is an executable statement. Its execution binds\nthe function name in the current local namespace to a function object\n(a wrapper around the executable code for the function). This\nfunction object contains a reference to the current global namespace\nas the global namespace to be used when the function is called.\n\nThe function definition does not execute the function body; this gets\nexecuted only when the function is called. [3]\n\nA function definition may be wrapped by one or more *decorator*\nexpressions. Decorator expressions are evaluated when the function is\ndefined, in the scope that contains the function definition. The\nresult must be a callable, which is invoked with the function object\nas the only argument. The returned value is bound to the function name\ninstead of the function object. Multiple decorators are applied in\nnested fashion. For example, the following code\n\n @f1(arg)\n @f2\n def func(): pass\n\nis equivalent to\n\n def func(): pass\n func = f1(arg)(f2(func))\n\nWhen one or more *parameters* have the form *parameter* "="\n*expression*, the function is said to have "default parameter values."\nFor a parameter with a default value, the corresponding *argument* may\nbe omitted from a call, in which case the parameter\'s default value is\nsubstituted. If a parameter has a default value, all following\nparameters up until the ""*"" must also have a default value --- this\nis a syntactic restriction that is not expressed by the grammar.\n\n**Default parameter values are evaluated from left to right when the\nfunction definition is executed.** This means that the expression is\nevaluated once, when the function is defined, and that the same "pre-\ncomputed" value is used for each call. This is especially important\nto understand when a default parameter is a mutable object, such as a\nlist or a dictionary: if the function modifies the object (e.g. by\nappending an item to a list), the default value is in effect modified.\nThis is generally not what was intended. A way around this is to use\n"None" as the default, and explicitly test for it in the body of the\nfunction, e.g.:\n\n def whats_on_the_telly(penguin=None):\n if penguin is None:\n penguin = []\n penguin.append("property of the zoo")\n return penguin\n\nFunction call semantics are described in more detail in section\n*Calls*. A function call always assigns values to all parameters\nmentioned in the parameter list, either from position arguments, from\nkeyword arguments, or from default values. If the form\n""*identifier"" is present, it is initialized to a tuple receiving any\nexcess positional parameters, defaulting to the empty tuple. If the\nform ""**identifier"" is present, it is initialized to a new\ndictionary receiving any excess keyword arguments, defaulting to a new\nempty dictionary. Parameters after ""*"" or ""*identifier"" are\nkeyword-only parameters and may only be passed used keyword arguments.\n\nParameters may have annotations of the form "": expression"" following\nthe parameter name. Any parameter may have an annotation even those\nof the form "*identifier" or "**identifier". Functions may have\n"return" annotation of the form ""-> expression"" after the parameter\nlist. These annotations can be any valid Python expression and are\nevaluated when the function definition is executed. Annotations may\nbe evaluated in a different order than they appear in the source code.\nThe presence of annotations does not change the semantics of a\nfunction. The annotation values are available as values of a\ndictionary keyed by the parameters\' names in the "__annotations__"\nattribute of the function object.\n\nIt is also possible to create anonymous functions (functions not bound\nto a name), for immediate use in expressions. This uses lambda\nexpressions, described in section *Lambdas*. Note that the lambda\nexpression is merely a shorthand for a simplified function definition;\na function defined in a ""def"" statement can be passed around or\nassigned to another name just like a function defined by a lambda\nexpression. The ""def"" form is actually more powerful since it\nallows the execution of multiple statements and annotations.\n\n**Programmer\'s note:** Functions are first-class objects. A ""def""\nstatement executed inside a function definition defines a local\nfunction that can be returned or passed around. Free variables used\nin the nested function can access the local variables of the function\ncontaining the def. See section *Naming and binding* for details.\n\nSee also: **PEP 3107** - Function Annotations\n\n The original specification for function annotations.\n\n\nClass definitions\n=================\n\nA class definition defines a class object (see section *The standard\ntype hierarchy*):\n\n classdef ::= [decorators] "class" classname [inheritance] ":" suite\n inheritance ::= "(" [parameter_list] ")"\n classname ::= identifier\n\nA class definition is an executable statement. The inheritance list\nusually gives a list of base classes (see *Customizing class creation*\nfor more advanced uses), so each item in the list should evaluate to a\nclass object which allows subclassing. Classes without an inheritance\nlist inherit, by default, from the base class "object"; hence,\n\n class Foo:\n pass\n\nis equivalent to\n\n class Foo(object):\n pass\n\nThe class\'s suite is then executed in a new execution frame (see\n*Naming and binding*), using a newly created local namespace and the\noriginal global namespace. (Usually, the suite contains mostly\nfunction definitions.) When the class\'s suite finishes execution, its\nexecution frame is discarded but its local namespace is saved. [4] A\nclass object is then created using the inheritance list for the base\nclasses and the saved local namespace for the attribute dictionary.\nThe class name is bound to this class object in the original local\nnamespace.\n\nClass creation can be customized heavily using *metaclasses*.\n\nClasses can also be decorated: just like when decorating functions,\n\n @f1(arg)\n @f2\n class Foo: pass\n\nis equivalent to\n\n class Foo: pass\n Foo = f1(arg)(f2(Foo))\n\nThe evaluation rules for the decorator expressions are the same as for\nfunction decorators. The result must be a class object, which is then\nbound to the class name.\n\n**Programmer\'s note:** Variables defined in the class definition are\nclass attributes; they are shared by instances. Instance attributes\ncan be set in a method with "self.name = value". Both class and\ninstance attributes are accessible through the notation ""self.name"",\nand an instance attribute hides a class attribute with the same name\nwhen accessed in this way. Class attributes can be used as defaults\nfor instance attributes, but using mutable values there can lead to\nunexpected results. *Descriptors* can be used to create instance\nvariables with different implementation details.\n\nSee also: **PEP 3115** - Metaclasses in Python 3 **PEP 3129** -\n Class Decorators\n\n-[ Footnotes ]-\n\n[1] The exception is propagated to the invocation stack unless\n there is a "finally" clause which happens to raise another\n exception. That new exception causes the old one to be lost.\n\n[2] Currently, control "flows off the end" except in the case of\n an exception or the execution of a "return", "continue", or\n "break" statement.\n\n[3] A string literal appearing as the first statement in the\n function body is transformed into the function\'s "__doc__"\n attribute and therefore the function\'s *docstring*.\n\n[4] A string literal appearing as the first statement in the class\n body is transformed into the namespace\'s "__doc__" item and\n therefore the class\'s *docstring*.\n', 'context-managers': u'\nWith Statement Context Managers\n*******************************\n\nA *context manager* is an object that defines the runtime context to\nbe established when executing a "with" statement. The context manager\nhandles the entry into, and the exit from, the desired runtime context\nfor the execution of the block of code. Context managers are normally\ninvoked using the "with" statement (described in section *The with\nstatement*), but can also be used by directly invoking their methods.\n\nTypical uses of context managers include saving and restoring various\nkinds of global state, locking and unlocking resources, closing opened\nfiles, etc.\n\nFor more information on context managers, see *Context Manager Types*.\n\nobject.__enter__(self)\n\n Enter the runtime context related to this object. The "with"\n statement will bind this method\'s return value to the target(s)\n specified in the "as" clause of the statement, if any.\n\nobject.__exit__(self, exc_type, exc_value, traceback)\n\n Exit the runtime context related to this object. The parameters\n describe the exception that caused the context to be exited. If the\n context was exited without an exception, all three arguments will\n be "None".\n\n If an exception is supplied, and the method wishes to suppress the\n exception (i.e., prevent it from being propagated), it should\n return a true value. Otherwise, the exception will be processed\n normally upon exit from this method.\n\n Note that "__exit__()" methods should not reraise the passed-in\n exception; this is the caller\'s responsibility.\n\nSee also: **PEP 0343** - The "with" statement\n\n The specification, background, and examples for the Python "with"\n statement.\n', 'continue': u'\nThe "continue" statement\n************************\n\n continue_stmt ::= "continue"\n\n"continue" may only occur syntactically nested in a "for" or "while"\nloop, but not nested in a function or class definition or "finally"\nclause within that loop. It continues with the next cycle of the\nnearest enclosing loop.\n\nWhen "continue" passes control out of a "try" statement with a\n"finally" clause, that "finally" clause is executed before really\nstarting the next loop cycle.\n', @@ -42,7 +42,7 @@ 'if': u'\nThe "if" statement\n******************\n\nThe "if" statement is used for conditional execution:\n\n if_stmt ::= "if" expression ":" suite\n ( "elif" expression ":" suite )*\n ["else" ":" suite]\n\nIt selects exactly one of the suites by evaluating the expressions one\nby one until one is found to be true (see section *Boolean operations*\nfor the definition of true and false); then that suite is executed\n(and no other part of the "if" statement is executed or evaluated).\nIf all expressions are false, the suite of the "else" clause, if\npresent, is executed.\n', 'imaginary': u'\nImaginary literals\n******************\n\nImaginary literals are described by the following lexical definitions:\n\n imagnumber ::= (floatnumber | intpart) ("j" | "J")\n\nAn imaginary literal yields a complex number with a real part of 0.0.\nComplex numbers are represented as a pair of floating point numbers\nand have the same restrictions on their range. To create a complex\nnumber with a nonzero real part, add a floating point number to it,\ne.g., "(3+4j)". Some examples of imaginary literals:\n\n 3.14j 10.j 10j .001j 1e100j 3.14e-10j\n', 'import': u'\nThe "import" statement\n**********************\n\n import_stmt ::= "import" module ["as" name] ( "," module ["as" name] )*\n | "from" relative_module "import" identifier ["as" name]\n ( "," identifier ["as" name] )*\n | "from" relative_module "import" "(" identifier ["as" name]\n ( "," identifier ["as" name] )* [","] ")"\n | "from" module "import" "*"\n module ::= (identifier ".")* identifier\n relative_module ::= "."* module | "."+\n name ::= identifier\n\nThe basic import statement (no "from" clause) is executed in two\nsteps:\n\n1. find a module, loading and initializing it if necessary\n\n2. define a name or names in the local namespace for the scope\n where the "import" statement occurs.\n\nWhen the statement contains multiple clauses (separated by commas) the\ntwo steps are carried out separately for each clause, just as though\nthe clauses had been separated out into individiual import statements.\n\nThe details of the first step, finding and loading modules are\ndescribed in greater detail in the section on the *import system*,\nwhich also describes the various types of packages and modules that\ncan be imported, as well as all the hooks that can be used to\ncustomize the import system. Note that failures in this step may\nindicate either that the module could not be located, *or* that an\nerror occurred while initializing the module, which includes execution\nof the module\'s code.\n\nIf the requested module is retrieved successfully, it will be made\navailable in the local namespace in one of three ways:\n\n* If the module name is followed by "as", then the name following\n "as" is bound directly to the imported module.\n\n* If no other name is specified, and the module being imported is a\n top level module, the module\'s name is bound in the local namespace\n as a reference to the imported module\n\n* If the module being imported is *not* a top level module, then the\n name of the top level package that contains the module is bound in\n the local namespace as a reference to the top level package. The\n imported module must be accessed using its full qualified name\n rather than directly\n\nThe "from" form uses a slightly more complex process:\n\n1. find the module specified in the "from" clause, loading and\n initializing it if necessary;\n\n2. for each of the identifiers specified in the "import" clauses:\n\n 1. check if the imported module has an attribute by that name\n\n 2. if not, attempt to import a submodule with that name and then\n check the imported module again for that attribute\n\n 3. if the attribute is not found, "ImportError" is raised.\n\n 4. otherwise, a reference to that value is stored in the local\n namespace, using the name in the "as" clause if it is present,\n otherwise using the attribute name\n\nExamples:\n\n import foo # foo imported and bound locally\n import foo.bar.baz # foo.bar.baz imported, foo bound locally\n import foo.bar.baz as fbb # foo.bar.baz imported and bound as fbb\n from foo.bar import baz # foo.bar.baz imported and bound as baz\n from foo import attr # foo imported and foo.attr bound as attr\n\nIf the list of identifiers is replaced by a star ("\'*\'"), all public\nnames defined in the module are bound in the local namespace for the\nscope where the "import" statement occurs.\n\nThe *public names* defined by a module are determined by checking the\nmodule\'s namespace for a variable named "__all__"; if defined, it must\nbe a sequence of strings which are names defined or imported by that\nmodule. The names given in "__all__" are all considered public and\nare required to exist. If "__all__" is not defined, the set of public\nnames includes all names found in the module\'s namespace which do not\nbegin with an underscore character ("\'_\'"). "__all__" should contain\nthe entire public API. It is intended to avoid accidentally exporting\nitems that are not part of the API (such as library modules which were\nimported and used within the module).\n\nThe wild card form of import --- "from module import *" --- is only\nallowed at the module level. Attempting to use it in class or\nfunction definitions will raise a "SyntaxError".\n\nWhen specifying what module to import you do not have to specify the\nabsolute name of the module. When a module or package is contained\nwithin another package it is possible to make a relative import within\nthe same top package without having to mention the package name. By\nusing leading dots in the specified module or package after "from" you\ncan specify how high to traverse up the current package hierarchy\nwithout specifying exact names. One leading dot means the current\npackage where the module making the import exists. Two dots means up\none package level. Three dots is up two levels, etc. So if you execute\n"from . import mod" from a module in the "pkg" package then you will\nend up importing "pkg.mod". If you execute "from ..subpkg2 import mod"\nfrom within "pkg.subpkg1" you will import "pkg.subpkg2.mod". The\nspecification for relative imports is contained within **PEP 328**.\n\n"importlib.import_module()" is provided to support applications that\ndetermine dynamically the modules to be loaded.\n\n\nFuture statements\n=================\n\nA *future statement* is a directive to the compiler that a particular\nmodule should be compiled using syntax or semantics that will be\navailable in a specified future release of Python where the feature\nbecomes standard.\n\nThe future statement is intended to ease migration to future versions\nof Python that introduce incompatible changes to the language. It\nallows use of the new features on a per-module basis before the\nrelease in which the feature becomes standard.\n\n future_statement ::= "from" "__future__" "import" feature ["as" name]\n ("," feature ["as" name])*\n | "from" "__future__" "import" "(" feature ["as" name]\n ("," feature ["as" name])* [","] ")"\n feature ::= identifier\n name ::= identifier\n\nA future statement must appear near the top of the module. The only\nlines that can appear before a future statement are:\n\n* the module docstring (if any),\n\n* comments,\n\n* blank lines, and\n\n* other future statements.\n\nThe features recognized by Python 3.0 are "absolute_import",\n"division", "generators", "unicode_literals", "print_function",\n"nested_scopes" and "with_statement". They are all redundant because\nthey are always enabled, and only kept for backwards compatibility.\n\nA future statement is recognized and treated specially at compile\ntime: Changes to the semantics of core constructs are often\nimplemented by generating different code. It may even be the case\nthat a new feature introduces new incompatible syntax (such as a new\nreserved word), in which case the compiler may need to parse the\nmodule differently. Such decisions cannot be pushed off until\nruntime.\n\nFor any given release, the compiler knows which feature names have\nbeen defined, and raises a compile-time error if a future statement\ncontains a feature not known to it.\n\nThe direct runtime semantics are the same as for any import statement:\nthere is a standard module "__future__", described later, and it will\nbe imported in the usual way at the time the future statement is\nexecuted.\n\nThe interesting runtime semantics depend on the specific feature\nenabled by the future statement.\n\nNote that there is nothing special about the statement:\n\n import __future__ [as name]\n\nThat is not a future statement; it\'s an ordinary import statement with\nno special semantics or syntax restrictions.\n\nCode compiled by calls to the built-in functions "exec()" and\n"compile()" that occur in a module "M" containing a future statement\nwill, by default, use the new syntax or semantics associated with the\nfuture statement. This can be controlled by optional arguments to\n"compile()" --- see the documentation of that function for details.\n\nA future statement typed at an interactive interpreter prompt will\ntake effect for the rest of the interpreter session. If an\ninterpreter is started with the *-i* option, is passed a script name\nto execute, and the script includes a future statement, it will be in\neffect in the interactive session started after the script is\nexecuted.\n\nSee also: **PEP 236** - Back to the __future__\n\n The original proposal for the __future__ mechanism.\n', - 'in': u'\nComparisons\n***********\n\nUnlike C, all comparison operations in Python have the same priority,\nwhich is lower than that of any arithmetic, shifting or bitwise\noperation. Also unlike C, expressions like "a < b < c" have the\ninterpretation that is conventional in mathematics:\n\n comparison ::= or_expr ( comp_operator or_expr )*\n comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="\n | "is" ["not"] | ["not"] "in"\n\nComparisons yield boolean values: "True" or "False".\n\nComparisons can be chained arbitrarily, e.g., "x < y <= z" is\nequivalent to "x < y and y <= z", except that "y" is evaluated only\nonce (but in both cases "z" is not evaluated at all when "x < y" is\nfound to be false).\n\nFormally, if *a*, *b*, *c*, ..., *y*, *z* are expressions and *op1*,\n*op2*, ..., *opN* are comparison operators, then "a op1 b op2 c ... y\nopN z" is equivalent to "a op1 b and b op2 c and ... y opN z", except\nthat each expression is evaluated at most once.\n\nNote that "a op1 b op2 c" doesn\'t imply any kind of comparison between\n*a* and *c*, so that, e.g., "x < y > z" is perfectly legal (though\nperhaps not pretty).\n\nThe operators "<", ">", "==", ">=", "<=", and "!=" compare the values\nof two objects. The objects need not have the same type. If both are\nnumbers, they are converted to a common type. Otherwise, the "==" and\n"!=" operators *always* consider objects of different types to be\nunequal, while the "<", ">", ">=" and "<=" operators raise a\n"TypeError" when comparing objects of different types that do not\nimplement these operators for the given pair of types. You can\ncontrol comparison behavior of objects of non-built-in types by\ndefining rich comparison methods like "__gt__()", described in section\n*Basic customization*.\n\nComparison of objects of the same type depends on the type:\n\n* Numbers are compared arithmetically.\n\n* The values "float(\'NaN\')" and "Decimal(\'NaN\')" are special. The\n are identical to themselves, "x is x" but are not equal to\n themselves, "x != x". Additionally, comparing any value to a\n not-a-number value will return "False". For example, both "3 <\n float(\'NaN\')" and "float(\'NaN\') < 3" will return "False".\n\n* Bytes objects are compared lexicographically using the numeric\n values of their elements.\n\n* Strings are compared lexicographically using the numeric\n equivalents (the result of the built-in function "ord()") of their\n characters. [3] String and bytes object can\'t be compared!\n\n* Tuples and lists are compared lexicographically using comparison\n of corresponding elements. This means that to compare equal, each\n element must compare equal and the two sequences must be of the same\n type and have the same length.\n\n If not equal, the sequences are ordered the same as their first\n differing elements. For example, "[1,2,x] <= [1,2,y]" has the same\n value as "x <= y". If the corresponding element does not exist, the\n shorter sequence is ordered first (for example, "[1,2] < [1,2,3]").\n\n* Mappings (dictionaries) compare equal if and only if they have the\n same "(key, value)" pairs. Order comparisons "(\'<\', \'<=\', \'>=\',\n \'>\')" raise "TypeError".\n\n* Sets and frozensets define comparison operators to mean subset and\n superset tests. Those relations do not define total orderings (the\n two sets "{1,2}" and {2,3} are not equal, nor subsets of one\n another, nor supersets of one another). Accordingly, sets are not\n appropriate arguments for functions which depend on total ordering.\n For example, "min()", "max()", and "sorted()" produce undefined\n results given a list of sets as inputs.\n\n* Most other objects of built-in types compare unequal unless they\n are the same object; the choice whether one object is considered\n smaller or larger than another one is made arbitrarily but\n consistently within one execution of a program.\n\nComparison of objects of differing types depends on whether either of\nthe types provide explicit support for the comparison. Most numeric\ntypes can be compared with one another. When cross-type comparison is\nnot supported, the comparison method returns "NotImplemented".\n\nThe operators "in" and "not in" test for membership. "x in s"\nevaluates to true if *x* is a member of *s*, and false otherwise. "x\nnot in s" returns the negation of "x in s". All built-in sequences\nand set types support this as well as dictionary, for which "in" tests\nwhether the dictionary has a given key. For container types such as\nlist, tuple, set, frozenset, dict, or collections.deque, the\nexpression "x in y" is equivalent to "any(x is e or x == e for e in\ny)".\n\nFor the string and bytes types, "x in y" is true if and only if *x* is\na substring of *y*. An equivalent test is "y.find(x) != -1". Empty\nstrings are always considered to be a substring of any other string,\nso """ in "abc"" will return "True".\n\nFor user-defined classes which define the "__contains__()" method, "x\nin y" is true if and only if "y.__contains__(x)" is true.\n\nFor user-defined classes which do not define "__contains__()" but do\ndefine "__iter__()", "x in y" is true if some value "z" with "x == z"\nis produced while iterating over "y". If an exception is raised\nduring the iteration, it is as if "in" raised that exception.\n\nLastly, the old-style iteration protocol is tried: if a class defines\n"__getitem__()", "x in y" is true if and only if there is a non-\nnegative integer index *i* such that "x == y[i]", and all lower\ninteger indices do not raise "IndexError" exception. (If any other\nexception is raised, it is as if "in" raised that exception).\n\nThe operator "not in" is defined to have the inverse true value of\n"in".\n\nThe operators "is" and "is not" test for object identity: "x is y" is\ntrue if and only if *x* and *y* are the same object. "x is not y"\nyields the inverse truth value. [4]\n', + 'in': u'\nComparisons\n***********\n\nUnlike C, all comparison operations in Python have the same priority,\nwhich is lower than that of any arithmetic, shifting or bitwise\noperation. Also unlike C, expressions like "a < b < c" have the\ninterpretation that is conventional in mathematics:\n\n comparison ::= or_expr ( comp_operator or_expr )*\n comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="\n | "is" ["not"] | ["not"] "in"\n\nComparisons yield boolean values: "True" or "False".\n\nComparisons can be chained arbitrarily, e.g., "x < y <= z" is\nequivalent to "x < y and y <= z", except that "y" is evaluated only\nonce (but in both cases "z" is not evaluated at all when "x < y" is\nfound to be false).\n\nFormally, if *a*, *b*, *c*, ..., *y*, *z* are expressions and *op1*,\n*op2*, ..., *opN* are comparison operators, then "a op1 b op2 c ... y\nopN z" is equivalent to "a op1 b and b op2 c and ... y opN z", except\nthat each expression is evaluated at most once.\n\nNote that "a op1 b op2 c" doesn\'t imply any kind of comparison between\n*a* and *c*, so that, e.g., "x < y > z" is perfectly legal (though\nperhaps not pretty).\n\nThe operators "<", ">", "==", ">=", "<=", and "!=" compare the values\nof two objects. The objects need not have the same type. If both are\nnumbers, they are converted to a common type. Otherwise, the "==" and\n"!=" operators *always* consider objects of different types to be\nunequal, while the "<", ">", ">=" and "<=" operators raise a\n"TypeError" when comparing objects of different types that do not\nimplement these operators for the given pair of types. You can\ncontrol comparison behavior of objects of non-built-in types by\ndefining rich comparison methods like "__gt__()", described in section\n*Basic customization*.\n\nComparison of objects of the same type depends on the type:\n\n* Numbers are compared arithmetically.\n\n* The values "float(\'NaN\')" and "Decimal(\'NaN\')" are special. They\n are identical to themselves, "x is x" but are not equal to\n themselves, "x != x". Additionally, comparing any value to a\n not-a-number value will return "False". For example, both "3 <\n float(\'NaN\')" and "float(\'NaN\') < 3" will return "False".\n\n* Bytes objects are compared lexicographically using the numeric\n values of their elements.\n\n* Strings are compared lexicographically using the numeric\n equivalents (the result of the built-in function "ord()") of their\n characters. [3] String and bytes object can\'t be compared!\n\n* Tuples and lists are compared lexicographically using comparison\n of corresponding elements. This means that to compare equal, each\n element must compare equal and the two sequences must be of the same\n type and have the same length.\n\n If not equal, the sequences are ordered the same as their first\n differing elements. For example, "[1,2,x] <= [1,2,y]" has the same\n value as "x <= y". If the corresponding element does not exist, the\n shorter sequence is ordered first (for example, "[1,2] < [1,2,3]").\n\n* Mappings (dictionaries) compare equal if and only if they have the\n same "(key, value)" pairs. Order comparisons "(\'<\', \'<=\', \'>=\',\n \'>\')" raise "TypeError".\n\n* Sets and frozensets define comparison operators to mean subset and\n superset tests. Those relations do not define total orderings (the\n two sets "{1,2}" and {2,3} are not equal, nor subsets of one\n another, nor supersets of one another). Accordingly, sets are not\n appropriate arguments for functions which depend on total ordering.\n For example, "min()", "max()", and "sorted()" produce undefined\n results given a list of sets as inputs.\n\n* Most other objects of built-in types compare unequal unless they\n are the same object; the choice whether one object is considered\n smaller or larger than another one is made arbitrarily but\n consistently within one execution of a program.\n\nComparison of objects of differing types depends on whether either of\nthe types provide explicit support for the comparison. Most numeric\ntypes can be compared with one another. When cross-type comparison is\nnot supported, the comparison method returns "NotImplemented".\n\nThe operators "in" and "not in" test for membership. "x in s"\nevaluates to true if *x* is a member of *s*, and false otherwise. "x\nnot in s" returns the negation of "x in s". All built-in sequences\nand set types support this as well as dictionary, for which "in" tests\nwhether the dictionary has a given key. For container types such as\nlist, tuple, set, frozenset, dict, or collections.deque, the\nexpression "x in y" is equivalent to "any(x is e or x == e for e in\ny)".\n\nFor the string and bytes types, "x in y" is true if and only if *x* is\na substring of *y*. An equivalent test is "y.find(x) != -1". Empty\nstrings are always considered to be a substring of any other string,\nso """ in "abc"" will return "True".\n\nFor user-defined classes which define the "__contains__()" method, "x\nin y" is true if and only if "y.__contains__(x)" is true.\n\nFor user-defined classes which do not define "__contains__()" but do\ndefine "__iter__()", "x in y" is true if some value "z" with "x == z"\nis produced while iterating over "y". If an exception is raised\nduring the iteration, it is as if "in" raised that exception.\n\nLastly, the old-style iteration protocol is tried: if a class defines\n"__getitem__()", "x in y" is true if and only if there is a non-\nnegative integer index *i* such that "x == y[i]", and all lower\ninteger indices do not raise "IndexError" exception. (If any other\nexception is raised, it is as if "in" raised that exception).\n\nThe operator "not in" is defined to have the inverse true value of\n"in".\n\nThe operators "is" and "is not" test for object identity: "x is y" is\ntrue if and only if *x* and *y* are the same object. "x is not y"\nyields the inverse truth value. [4]\n', 'integers': u'\nInteger literals\n****************\n\nInteger literals are described by the following lexical definitions:\n\n integer ::= decimalinteger | octinteger | hexinteger | bininteger\n decimalinteger ::= nonzerodigit digit* | "0"+\n nonzerodigit ::= "1"..."9"\n digit ::= "0"..."9"\n octinteger ::= "0" ("o" | "O") octdigit+\n hexinteger ::= "0" ("x" | "X") hexdigit+\n bininteger ::= "0" ("b" | "B") bindigit+\n octdigit ::= "0"..."7"\n hexdigit ::= digit | "a"..."f" | "A"..."F"\n bindigit ::= "0" | "1"\n\nThere is no limit for the length of integer literals apart from what\ncan be stored in available memory.\n\nNote that leading zeros in a non-zero decimal number are not allowed.\nThis is for disambiguation with C-style octal literals, which Python\nused before version 3.0.\n\nSome examples of integer literals:\n\n 7 2147483647 0o177 0b100110111\n 3 79228162514264337593543950336 0o377 0x100000000\n 79228162514264337593543950336 0xdeadbeef\n', 'lambda': u'\nLambdas\n*******\n\n lambda_expr ::= "lambda" [parameter_list]: expression\n lambda_expr_nocond ::= "lambda" [parameter_list]: expression_nocond\n\nLambda expressions (sometimes called lambda forms) are used to create\nanonymous functions. The expression "lambda arguments: expression"\nyields a function object. The unnamed object behaves like a function\nobject defined with\n\n def (arguments):\n return expression\n\nSee section *Function definitions* for the syntax of parameter lists.\nNote that functions created with lambda expressions cannot contain\nstatements or annotations.\n', 'lists': u'\nList displays\n*************\n\nA list display is a possibly empty series of expressions enclosed in\nsquare brackets:\n\n list_display ::= "[" [expression_list | comprehension] "]"\n\nA list display yields a new list object, the contents being specified\nby either a list of expressions or a comprehension. When a comma-\nseparated list of expressions is supplied, its elements are evaluated\nfrom left to right and placed into the list object in that order.\nWhen a comprehension is supplied, the list is constructed from the\nelements resulting from the comprehension.\n', diff --git a/README b/README --- a/README +++ b/README @@ -1,4 +1,4 @@ -This is Python version 3.5.0 alpha 1 +This is Python version 3.5.0 alpha 2 ==================================== Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 10:40:37 2015 From: python-checkins at python.org (larry.hastings) Date: Mon, 09 Mar 2015 09:40:37 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Post-release_changes_for_3?= =?utf-8?b?LjUuMGEyLg==?= Message-ID: <20150309094037.1198.16456@psf.io> https://hg.python.org/cpython/rev/a73b782ae266 changeset: 94916:a73b782ae266 user: Larry Hastings date: Mon Mar 09 02:39:47 2015 -0700 summary: Post-release changes for 3.5.0a2. files: Include/patchlevel.h | 2 +- Misc/NEWS | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 2 /* Version as a string */ -#define PY_VERSION "3.5.0a2" +#define PY_VERSION "3.5.0a2+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -2,10 +2,22 @@ Python News +++++++++++ +What's New in Python 3.5.0 alpha 3? +=================================== + +Release date: 2015-03-28 + +Core and Builtins +----------------- + +Library +------- + + What's New in Python 3.5 alpha 2? ================================= -Release date: 2015-03-08 +Release date: 2015-03-09 Core and Builtins ----------------- -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 10:40:37 2015 From: python-checkins at python.org (larry.hastings) Date: Mon, 09 Mar 2015 09:40:37 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Added_tag_v3=2E5=2E0a2_for?= =?utf-8?q?_changeset_0337bd7ebcb6?= Message-ID: <20150309094037.117225.45111@psf.io> https://hg.python.org/cpython/rev/d0cdc70e1f7a changeset: 94914:d0cdc70e1f7a user: Larry Hastings date: Sun Mar 08 00:24:48 2015 -0800 summary: Added tag v3.5.0a2 for changeset 0337bd7ebcb6 files: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -145,3 +145,4 @@ 69dd528ca6255a66c37cc5cf680e8357d108b036 v3.4.3rc1 b4cbecbc0781e89a309d03b60a1f75f8499250e6 v3.4.3 5d4b6a57d5fd7564bf73f3db0e46fe5eeb00bcd8 v3.5.0a1 +0337bd7ebcb6559d69679bc7025059ad1ce4f432 v3.5.0a2 -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 10:40:38 2015 From: python-checkins at python.org (larry.hastings) Date: Mon, 09 Mar 2015 09:40:38 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_default_-=3E_default?= =?utf-8?b?KTogTWVyZ2Uu?= Message-ID: <20150309094038.117235.46266@psf.io> https://hg.python.org/cpython/rev/41839a8a526b changeset: 94917:41839a8a526b parent: 94916:a73b782ae266 parent: 94911:82df13f0ea9b user: Larry Hastings date: Mon Mar 09 02:40:13 2015 -0700 summary: Merge. files: Doc/library/index.rst | 2 +- Doc/library/ssl.rst | 2 +- Lib/test/test_sysconfig.py | 6 ++++++ Misc/NEWS | 4 ++++ Tools/msi/bundle/Default.thm | 2 +- Tools/msi/bundle/bundle.wxs | 4 ++-- Tools/msi/msi.props | 4 ++-- Tools/msi/uploadrelease.bat | 3 ++- Tools/msi/uploadrelease.proj | 4 ++-- configure | 10 +++++++++- configure.ac | 10 +++++++++- 11 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Doc/library/index.rst b/Doc/library/index.rst --- a/Doc/library/index.rst +++ b/Doc/library/index.rst @@ -20,7 +20,7 @@ encourage and enhance the portability of Python programs by abstracting away platform-specifics into platform-neutral APIs. -The Python installers for the Windows platform usually includes +The Python installers for the Windows platform usually include the entire standard library and often also include many additional components. For Unix-like operating systems Python is normally provided as a collection of packages, so it may be necessary to use the packaging diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -556,7 +556,7 @@ prefer trusted certificates when building the trust chain to validate a certificate. This flag is enabled by default. - .. versionadded:: 3.4.5 + .. versionadded:: 3.4.4 .. data:: PROTOCOL_SSLv23 diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -389,6 +389,12 @@ self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) + @unittest.skipUnless(sys.platform == 'linux', 'Linux-specific test') + def test_bitness_in_ext_suffix(self): + suffix = sysconfig.get_config_var('EXT_SUFFIX') + bitness = '-32b' if sys.maxsize < 2**32 else '-64b' + self.assertTrue(suffix.endswith(bitness + '.so'), suffix) + class MakefileTests(unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,10 @@ Core and Builtins ----------------- +- Issue #22980: Under Linux, C extensions now include bitness in the file + name, to make it easy to test 32-bit and 64-bit builds in the same + working tree. + - Issue #23571: PyObject_Call() and PyCFunction_Call() now raise a SystemError if a function returns a result and raises an exception. The SystemError is chained to the previous exception. diff --git a/Tools/msi/bundle/Default.thm b/Tools/msi/bundle/Default.thm --- a/Tools/msi/bundle/Default.thm +++ b/Tools/msi/bundle/Default.thm @@ -115,7 +115,7 @@ #(loc.FailureHyperlinkLogText) - Failure Message + #(loc.FailureRestartText) diff --git a/Tools/msi/bundle/bundle.wxs b/Tools/msi/bundle/bundle.wxs --- a/Tools/msi/bundle/bundle.wxs +++ b/Tools/msi/bundle/bundle.wxs @@ -2,11 +2,11 @@ diff --git a/Tools/msi/msi.props b/Tools/msi/msi.props --- a/Tools/msi/msi.props +++ b/Tools/msi/msi.props @@ -62,7 +62,7 @@ 64-bit $(DefineConstants); - Version=$(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber).$(RevisionNumber); + Version=$(MajorVersionNumber).$(MinorVersionNumber).$(Field3Value).0; ShortVersion=$(MajorVersionNumber).$(MinorVersionNumber); LongVersion=$(PythonVersion); MajorVersionNumber=$(MajorVersionNumber); @@ -142,7 +142,7 @@ - <_Uuids>@(_Uuid->'("%(Identity)", "%(Uri)")',',') + <_Uuids>@(_Uuid->'("%(Identity)", "$(MajorVersionNumber).$(MinorVersionNumber)/%(Uri)")',',') <_GenerateCommand>import uuid; print('\n'.join('{}={}'.format(i, uuid.uuid5(uuid.UUID('c8d9733e-a70c-43ff-ab0c-e26456f11083'), '$(ReleaseUri)' + j)) for i,j in [$(_Uuids.Replace(`"`,`'`))])) diff --git a/Tools/msi/uploadrelease.bat b/Tools/msi/uploadrelease.bat --- a/Tools/msi/uploadrelease.bat +++ b/Tools/msi/uploadrelease.bat @@ -30,9 +30,10 @@ echo Found pscp.exe at %PSCP% call "%PCBUILD%env.bat" > nul 2> nul +pushd "%D%" msbuild /v:m /nologo uploadrelease.proj /t:Upload /p:Platform=x86 msbuild /v:m /nologo uploadrelease.proj /t:Upload /p:Platform=x64 /p:IncludeDoc=false - +popd exit /B 0 :Help diff --git a/Tools/msi/uploadrelease.proj b/Tools/msi/uploadrelease.proj --- a/Tools/msi/uploadrelease.proj +++ b/Tools/msi/uploadrelease.proj @@ -41,14 +41,14 @@ diff --git a/configure b/configure --- a/configure +++ b/configure @@ -14200,7 +14200,15 @@ $as_echo "$ABIFLAGS" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking SOABI" >&5 $as_echo_n "checking SOABI... " >&6; } -SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS} + +case $ac_sys_system in + Linux*|GNU*) + BITNESS_SUFFIX=-$(($ac_cv_sizeof_void_p * 8))b;; + *) + BITNESS_SUFFIX=;; +esac +SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${BITNESS_SUFFIX} + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SOABI" >&5 $as_echo "$SOABI" >&6; } diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -4175,7 +4175,15 @@ AC_MSG_CHECKING(ABIFLAGS) AC_MSG_RESULT($ABIFLAGS) AC_MSG_CHECKING(SOABI) -SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS} + +case $ac_sys_system in + Linux*|GNU*) + BITNESS_SUFFIX=-$(($ac_cv_sizeof_void_p * 8))b;; + *) + BITNESS_SUFFIX=;; +esac +SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${BITNESS_SUFFIX} + AC_MSG_RESULT($SOABI) AC_SUBST(EXT_SUFFIX) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 10:43:39 2015 From: python-checkins at python.org (larry.hastings) Date: Mon, 09 Mar 2015 09:43:39 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_Updated_3=2E5_release_schedul?= =?utf-8?q?e_and_features=2E?= Message-ID: <20150309094339.1195.76641@psf.io> https://hg.python.org/peps/rev/b021806bcb43 changeset: 5724:b021806bcb43 user: Larry Hastings date: Mon Mar 09 02:43:35 2015 -0700 summary: Updated 3.5 release schedule and features. files: pep-0478.txt | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pep-0478.txt b/pep-0478.txt --- a/pep-0478.txt +++ b/pep-0478.txt @@ -37,7 +37,7 @@ The releases: - 3.5.0 alpha 1: February 8, 2015 -- 3.5.0 alpha 2: March 8, 2015 +- 3.5.0 alpha 2: March 9, 2015 - 3.5.0 alpha 3: March 28, 2015 - 3.5.0 alpha 4: April 19, 2015 - 3.5.0 beta 1: May 24, 2015 @@ -59,12 +59,12 @@ * PEP 465, a new matrix multiplication operator * PEP 461, %-formatting for binary strings +* PEP 471, os.scandir() Accepted PEPs: * PEP 441, improved Python zip application support * PEP 448, additional unpacking generalizations -* PEP 471, os.scandir() * PEP 475, retrying system calls that fail with EINTR * PEP 485, math.isclose(), a function for testing approximate equality * PEP 486, make the Python Launcher aware of virtual environments -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Mon Mar 9 15:38:36 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 09 Mar 2015 14:38:36 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_fix_up_import_?= =?utf-8?q?style?= Message-ID: <20150309143805.1797.46890@psf.io> https://hg.python.org/cpython/rev/54d9c3be8f50 changeset: 94918:54d9c3be8f50 branch: 3.4 parent: 94910:e623283e4dd6 user: Benjamin Peterson date: Mon Mar 09 10:37:50 2015 -0400 summary: fix up import style files: Lib/test/test_float.py | 13 ++++++++----- 1 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -1,13 +1,16 @@ -import unittest, struct +import fractions +import math +import operator import os +import random import sys +import struct +import time +import unittest + from test import support -import math from math import isinf, isnan, copysign, ldexp -import operator -import time -import random, fractions INF = float("inf") NAN = float("nan") -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 15:38:36 2015 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 09 Mar 2015 14:38:36 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <20150309143805.9682.27231@psf.io> https://hg.python.org/cpython/rev/5a87fb8df5fe changeset: 94919:5a87fb8df5fe parent: 94917:41839a8a526b parent: 94918:54d9c3be8f50 user: Benjamin Peterson date: Mon Mar 09 10:37:59 2015 -0400 summary: merge 3.4 files: Lib/test/test_float.py | 13 ++++++++----- 1 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -1,13 +1,16 @@ -import unittest, struct +import fractions +import math +import operator import os +import random import sys +import struct +import time +import unittest + from test import support -import math from math import isinf, isnan, copysign, ldexp -import operator -import time -import random, fractions INF = float("inf") NAN = float("nan") -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Mon Mar 9 18:06:25 2015 From: python-checkins at python.org (steve.dower) Date: Mon, 09 Mar 2015 17:06:25 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323619=3A_Ensure_C?= =?utf-8?q?_variable_is_initialized_before_using_it=2E?= Message-ID: <20150309170615.11195.58611@psf.io> https://hg.python.org/cpython/rev/0469af231d22 changeset: 94920:0469af231d22 user: Steve Dower date: Mon Mar 09 10:05:50 2015 -0700 summary: Issue #23619: Ensure C variable is initialized before using it. The garbage in _crtInstalledToken was causing the IsCrtInstalled() function to return TRUE without actually checking anything. We now initialize the variable first. files: Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp --- a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp +++ b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp @@ -2574,6 +2574,7 @@ } } + _crtInstalledToken = -1; pEngine->SetVariableNumeric(L"CRTInstalled", IsCrtInstalled() ? 1 : 0); _wixLoc = nullptr; @@ -2603,8 +2604,6 @@ _suppressRepair = FALSE; _modifying = FALSE; - _crtInstalledToken = -1; - _overridableVariables = nullptr; _taskbarList = nullptr; _taskbarButtonCreatedMessage = UINT_MAX; -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Tue Mar 10 04:54:56 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 10 Mar 2015 04:54:56 +0100 Subject: [Python-checkins] Daily reference leaks (0469af231d22): sum=0 Message-ID: results for 0469af231d22 on branch "default" -------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog2iRh5S', '--timeout', '120', '-x', 'test_eintr'] From python-checkins at python.org Tue Mar 10 13:25:48 2015 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Mar 2015 12:25:48 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323571=3A_Oops=2C_?= =?utf-8?q?fix_=23ifdef_assert=28=29?= Message-ID: <20150310122529.1307.63110@psf.io> https://hg.python.org/cpython/rev/52c7017fdcdd changeset: 94921:52c7017fdcdd user: Victor Stinner date: Mon Mar 09 15:55:37 2015 +0100 summary: Issue #23571: Oops, fix #ifdef assert() assert() are noop when NDEBUG is defined. We want the opposite. files: Objects/abstract.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2078,7 +2078,7 @@ { int err_occurred = (PyErr_Occurred() != NULL); -#ifdef NDEBUG +#ifndef NDEBUG /* In debug mode: abort() with an assertion error. Use two different assertions, so if an assertion fails, it's possible to know if result was set or not and if an exception was raised or not. */ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 13:25:48 2015 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Mar 2015 12:25:48 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323605=3A_os=2Ewal?= =?utf-8?q?k=28=29_now_calls_os=2Escandir=28=29_instead_of_os=2Elistdir=28?= =?utf-8?b?KS4=?= Message-ID: <20150310122530.117213.50809@psf.io> https://hg.python.org/cpython/rev/f805fdacdfe0 changeset: 94922:f805fdacdfe0 user: Victor Stinner date: Tue Mar 10 13:20:34 2015 +0100 summary: Issue #23605: os.walk() now calls os.scandir() instead of os.listdir(). The usage of os.scandir() reduces the number of calls to os.stat(). Initial patch written by Ben Hoyt. files: Doc/library/os.rst | 5 +++ Lib/os.py | 51 ++++++++++++++++++++++++---------- Misc/NEWS | 4 ++ 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2618,6 +2618,11 @@ for name in dirs: os.rmdir(os.path.join(root, name)) + .. versionchanged:: 3.5 + The function now calls :func:`os.scandir` instead of :func:`os.listdir`. + The usage of :func:`os.scandir` reduces the number of calls to + :func:`os.stat`. + .. function:: fwalk(top='.', topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=None) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -323,7 +323,7 @@ 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 + By default errors from the os.scandir() call are ignored. If optional arg 'onerror' is specified, it should be a function; it will be called with one argument, an OSError instance. It can report the error to continue with the walk, or raise the exception @@ -352,7 +352,9 @@ """ - islink, join, isdir = path.islink, path.join, path.isdir + dirs = [] + nondirs = [] + symlinks = set() # We may not have read permission for top, in which case we can't # get a list of the files the directory contains. os.walk @@ -360,27 +362,46 @@ # minor reason when (say) a thousand readable directories are still # left to visit. That logic is copied here. try: - # Note that listdir is global in this module due + # Note that scandir is global in this module due # to earlier import-*. - names = listdir(top) - except OSError as err: + for entry in scandir(top): + try: + is_dir = entry.is_dir() + except OSError: + # If is_dir() raises an OSError, consider that the entry is not + # a directory, same behaviour than os.path.isdir(). + is_dir = False + + if is_dir: + dirs.append(entry.name) + + try: + if entry.is_symlink(): + symlinks.add(entry.name) + except OSError: + # If is_symlink() raises an OSError, consider that the + # entry is not a symbolik link, same behaviour than + # os.path.islink(). + pass + else: + nondirs.append(entry.name) + except OSError as error: + # scandir() or iterating into scandir() iterator raised an OSError if onerror is not None: - onerror(err) + onerror(error) return - dirs, nondirs = [], [] - for name in names: - if isdir(join(top, name)): - dirs.append(name) - else: - nondirs.append(name) - + # Yield before recursion if going top down if topdown: yield top, dirs, nondirs + + # Recurse into sub-directories for name in dirs: - new_path = join(top, name) - if followlinks or not islink(new_path): + if followlinks or name not in symlinks: + new_path = path.join(top, name) yield from walk(new_path, topdown, onerror, followlinks) + + # Yield after recursion if going bottom up if not topdown: yield top, dirs, nondirs diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,10 @@ Library ------- +- Issue #23605: os.walk() now calls os.scandir() instead of os.listdir(). + The usage of os.scandir() reduces the number of calls to os.stat(). + Initial patch written by Ben Hoyt. + What's New in Python 3.5 alpha 2? ================================= -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 13:29:51 2015 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Mar 2015 12:29:51 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2322524=3A_Rephrase?= =?utf-8?q?_scandir_addition_in_What=27s_New_in_Python_3=2E5?= Message-ID: <20150310122951.8065.90802@psf.io> https://hg.python.org/cpython/rev/8d76dabd40f6 changeset: 94923:8d76dabd40f6 user: Victor Stinner date: Tue Mar 10 13:29:41 2015 +0100 summary: Issue #22524: Rephrase scandir addition in What's New in Python 3.5 Patch written by Ben Hoyt. files: Doc/whatsnew/3.5.rst | 26 ++++++++++++++++++-------- 1 files changed, 18 insertions(+), 8 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 @@ -115,13 +115,17 @@ PEP 471 - os.scandir() function -- a better and faster directory iterator ------------------------------------------------------------------------- -:pep:`471` includes a new directory iteration function, :func:`os.scandir`, -in the standard library. +:pep:`471` adds a new directory iteration function, :func:`os.scandir`, +to the standard library. Additionally, :func:`os.walk` is now +implemented using :func:`os.scandir`, which speeds it up by 3-5 times +on POSIX systems and by 7-20 times on Windows systems. + +PEP and implementation written by Ben Hoyt with the help of Victor Stinner. .. seealso:: :pep:`471` -- os.scandir() function -- a better and faster directory - iterator. + iterator PEP 475: Retry system calls failing with EINTR @@ -131,8 +135,6 @@ this means that user code doesn't have to deal with EINTR or InterruptedError manually, and should make it more robust against asynchronous signal reception. -PEP and implementation written by Ben Hoyt with the help of Victor Stinner. - .. seealso:: :pep:`475` -- Retry system calls failing with EINTR @@ -294,9 +296,11 @@ os -- -* New :func:`os.scandir` function: Return an iterator of :class:`os.DirEntry` - objects corresponding to the entries in the directory given by *path*. - (Implementation written by Ben Hoyt with the help of Victor Stinner.) +* New :func:`os.scandir` function that exposes file information from + the operating system when listing a directory. :func:`os.scandir` + returns an iterator of :class:`os.DirEntry` objects corresponding to + the entries in the directory given by *path*. (Contributed by Ben + Hoyt with the help of Victor Stinner in :issue:`22524`.) * :class:`os.stat_result` now has a :attr:`~os.stat_result.st_file_attributes` attribute on Windows. (Contributed by Ben Hoyt in :issue:`21719`.) @@ -419,6 +423,12 @@ The following performance enhancements have been added: +* :func:`os.walk` has been sped up by 3-5x on POSIX systems and 7-20x + on Windows. This was done using the new :func:`os.scandir` function, + which exposes file information from the underlying ``readdir`` and + ``FindFirstFile``/``FindNextFile`` system calls. (Contributed by + Ben Hoyt with help from Victor Stinner in :issue:`23605`.) + * 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. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 13:32:05 2015 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Mar 2015 12:32:05 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogTWVyZ2UgMy40IChvcyBkb2Mp?= Message-ID: <20150310123205.16661.99925@psf.io> https://hg.python.org/cpython/rev/756c4822ba38 changeset: 94925:756c4822ba38 parent: 94923:8d76dabd40f6 parent: 94924:50e32059a404 user: Victor Stinner date: Tue Mar 10 13:31:58 2015 +0100 summary: Merge 3.4 (os doc) files: Doc/library/os.rst | 5 +++-- 1 files changed, 3 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 @@ -2604,8 +2604,9 @@ if 'CVS' in dirs: dirs.remove('CVS') # don't visit CVS directories - In the next example, walking the tree bottom-up is essential: :func:`rmdir` - doesn't allow deleting a directory before the directory is empty:: + In the next example (simple implementation of :func:`shutil.rmtree`), + walking the tree bottom-up is essential, :func:`rmdir` doesn't allow + deleting a directory before the directory is empty:: # Delete everything reachable from the directory named in "top", # assuming there are no symbolic links. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 13:32:06 2015 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Mar 2015 12:32:06 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNjA1?= =?utf-8?q?=3A_os=2Ewalk=28=29_doc_now_mentions_shutil=2Ermtree=28=29_in_t?= =?utf-8?q?he_last_example?= Message-ID: <20150310123205.1318.20772@psf.io> https://hg.python.org/cpython/rev/50e32059a404 changeset: 94924:50e32059a404 branch: 3.4 parent: 94918:54d9c3be8f50 user: Victor Stinner date: Tue Mar 10 13:31:47 2015 +0100 summary: Issue #23605: os.walk() doc now mentions shutil.rmtree() in the last example files: Doc/library/os.rst | 5 +++-- 1 files changed, 3 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 @@ -2391,8 +2391,9 @@ if 'CVS' in dirs: dirs.remove('CVS') # don't visit CVS directories - In the next example, walking the tree bottom-up is essential: :func:`rmdir` - doesn't allow deleting a directory before the directory is empty:: + In the next example (simple implementation of :func:`shutil.rmtree`), + walking the tree bottom-up is essential, :func:`rmdir` doesn't allow + deleting a directory before the directory is empty:: # Delete everything reachable from the directory named in "top", # assuming there are no symbolic links. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 13:46:48 2015 From: python-checkins at python.org (berker.peksag) Date: Tue, 10 Mar 2015 12:46:48 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNDMy?= =?utf-8?q?=3A_Remove_duplicate_content_from_SystemExit_docs=2E?= Message-ID: <20150310124647.9668.22443@psf.io> https://hg.python.org/cpython/rev/64c0b4aead0f changeset: 94926:64c0b4aead0f branch: 3.4 parent: 94924:50e32059a404 user: Berker Peksag date: Tue Mar 10 14:47:15 2015 +0200 summary: Issue #23432: Remove duplicate content from SystemExit docs. Also, document SystemExit.code attribute explicitly. files: Doc/library/exceptions.rst | 26 +++++++++++++------------- 1 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -353,18 +353,17 @@ .. exception:: SystemExit - This exception is raised by the :func:`sys.exit` function. When it is not - handled, the Python interpreter exits; no stack traceback is printed. If the - associated value is an integer, it specifies the system exit status (passed - to C's :c:func:`exit` function); if it is ``None``, the exit status is zero; - if it has another type (such as a string), the object's value is printed and + This exception is raised by the :func:`sys.exit` function. It inherits from + :exc:`BaseException` instead of :exc:`Exception` so that it is not accidentally + caught by code that catches :exc:`Exception`. This allows the exception to + properly propagate up and cause the interpreter to exit. When it is not + handled, the Python interpreter exits; no stack traceback is printed. The + constructor accepts the same optional argument passed to :func:`sys.exit`. + If the value is an integer, it specifies the system exit status (passed to + C's :c:func:`exit` function); if it is ``None``, the exit status is zero; if + it has another type (such as a string), the object's value is printed and the exit status is one. - Instances have an attribute :attr:`!code` which is set to the proposed exit - status or error message (defaulting to ``None``). Also, this exception derives - directly from :exc:`BaseException` and not :exc:`Exception`, since it is not - technically an error. - A call to :func:`sys.exit` is translated into an exception so that clean-up handlers (:keyword:`finally` clauses of :keyword:`try` statements) can be executed, and so that a debugger can execute a script without running the risk @@ -372,9 +371,10 @@ absolutely positively necessary to exit immediately (for example, in the child process after a call to :func:`os.fork`). - The exception inherits from :exc:`BaseException` instead of :exc:`Exception` so - that it is not accidentally caught by code that catches :exc:`Exception`. This - allows the exception to properly propagate up and cause the interpreter to exit. + .. attribute:: code + + The exit status or error message that is passed to the constructor. + (Defaults to ``None``.) .. exception:: TypeError -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 13:47:06 2015 From: python-checkins at python.org (berker.peksag) Date: Tue, 10 Mar 2015 12:47:06 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323432=3A_Remove_duplicate_content_from_SystemEx?= =?utf-8?q?it_docs=2E?= Message-ID: <20150310124648.1199.35897@psf.io> https://hg.python.org/cpython/rev/5163fb8dc61f changeset: 94927:5163fb8dc61f parent: 94925:756c4822ba38 parent: 94926:64c0b4aead0f user: Berker Peksag date: Tue Mar 10 14:47:41 2015 +0200 summary: Issue #23432: Remove duplicate content from SystemExit docs. Also, document SystemExit.code attribute explicitly. files: Doc/library/exceptions.rst | 26 +++++++++++++------------- 1 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -353,18 +353,17 @@ .. exception:: SystemExit - This exception is raised by the :func:`sys.exit` function. When it is not - handled, the Python interpreter exits; no stack traceback is printed. If the - associated value is an integer, it specifies the system exit status (passed - to C's :c:func:`exit` function); if it is ``None``, the exit status is zero; - if it has another type (such as a string), the object's value is printed and + This exception is raised by the :func:`sys.exit` function. It inherits from + :exc:`BaseException` instead of :exc:`Exception` so that it is not accidentally + caught by code that catches :exc:`Exception`. This allows the exception to + properly propagate up and cause the interpreter to exit. When it is not + handled, the Python interpreter exits; no stack traceback is printed. The + constructor accepts the same optional argument passed to :func:`sys.exit`. + If the value is an integer, it specifies the system exit status (passed to + C's :c:func:`exit` function); if it is ``None``, the exit status is zero; if + it has another type (such as a string), the object's value is printed and the exit status is one. - Instances have an attribute :attr:`!code` which is set to the proposed exit - status or error message (defaulting to ``None``). Also, this exception derives - directly from :exc:`BaseException` and not :exc:`Exception`, since it is not - technically an error. - A call to :func:`sys.exit` is translated into an exception so that clean-up handlers (:keyword:`finally` clauses of :keyword:`try` statements) can be executed, and so that a debugger can execute a script without running the risk @@ -372,9 +371,10 @@ absolutely positively necessary to exit immediately (for example, in the child process after a call to :func:`os.fork`). - The exception inherits from :exc:`BaseException` instead of :exc:`Exception` so - that it is not accidentally caught by code that catches :exc:`Exception`. This - allows the exception to properly propagate up and cause the interpreter to exit. + .. attribute:: code + + The exit status or error message that is passed to the constructor. + (Defaults to ``None``.) .. exception:: TypeError -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 16:29:38 2015 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Mar 2015 15:29:38 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbyBkb2M6?= =?utf-8?q?_changes_on_the_Queue_API_missed_Python_3=2E4=2E3_release?= Message-ID: <20150310152937.1204.2641@psf.io> https://hg.python.org/cpython/rev/12a857e173b7 changeset: 94928:12a857e173b7 branch: 3.4 parent: 94926:64c0b4aead0f user: Victor Stinner date: Tue Mar 10 16:27:54 2015 +0100 summary: asyncio doc: changes on the Queue API missed Python 3.4.3 release files: Doc/library/asyncio-queue.rst | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/asyncio-queue.rst b/Doc/library/asyncio-queue.rst --- a/Doc/library/asyncio-queue.rst +++ b/Doc/library/asyncio-queue.rst @@ -33,7 +33,7 @@ This class is :ref:`not thread safe `. - .. versionchanged:: 3.4.3 + .. versionchanged:: 3.4.4 New :meth:`join` and :meth:`task_done` methods. .. method:: empty() @@ -79,7 +79,7 @@ This method is a :ref:`coroutine `. - .. versionadded:: 3.4.3 + .. versionadded:: 3.4.4 .. coroutinemethod:: put(item) @@ -117,7 +117,7 @@ Raises :exc:`ValueError` if called more times than there were items placed in the queue. - .. versionadded:: 3.4.3 + .. versionadded:: 3.4.4 .. attribute:: maxsize @@ -151,7 +151,7 @@ Deprecated alias for :class:`Queue`. - .. deprecated:: 3.4.3 + .. deprecated:: 3.4.4 Exceptions -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 16:29:38 2015 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Mar 2015 15:29:38 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_3=2E4_=28asyncio_doc=29?= Message-ID: <20150310152938.1775.71747@psf.io> https://hg.python.org/cpython/rev/388edf038328 changeset: 94929:388edf038328 parent: 94927:5163fb8dc61f parent: 94928:12a857e173b7 user: Victor Stinner date: Tue Mar 10 16:29:31 2015 +0100 summary: Merge 3.4 (asyncio doc) files: Doc/library/asyncio-queue.rst | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/asyncio-queue.rst b/Doc/library/asyncio-queue.rst --- a/Doc/library/asyncio-queue.rst +++ b/Doc/library/asyncio-queue.rst @@ -33,7 +33,7 @@ This class is :ref:`not thread safe `. - .. versionchanged:: 3.4.3 + .. versionchanged:: 3.4.4 New :meth:`join` and :meth:`task_done` methods. .. method:: empty() @@ -79,7 +79,7 @@ This method is a :ref:`coroutine `. - .. versionadded:: 3.4.3 + .. versionadded:: 3.4.4 .. coroutinemethod:: put(item) @@ -117,7 +117,7 @@ Raises :exc:`ValueError` if called more times than there were items placed in the queue. - .. versionadded:: 3.4.3 + .. versionadded:: 3.4.4 .. attribute:: maxsize @@ -151,7 +151,7 @@ Deprecated alias for :class:`Queue`. - .. deprecated:: 3.4.3 + .. deprecated:: 3.4.4 Exceptions -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 16:35:30 2015 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Mar 2015 15:35:30 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogYXN5bmNpbzogRml4?= =?utf-8?q?_repr=28BaseSubprocessTransport=29_if_it_didn=27t_start_yet?= Message-ID: <20150310153519.7575.87291@psf.io> https://hg.python.org/cpython/rev/869eff562b55 changeset: 94930:869eff562b55 branch: 3.4 parent: 94928:12a857e173b7 user: Victor Stinner date: Tue Mar 10 16:32:29 2015 +0100 summary: asyncio: Fix repr(BaseSubprocessTransport) if it didn't start yet Replace "running" with "not started" and don't show the pid if the subprocess didn't start yet. files: Lib/asyncio/base_subprocess.py | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-) 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 @@ -54,11 +54,14 @@ info = [self.__class__.__name__] if self._closed: info.append('closed') - info.append('pid=%s' % self._pid) + if self._pid is not None: + info.append('pid=%s' % self._pid) if self._returncode is not None: info.append('returncode=%s' % self._returncode) + elif self._pid is not None: + info.append('running') else: - info.append('running') + info.append('not started') stdin = self._pipes.get(0) if stdin is not None: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 16:35:30 2015 From: python-checkins at python.org (victor.stinner) Date: Tue, 10 Mar 2015 15:35:30 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Merge_3=2E4_=28asyncio=29?= Message-ID: <20150310153519.8065.21900@psf.io> https://hg.python.org/cpython/rev/be3b41703822 changeset: 94931:be3b41703822 parent: 94929:388edf038328 parent: 94930:869eff562b55 user: Victor Stinner date: Tue Mar 10 16:32:50 2015 +0100 summary: Merge 3.4 (asyncio) files: Lib/asyncio/base_subprocess.py | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-) 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 @@ -54,11 +54,14 @@ info = [self.__class__.__name__] if self._closed: info.append('closed') - info.append('pid=%s' % self._pid) + if self._pid is not None: + info.append('pid=%s' % self._pid) if self._returncode is not None: info.append('returncode=%s' % self._returncode) + elif self._pid is not None: + info.append('running') else: - info.append('running') + info.append('not started') stdin = self._pipes.get(0) if stdin is not None: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 17:58:48 2015 From: python-checkins at python.org (steve.dower) Date: Tue, 10 Mar 2015 16:58:48 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323606=3A_Disable_?= =?utf-8?q?ctypes=2Eutil=2Efind=5Flibrary=28=22c=22=29_on_Windows_so_tests?= =?utf-8?q?_are?= Message-ID: <20150310165745.19099.87345@psf.io> https://hg.python.org/cpython/rev/86c9ef950288 changeset: 94932:86c9ef950288 user: Steve Dower date: Tue Mar 10 09:56:38 2015 -0700 summary: Issue #23606: Disable ctypes.util.find_library("c") on Windows so tests are skipped while we figure out how best to approach the CRT change files: Lib/ctypes/util.py | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -41,7 +41,9 @@ elif version <= 13: clibname = 'msvcr%d' % (version * 10) else: - clibname = 'appcrt%d' % (version * 10) + # CRT is no longer directly loadable. See issue23606 for the + # discussion about alternative approaches. + return None # If python was built with in debug mode import importlib.machinery -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 21:20:41 2015 From: python-checkins at python.org (steve.dower) Date: Tue, 10 Mar 2015 20:20:41 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIyMDI4?= =?utf-8?q?=3A_Ensure_mimetypes_will_not_open_registry_keys_with_embedded_?= =?utf-8?q?nulls?= Message-ID: <20150310202041.11195.96285@psf.io> https://hg.python.org/cpython/rev/7c4c4e43c452 changeset: 94933:7c4c4e43c452 branch: 2.7 parent: 94909:17253605eca4 user: Steve Dower date: Tue Mar 10 13:19:17 2015 -0700 summary: Issue #22028: Ensure mimetypes will not open registry keys with embedded nulls files: Lib/mimetypes.py | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -242,9 +242,12 @@ i = 0 while True: try: - yield _winreg.EnumKey(mimedb, i) + ctype = _winreg.EnumKey(mimedb, i) except EnvironmentError: break + else: + if '\0' not in ctype: + yield ctype i += 1 default_encoding = sys.getdefaultencoding() -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 21:20:59 2015 From: python-checkins at python.org (steve.dower) Date: Tue, 10 Mar 2015 20:20:59 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2322028=3A_Ensure_mimetypes_will_not_open_registr?= =?utf-8?q?y_keys_with_embedded_nulls?= Message-ID: <20150310202059.7559.38901@psf.io> https://hg.python.org/cpython/rev/6ccade563bf7 changeset: 94936:6ccade563bf7 parent: 94934:499be67e3dcb parent: 94935:bf2016a1911f user: Steve Dower date: Tue Mar 10 13:20:21 2015 -0700 summary: Issue #22028: Ensure mimetypes will not open registry keys with embedded nulls files: Lib/mimetypes.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -246,7 +246,8 @@ except EnvironmentError: break else: - yield ctype + if '\0' not in ctype: + yield ctype i += 1 with _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, '') as hkcr: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 21:20:59 2015 From: python-checkins at python.org (steve.dower) Date: Tue, 10 Mar 2015 20:20:59 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Adds_note_about_installed_?= =?utf-8?q?debug_binaries=2E?= Message-ID: <20150310202059.7559.20585@psf.io> https://hg.python.org/cpython/rev/499be67e3dcb changeset: 94934:499be67e3dcb parent: 94932:86c9ef950288 user: Steve Dower date: Tue Mar 10 13:15:40 2015 -0700 summary: Adds note about installed debug binaries. files: Tools/msi/bundle/Default.wxl | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Tools/msi/bundle/Default.wxl b/Tools/msi/bundle/Default.wxl --- a/Tools/msi/bundle/Default.wxl +++ b/Tools/msi/bundle/Default.wxl @@ -73,7 +73,7 @@ Install as &Administrator &Precompile standard library Install debugging &symbols - Install debu&g binaries + Install debu&g binaries (requires VS 2015 or later) [ActionLikeInstallation] Progress [ActionLikeInstalling]: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 21:20:59 2015 From: python-checkins at python.org (steve.dower) Date: Tue, 10 Mar 2015 20:20:59 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIyMDI4?= =?utf-8?q?=3A_Ensure_mimetypes_will_not_open_registry_keys_with_embedded_?= =?utf-8?q?nulls?= Message-ID: <20150310202059.9678.8251@psf.io> https://hg.python.org/cpython/rev/bf2016a1911f changeset: 94935:bf2016a1911f branch: 3.4 parent: 94930:869eff562b55 user: Steve Dower date: Tue Mar 10 13:17:21 2015 -0700 summary: Issue #22028: Ensure mimetypes will not open registry keys with embedded nulls files: Lib/mimetypes.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -246,7 +246,8 @@ except EnvironmentError: break else: - yield ctype + if '\0' not in ctype: + yield ctype i += 1 with _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, '') as hkcr: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 22:37:40 2015 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 10 Mar 2015 21:37:40 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNjI5?= =?utf-8?q?=3A_Fix_the_default_=5F=5Fsizeof=5F=5F_implementation_for_varia?= =?utf-8?q?ble-sized?= Message-ID: <20150310213740.16669.3945@psf.io> https://hg.python.org/cpython/rev/f9afa4f87570 changeset: 94937:f9afa4f87570 branch: 3.4 parent: 94935:bf2016a1911f user: Antoine Pitrou date: Tue Mar 10 22:32:00 2015 +0100 summary: Issue #23629: Fix the default __sizeof__ implementation for variable-sized objects. files: Lib/test/test_buffer.py | 15 +++++++++++++++ Lib/test/test_sys.py | 5 +++-- Misc/NEWS | 3 +++ Objects/bytesobject.c | 14 -------------- Objects/tupleobject.c | 12 ------------ Objects/typeobject.c | 2 +- 6 files changed, 22 insertions(+), 29 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 @@ -2449,6 +2449,21 @@ self.assertEqual(m.tobytes(), b'') self.assertEqual(m.tolist(), []) + check_sizeof = support.check_sizeof + + def test_memoryview_sizeof(self): + check = self.check_sizeof + vsize = support.calcvobjsize + base_struct = 'Pnin 2P2n2i5P 3cP' + per_dim = '3n' + + items = list(range(8)) + check(memoryview(b''), vsize(base_struct + 1 * per_dim)) + a = ndarray(items, shape=[2, 4], format="b") + check(memoryview(a), vsize(base_struct + 2 * per_dim)) + a = ndarray(items, shape=[2, 2, 2], format="b") + check(memoryview(a), vsize(base_struct + 3 * per_dim)) + def test_memoryview_struct_module(self): class INT(object): 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 @@ -778,6 +778,9 @@ check(x, vsize('n2Pi') + x.__alloc__()) # bytearray_iterator check(iter(bytearray()), size('nP')) + # bytes + check(b'', vsize('n') + 1) + check(b'x' * 10, vsize('n') + 11) # cell def get_cell(): x = 42 @@ -897,8 +900,6 @@ check(int(PyLong_BASE), vsize('') + 2*self.longdigit) check(int(PyLong_BASE**2-1), vsize('') + 2*self.longdigit) check(int(PyLong_BASE**2), vsize('') + 3*self.longdigit) - # memoryview - check(memoryview(b''), size('Pnin 2P2n2i5P 3cPn')) # module check(unittest, size('PnPPP')) # None diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #23629: Fix the default __sizeof__ implementation for variable-sized + objects. + Library ------- diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -2370,18 +2370,6 @@ return NULL; } -PyDoc_STRVAR(sizeof__doc__, -"B.__sizeof__() -> size of B in memory, in bytes"); - -static PyObject * -bytes_sizeof(PyBytesObject *v) -{ - Py_ssize_t res; - res = PyBytesObject_SIZE + Py_SIZE(v) * Py_TYPE(v)->tp_itemsize; - return PyLong_FromSsize_t(res); -} - - static PyObject * bytes_getnewargs(PyBytesObject *v) { @@ -2447,8 +2435,6 @@ translate__doc__}, {"upper", (PyCFunction)stringlib_upper, METH_NOARGS, _Py_upper__doc__}, {"zfill", (PyCFunction)stringlib_zfill, METH_VARARGS, zfill__doc__}, - {"__sizeof__", (PyCFunction)bytes_sizeof, METH_NOARGS, - sizeof__doc__}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -759,27 +759,15 @@ } -static PyObject * -tuple_sizeof(PyTupleObject *self) -{ - Py_ssize_t res; - - res = PyTuple_Type.tp_basicsize + Py_SIZE(self) * sizeof(PyObject *); - return PyLong_FromSsize_t(res); -} - PyDoc_STRVAR(index_doc, "T.index(value, [start, [stop]]) -> integer -- return first index of value.\n" "Raises ValueError if the value is not present." ); PyDoc_STRVAR(count_doc, "T.count(value) -> integer -- return number of occurrences of value"); -PyDoc_STRVAR(sizeof_doc, -"T.__sizeof__() -- size of T in memory, in bytes"); static PyMethodDef tuple_methods[] = { {"__getnewargs__", (PyCFunction)tuple_getnewargs, METH_NOARGS}, - {"__sizeof__", (PyCFunction)tuple_sizeof, METH_NOARGS, sizeof_doc}, {"index", (PyCFunction)tupleindex, METH_VARARGS, index_doc}, {"count", (PyCFunction)tuplecount, METH_O, count_doc}, {NULL, NULL} /* sentinel */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4258,7 +4258,7 @@ res = 0; isize = self->ob_type->tp_itemsize; if (isize > 0) - res = Py_SIZE(self->ob_type) * isize; + res = Py_SIZE(self) * isize; res += self->ob_type->tp_basicsize; return PyLong_FromSsize_t(res); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 22:37:40 2015 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 10 Mar 2015 21:37:40 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323629=3A_Fix_the_default_=5F=5Fsizeof=5F=5F_imp?= =?utf-8?q?lementation_for_variable-sized?= Message-ID: <20150310213740.19079.73996@psf.io> https://hg.python.org/cpython/rev/9994e0172a0c changeset: 94938:9994e0172a0c parent: 94936:6ccade563bf7 parent: 94937:f9afa4f87570 user: Antoine Pitrou date: Tue Mar 10 22:35:24 2015 +0100 summary: Issue #23629: Fix the default __sizeof__ implementation for variable-sized objects. files: Lib/test/test_buffer.py | 15 +++++++++++ Lib/test/test_sys.py | 5 ++- Misc/NEWS | 3 ++ Objects/bytesobject.c | 37 ----------------------------- Objects/tupleobject.c | 12 --------- Objects/typeobject.c | 2 +- 6 files changed, 22 insertions(+), 52 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 @@ -2462,6 +2462,21 @@ self.assertEqual(m.tobytes(), b'') self.assertEqual(m.tolist(), []) + check_sizeof = support.check_sizeof + + def test_memoryview_sizeof(self): + check = self.check_sizeof + vsize = support.calcvobjsize + base_struct = 'Pnin 2P2n2i5P P' + per_dim = '3n' + + items = list(range(8)) + check(memoryview(b''), vsize(base_struct + 1 * per_dim)) + a = ndarray(items, shape=[2, 4], format="b") + check(memoryview(a), vsize(base_struct + 2 * per_dim)) + a = ndarray(items, shape=[2, 2, 2], format="b") + check(memoryview(a), vsize(base_struct + 3 * per_dim)) + def test_memoryview_struct_module(self): class INT(object): 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 @@ -846,6 +846,9 @@ check(x, vsize('n2Pi') + x.__alloc__()) # bytearray_iterator check(iter(bytearray()), size('nP')) + # bytes + check(b'', vsize('n') + 1) + check(b'x' * 10, vsize('n') + 11) # cell def get_cell(): x = 42 @@ -965,8 +968,6 @@ check(int(PyLong_BASE), vsize('') + 2*self.longdigit) check(int(PyLong_BASE**2-1), vsize('') + 2*self.longdigit) check(int(PyLong_BASE**2), vsize('') + 3*self.longdigit) - # memoryview - check(memoryview(b''), size('Pnin 2P2n2i5P Pn')) # module check(unittest, size('PnPPP')) # None diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #23629: Fix the default __sizeof__ implementation for variable-sized + objects. + Library ------- diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3463,42 +3463,6 @@ return NULL; } -/*[clinic input] -bytes.__sizeof__ as bytes_sizeof - - self: self(type="PyBytesObject *") - -Returns the size of the bytes object in memory, in bytes. -[clinic start generated code]*/ - -PyDoc_STRVAR(bytes_sizeof__doc__, -"__sizeof__($self, /)\n" -"--\n" -"\n" -"Returns the size of the bytes object in memory, in bytes."); - -#define BYTES_SIZEOF_METHODDEF \ - {"__sizeof__", (PyCFunction)bytes_sizeof, METH_NOARGS, bytes_sizeof__doc__}, - -static PyObject * -bytes_sizeof_impl(PyBytesObject *self); - -static PyObject * -bytes_sizeof(PyBytesObject *self, PyObject *Py_UNUSED(ignored)) -{ - return bytes_sizeof_impl(self); -} - -static PyObject * -bytes_sizeof_impl(PyBytesObject *self) -/*[clinic end generated code: output=44933279343f24ae input=bee4c64bb42078ed]*/ -{ - Py_ssize_t res; - res = PyBytesObject_SIZE + Py_SIZE(self) * Py_TYPE(self)->tp_itemsize; - return PyLong_FromSsize_t(res); -} - - static PyObject * bytes_getnewargs(PyBytesObject *v) { @@ -3559,7 +3523,6 @@ BYTES_TRANSLATE_METHODDEF {"upper", (PyCFunction)stringlib_upper, METH_NOARGS, _Py_upper__doc__}, {"zfill", (PyCFunction)stringlib_zfill, METH_VARARGS, zfill__doc__}, - BYTES_SIZEOF_METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -759,27 +759,15 @@ } -static PyObject * -tuple_sizeof(PyTupleObject *self) -{ - Py_ssize_t res; - - res = PyTuple_Type.tp_basicsize + Py_SIZE(self) * sizeof(PyObject *); - return PyLong_FromSsize_t(res); -} - PyDoc_STRVAR(index_doc, "T.index(value, [start, [stop]]) -> integer -- return first index of value.\n" "Raises ValueError if the value is not present." ); PyDoc_STRVAR(count_doc, "T.count(value) -> integer -- return number of occurrences of value"); -PyDoc_STRVAR(sizeof_doc, -"T.__sizeof__() -- size of T in memory, in bytes"); static PyMethodDef tuple_methods[] = { {"__getnewargs__", (PyCFunction)tuple_getnewargs, METH_NOARGS}, - {"__sizeof__", (PyCFunction)tuple_sizeof, METH_NOARGS, sizeof_doc}, {"index", (PyCFunction)tupleindex, METH_VARARGS, index_doc}, {"count", (PyCFunction)tuplecount, METH_O, count_doc}, {NULL, NULL} /* sentinel */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4276,7 +4276,7 @@ res = 0; isize = self->ob_type->tp_itemsize; if (isize > 0) - res = Py_SIZE(self->ob_type) * isize; + res = Py_SIZE(self) * isize; res += self->ob_type->tp_basicsize; return PyLong_FromSsize_t(res); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Tue Mar 10 22:41:27 2015 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 10 Mar 2015 21:41:27 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIzNjI5?= =?utf-8?q?=3A_Fix_the_default_=5F=5Fsizeof=5F=5F_implementation_for_varia?= =?utf-8?q?ble-sized?= Message-ID: <20150310214127.16661.7255@psf.io> https://hg.python.org/cpython/rev/dbd48b22b477 changeset: 94939:dbd48b22b477 branch: 2.7 parent: 94933:7c4c4e43c452 user: Antoine Pitrou date: Tue Mar 10 22:32:00 2015 +0100 summary: Issue #23629: Fix the default __sizeof__ implementation for variable-sized objects. files: Misc/NEWS | 3 +++ Objects/tupleobject.c | 12 ------------ Objects/typeobject.c | 2 +- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #23629: Fix the default __sizeof__ implementation for variable-sized + objects. + - Issue #23055: Fixed a buffer overflow in PyUnicode_FromFormatV. Analysis and fix by Guido Vranken. diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -761,27 +761,15 @@ } -static PyObject * -tuple_sizeof(PyTupleObject *self) -{ - Py_ssize_t res; - - res = PyTuple_Type.tp_basicsize + Py_SIZE(self) * sizeof(PyObject *); - return PyInt_FromSsize_t(res); -} - PyDoc_STRVAR(index_doc, "T.index(value, [start, [stop]]) -> integer -- return first index of value.\n" "Raises ValueError if the value is not present." ); PyDoc_STRVAR(count_doc, "T.count(value) -> integer -- return number of occurrences of value"); -PyDoc_STRVAR(sizeof_doc, -"T.__sizeof__() -- size of T in memory, in bytes"); static PyMethodDef tuple_methods[] = { {"__getnewargs__", (PyCFunction)tuple_getnewargs, METH_NOARGS}, - {"__sizeof__", (PyCFunction)tuple_sizeof, METH_NOARGS, sizeof_doc}, {"index", (PyCFunction)tupleindex, METH_VARARGS, index_doc}, {"count", (PyCFunction)tuplecount, METH_O, count_doc}, {NULL, NULL} /* sentinel */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3507,7 +3507,7 @@ res = 0; isize = self->ob_type->tp_itemsize; if (isize > 0) - res = self->ob_type->ob_size * isize; + res = Py_SIZE(self) * isize; res += self->ob_type->tp_basicsize; return PyInt_FromSsize_t(res); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 11 01:06:59 2015 From: python-checkins at python.org (benjamin.peterson) Date: Wed, 11 Mar 2015 00:06:59 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_close_files_ex?= =?utf-8?q?plicit_=28closes_=2321610=29?= Message-ID: <20150311000658.1369.71620@psf.io> https://hg.python.org/cpython/rev/5a5d4073d46a changeset: 94940:5a5d4073d46a branch: 2.7 user: Benjamin Peterson date: Tue Mar 10 19:06:18 2015 -0500 summary: close files explicit (closes #21610) Patch by mattip. files: Lib/modulefinder.py | 14 ++++++++------ 1 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Lib/modulefinder.py b/Lib/modulefinder.py --- a/Lib/modulefinder.py +++ b/Lib/modulefinder.py @@ -109,16 +109,16 @@ def run_script(self, pathname): self.msg(2, "run_script", pathname) - fp = open(pathname, READ_MODE) - stuff = ("", "r", imp.PY_SOURCE) - self.load_module('__main__', fp, pathname, stuff) + with open(pathname, READ_MODE) as fp: + stuff = ("", "r", imp.PY_SOURCE) + self.load_module('__main__', fp, pathname, stuff) def load_file(self, pathname): dir, name = os.path.split(pathname) name, ext = os.path.splitext(name) - fp = open(pathname, READ_MODE) - stuff = (ext, "r", imp.PY_SOURCE) - self.load_module(name, fp, pathname, stuff) + with open(pathname, READ_MODE) as fp: + stuff = (ext, "r", imp.PY_SOURCE) + self.load_module(name, fp, pathname, stuff) def import_hook(self, name, caller=None, fromlist=None, level=-1): self.msg(3, "import_hook", name, caller, fromlist, level) @@ -461,6 +461,8 @@ fp, buf, stuff = self.find_module("__init__", m.__path__) self.load_module(fqname, fp, buf, stuff) self.msgout(2, "load_package ->", m) + if fp: + fp.close() return m def add_module(self, fqname): -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 11 03:41:02 2015 From: python-checkins at python.org (steve.dower) Date: Wed, 11 Mar 2015 02:41:02 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Removes_unused?= =?utf-8?q?_format_string_insertion_in_launcher=2Ec=2E?= Message-ID: <20150311024102.16661.72236@psf.io> https://hg.python.org/cpython/rev/e1ec248d585f changeset: 94941:e1ec248d585f branch: 3.4 parent: 94937:f9afa4f87570 user: Steve Dower date: Tue Mar 10 19:38:25 2015 -0700 summary: Removes unused format string insertion in launcher.c. files: PC/launcher.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/PC/launcher.c b/PC/launcher.c --- a/PC/launcher.c +++ b/PC/launcher.c @@ -260,7 +260,7 @@ } else if (find_existing_python(ip->executable)) { debug(L"locate_pythons_for_key: %s: already \ -found: %s\n", ip->executable); +found\n", ip->executable); } else { /* check the executable type. */ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 11 03:41:04 2015 From: python-checkins at python.org (steve.dower) Date: Wed, 11 Mar 2015 02:41:04 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Removes_unused_format_string_insertion_in_launcher=2Ec?= =?utf-8?q?=2E?= Message-ID: <20150311024103.1317.92566@psf.io> https://hg.python.org/cpython/rev/6c6c873c0059 changeset: 94942:6c6c873c0059 parent: 94938:9994e0172a0c parent: 94941:e1ec248d585f user: Steve Dower date: Tue Mar 10 19:40:37 2015 -0700 summary: Removes unused format string insertion in launcher.c. files: PC/launcher.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/PC/launcher.c b/PC/launcher.c --- a/PC/launcher.c +++ b/PC/launcher.c @@ -268,7 +268,7 @@ } else if (find_existing_python(ip->executable)) { debug(L"locate_pythons_for_key: %ls: already \ -found: %ls\n", ip->executable); +found\n", ip->executable); } else { /* check the executable type. */ -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 11 16:17:28 2015 From: python-checkins at python.org (ethan.furman) Date: Wed, 11 Mar 2015 15:17:28 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_issue23467=3A_add_?= =?utf-8?q?=25r_compatibility_to_bytes_and_bytearray?= Message-ID: <20150311151728.9684.66274@psf.io> https://hg.python.org/cpython/rev/611fa301b807 changeset: 94943:611fa301b807 user: Ethan Furman date: Wed Mar 11 08:17:00 2015 -0700 summary: Close issue23467: add %r compatibility to bytes and bytearray files: Doc/library/stdtypes.rst | 8 +++++++- Lib/test/test_format.py | 5 +++++ Objects/bytesobject.c | 2 ++ 3 files changed, 14 insertions(+), 1 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3165,7 +3165,7 @@ +------------+-----------------------------------------------------+-------+ | ``'o'`` | Signed octal value. | \(1) | +------------+-----------------------------------------------------+-------+ -| ``'u'`` | Obsolete type -- it is identical to ``'d'``. | \(7) | +| ``'u'`` | Obsolete type -- it is identical to ``'d'``. | \(8) | +------------+-----------------------------------------------------+-------+ | ``'x'`` | Signed hexadecimal (lowercase). | \(2) | +------------+-----------------------------------------------------+-------+ @@ -3200,6 +3200,9 @@ | ``'a'`` | Bytes (converts any Python object using | \(5) | | | ``repr(obj).encode('ascii','backslashreplace)``). | | +------------+-----------------------------------------------------+-------+ +| ``'r'`` | ``'r'`` is an alias for ``'a'`` and should only | \(7) | +| | be used for Python2/3 code bases. | | ++------------+-----------------------------------------------------+-------+ | ``'%'`` | No argument is converted, results in a ``'%'`` | | | | character in the result. | | +------------+-----------------------------------------------------+-------+ @@ -3238,6 +3241,9 @@ ``b'%s'`` is deprecated, but will not be removed during the 3.x series. (7) + ``b'%r'`` is deprecated, but will not be removed during the 3.x series. + +(8) See :pep:`237`. .. note:: diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -310,6 +310,11 @@ testcommon(b"%a", b"ghi", b"b'ghi'") testcommon(b"%a", "jkl", b"'jkl'") testcommon(b"%a", "\u0544", b"'\\u0544'") + # %r is an alias for %a + testcommon(b"%r", 3.14, b"3.14") + testcommon(b"%r", b"ghi", b"b'ghi'") + testcommon(b"%r", "jkl", b"'jkl'") + testcommon(b"%r", "\u0544", b"'\\u0544'") # Test exception for unknown format characters, etc. if verbose: diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -720,6 +720,8 @@ pbuf = "%"; len = 1; break; + case 'r': + // %r is only for 2/3 code; 3 only code should use %a case 'a': temp = PyObject_ASCII(v); if (temp == NULL) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 11 16:32:53 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 11 Mar 2015 15:32:53 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323615=3A_Modules_bz2=2C_tarfile_and_tokenize_no?= =?utf-8?q?w_can_be_reloaded_with?= Message-ID: <20150311153253.8063.38490@psf.io> https://hg.python.org/cpython/rev/6e736a57a482 changeset: 94945:6e736a57a482 parent: 94943:611fa301b807 parent: 94944:383ba3699084 user: Serhiy Storchaka date: Wed Mar 11 17:31:33 2015 +0200 summary: Issue #23615: Modules bz2, tarfile and tokenize now can be reloaded with imp.reload(). Patch by Thomas Kluyver. files: Lib/bz2.py | 3 +-- Lib/tarfile.py | 2 +- Lib/tokenize.py | 3 +-- Misc/NEWS | 3 +++ Tools/freeze/bkfile.py | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Lib/bz2.py b/Lib/bz2.py --- a/Lib/bz2.py +++ b/Lib/bz2.py @@ -9,6 +9,7 @@ __author__ = "Nadeem Vawda " +from builtins import open as _builtin_open import io import warnings @@ -27,8 +28,6 @@ _BUFFER_SIZE = 8192 -_builtin_open = open - class BZ2File(io.BufferedIOBase): diff --git a/Lib/tarfile.py b/Lib/tarfile.py --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -38,6 +38,7 @@ #--------- # Imports #--------- +from builtins import open as bltn_open import sys import os import io @@ -2433,7 +2434,6 @@ except TarError: return False -bltn_open = open open = TarFile.open diff --git a/Lib/tokenize.py b/Lib/tokenize.py --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -24,6 +24,7 @@ __credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, ' 'Skip Montanaro, Raymond Hettinger, Trent Nelson, ' 'Michael Foord') +from builtins import open as _builtin_open from codecs import lookup, BOM_UTF8 import collections from io import TextIOWrapper @@ -429,8 +430,6 @@ return default, [first, second] -_builtin_open = open - def open(filename): """Open a file in read only mode using the encoding detected by detect_encoding(). diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,9 @@ Library ------- +- Issue #23615: Modules bz2, tarfile and tokenize now can be reloaded with + imp.reload(). Patch by Thomas Kluyver. + - Issue #23605: os.walk() now calls os.scandir() instead of os.listdir(). The usage of os.scandir() reduces the number of calls to os.stat(). Initial patch written by Ben Hoyt. diff --git a/Tools/freeze/bkfile.py b/Tools/freeze/bkfile.py --- a/Tools/freeze/bkfile.py +++ b/Tools/freeze/bkfile.py @@ -1,4 +1,4 @@ -_orig_open = open +from builtins import open as _orig_open class _BkFile: def __init__(self, file, mode, bufsize): -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 11 16:32:53 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 11 Mar 2015 15:32:53 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNjE1?= =?utf-8?q?=3A_Modules_bz2=2C_tarfile_and_tokenize_now_can_be_reloaded_wit?= =?utf-8?q?h?= Message-ID: <20150311153253.11193.26871@psf.io> https://hg.python.org/cpython/rev/383ba3699084 changeset: 94944:383ba3699084 branch: 3.4 parent: 94941:e1ec248d585f user: Serhiy Storchaka date: Wed Mar 11 17:18:03 2015 +0200 summary: Issue #23615: Modules bz2, tarfile and tokenize now can be reloaded with imp.reload(). Patch by Thomas Kluyver. files: Lib/bz2.py | 3 +-- Lib/tarfile.py | 2 +- Lib/tokenize.py | 3 +-- Misc/NEWS | 3 +++ Tools/freeze/bkfile.py | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Lib/bz2.py b/Lib/bz2.py --- a/Lib/bz2.py +++ b/Lib/bz2.py @@ -9,6 +9,7 @@ __author__ = "Nadeem Vawda " +from builtins import open as _builtin_open import io import warnings @@ -27,8 +28,6 @@ _BUFFER_SIZE = 8192 -_builtin_open = open - class BZ2File(io.BufferedIOBase): diff --git a/Lib/tarfile.py b/Lib/tarfile.py --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -38,6 +38,7 @@ #--------- # Imports #--------- +from builtins import open as bltn_open import sys import os import io @@ -2421,7 +2422,6 @@ except TarError: return False -bltn_open = open open = TarFile.open diff --git a/Lib/tokenize.py b/Lib/tokenize.py --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -24,6 +24,7 @@ __credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, ' 'Skip Montanaro, Raymond Hettinger, Trent Nelson, ' 'Michael Foord') +from builtins import open as _builtin_open from codecs import lookup, BOM_UTF8 import collections from io import TextIOWrapper @@ -429,8 +430,6 @@ return default, [first, second] -_builtin_open = open - def open(filename): """Open a file in read only mode using the encoding detected by detect_encoding(). diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,9 @@ Library ------- +- Issue #23615: Modules bz2, tarfile and tokenize now can be reloaded with + imp.reload(). Patch by Thomas Kluyver. + - Issue #23476: In the ssl module, enable OpenSSL's X509_V_FLAG_TRUSTED_FIRST flag on certificate stores when it is available. diff --git a/Tools/freeze/bkfile.py b/Tools/freeze/bkfile.py --- a/Tools/freeze/bkfile.py +++ b/Tools/freeze/bkfile.py @@ -1,4 +1,4 @@ -_orig_open = open +from builtins import open as _orig_open class _BkFile: def __init__(self, file, mode, bufsize): -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 11 16:32:53 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 11 Mar 2015 15:32:53 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIzNjE1?= =?utf-8?q?=3A_Module_tarfile_is_now_can_be_reloaded_with_imp=2Ereload=28?= =?utf-8?b?KS4=?= Message-ID: <20150311153253.19083.51084@psf.io> https://hg.python.org/cpython/rev/36bd5add9732 changeset: 94946:36bd5add9732 branch: 2.7 parent: 94940:5a5d4073d46a user: Serhiy Storchaka date: Wed Mar 11 17:31:59 2015 +0200 summary: Issue #23615: Module tarfile is now can be reloaded with imp.reload(). files: Lib/tarfile.py | 2 +- Misc/NEWS | 2 ++ Tools/freeze/bkfile.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/tarfile.py b/Lib/tarfile.py --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -41,6 +41,7 @@ #--------- # Imports #--------- +from __builtin__ import open as bltn_open import sys import os import shutil @@ -2611,5 +2612,4 @@ except TarError: return False -bltn_open = open open = TarFile.open diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,8 @@ Library ------- +- Issue #23615: Module tarfile is now can be reloaded with imp.reload(). + - Issue #22853: Fixed a deadlock when use multiprocessing.Queue at import time. Patch by Florian Finkernagel and Davin Potts. diff --git a/Tools/freeze/bkfile.py b/Tools/freeze/bkfile.py --- a/Tools/freeze/bkfile.py +++ b/Tools/freeze/bkfile.py @@ -1,4 +1,4 @@ -_orig_open = open +from __builtin__ import open as _orig_open class _BkFile: def __init__(self, file, mode, bufsize): -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 11 16:43:59 2015 From: python-checkins at python.org (ethan.furman) Date: Wed, 11 Mar 2015 15:43:59 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Close_issue23486=3A_perfor?= =?utf-8?q?mance_boost_for_enum_member_lookup?= Message-ID: <20150311154346.8071.8975@psf.io> https://hg.python.org/cpython/rev/2545bfe0d273 changeset: 94947:2545bfe0d273 parent: 94945:6e736a57a482 user: Ethan Furman date: Wed Mar 11 08:43:12 2015 -0700 summary: Close issue23486: performance boost for enum member lookup files: Lib/enum.py | 11 ++++++++++- 1 files changed, 10 insertions(+), 1 deletions(-) diff --git a/Lib/enum.py b/Lib/enum.py --- a/Lib/enum.py +++ b/Lib/enum.py @@ -112,6 +112,10 @@ enum_class._member_map_ = OrderedDict() # name->value map enum_class._member_type_ = member_type + # save attributes from super classes so we know if we can take + # the shortcut of storing members in the class dict + base_attributes = {a for b in bases for a in b.__dict__} + # Reverse value->name map for hashable values. enum_class._value2member_map_ = {} @@ -165,6 +169,11 @@ else: # Aliases don't appear in member names (only in __members__). enum_class._member_names_.append(member_name) + # performance boost for any member that would not shadow + # a DynamicClassAttribute + if member_name not in base_attributes: + setattr(enum_class, member_name, enum_member) + # now add to _member_map_ enum_class._member_map_[member_name] = enum_member try: # This may fail if value is not hashable. We can't add the value @@ -468,7 +477,7 @@ m for cls in self.__class__.mro() for m in cls.__dict__ - if m[0] != '_' + if m[0] != '_' and m not in self._member_map_ ] return (['__class__', '__doc__', '__module__'] + added_behavior) -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Wed Mar 11 17:14:10 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 11 Mar 2015 17:14:10 +0100 Subject: [Python-checkins] Daily reference leaks (6c6c873c0059): sum=3 Message-ID: results for 6c6c873c0059 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/reflogpeKUFN', '--timeout', '7200', '-x', 'test_eintr'] From python-checkins at python.org Wed Mar 11 17:23:15 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 11 Mar 2015 16:23:15 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323192=3A_Fixed_generator_lambdas=2E__Patch_by_B?= =?utf-8?q?runo_Cauet=2E?= Message-ID: <20150311162315.16659.46614@psf.io> https://hg.python.org/cpython/rev/a3b889e9d3f3 changeset: 94949:a3b889e9d3f3 parent: 94947:2545bfe0d273 parent: 94948:2b4a04c3681b user: Serhiy Storchaka date: Wed Mar 11 18:22:29 2015 +0200 summary: Issue #23192: Fixed generator lambdas. Patch by Bruno Cauet. files: Lib/test/test_generators.py | 20 ++++++++++++++++++++ Misc/NEWS | 2 ++ Python/compile.c | 4 ++-- 3 files changed, 24 insertions(+), 2 deletions(-) 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 @@ -49,6 +49,26 @@ self.assertTrue(finalized) self.assertEqual(gc.garbage, old_garbage) + def test_lambda_generator(self): + # Issue #23192: Test that a lambda returning a generator behaves + # like the equivalent function + f = lambda: (yield 1) + def g(): return (yield 1) + + # test 'yield from' + f2 = lambda: (yield from g()) + def g2(): return (yield from g()) + + f3 = lambda: (yield from f()) + def g3(): return (yield from f()) + + for gen_fun in (f, g, f2, g2, f3, g3): + gen = gen_fun() + self.assertEqual(next(gen), 1) + with self.assertRaises(StopIteration) as cm: + gen.send(2) + self.assertEqual(cm.exception.value, 2) + class GeneratorTest(unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ Core and Builtins ----------------- +- Issue #23192: Fixed generator lambdas. Patch by Bruno Cauet. + - Issue #23629: Fix the default __sizeof__ implementation for variable-sized objects. diff --git a/Python/compile.c b/Python/compile.c --- a/Python/compile.c +++ b/Python/compile.c @@ -1897,12 +1897,12 @@ c->u->u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs); VISIT_IN_SCOPE(c, expr, e->v.Lambda.body); if (c->u->u_ste->ste_generator) { - ADDOP_IN_SCOPE(c, POP_TOP); + co = assemble(c, 0); } else { ADDOP_IN_SCOPE(c, RETURN_VALUE); + co = assemble(c, 1); } - co = assemble(c, 1); qualname = c->u->u_qualname; Py_INCREF(qualname); compiler_exit_scope(c); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Wed Mar 11 17:23:16 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Wed, 11 Mar 2015 16:23:16 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzMTky?= =?utf-8?q?=3A_Fixed_generator_lambdas=2E__Patch_by_Bruno_Cauet=2E?= Message-ID: <20150311162315.8093.42686@psf.io> https://hg.python.org/cpython/rev/2b4a04c3681b changeset: 94948:2b4a04c3681b branch: 3.4 parent: 94944:383ba3699084 user: Serhiy Storchaka date: Wed Mar 11 18:20:35 2015 +0200 summary: Issue #23192: Fixed generator lambdas. Patch by Bruno Cauet. files: Lib/test/test_generators.py | 20 ++++++++++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 2 ++ Python/compile.c | 4 ++-- 4 files changed, 25 insertions(+), 2 deletions(-) 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 @@ -49,6 +49,26 @@ self.assertTrue(finalized) self.assertEqual(gc.garbage, old_garbage) + def test_lambda_generator(self): + # Issue #23192: Test that a lambda returning a generator behaves + # like the equivalent function + f = lambda: (yield 1) + def g(): return (yield 1) + + # test 'yield from' + f2 = lambda: (yield from g()) + def g2(): return (yield from g()) + + f3 = lambda: (yield from f()) + def g3(): return (yield from f()) + + for gen_fun in (f, g, f2, g2, f3, g3): + gen = gen_fun() + self.assertEqual(next(gen), 1) + with self.assertRaises(StopIteration) as cm: + gen.send(2) + self.assertEqual(cm.exception.value, 2) + class ExceptionTest(unittest.TestCase): # Tests for the issue #23353: check that the currently handled exception diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -216,6 +216,7 @@ Terry Carroll Edward Catmur Lorenzo M. Catucci +Bruno Cauet Donn Cave Charles Cazabon Jes?s Cea Avi?n diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ Core and Builtins ----------------- +- Issue #23192: Fixed generator lambdas. Patch by Bruno Cauet. + - Issue #23629: Fix the default __sizeof__ implementation for variable-sized objects. diff --git a/Python/compile.c b/Python/compile.c --- a/Python/compile.c +++ b/Python/compile.c @@ -1897,12 +1897,12 @@ c->u->u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs); VISIT_IN_SCOPE(c, expr, e->v.Lambda.body); if (c->u->u_ste->ste_generator) { - ADDOP_IN_SCOPE(c, POP_TOP); + co = assemble(c, 0); } else { ADDOP_IN_SCOPE(c, RETURN_VALUE); + co = assemble(c, 1); } - co = assemble(c, 1); qualname = c->u->u_qualname; Py_INCREF(qualname); compiler_exit_scope(c); -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 04:26:28 2015 From: python-checkins at python.org (nick.coghlan) Date: Thu, 12 Mar 2015 03:26:28 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_PEP_474=3A_More_thoughts_on_o?= =?utf-8?q?verall_infrastructure_maintenance?= Message-ID: <20150312032628.30090.29181@psf.io> https://hg.python.org/peps/rev/37be0a3708c9 changeset: 5725:37be0a3708c9 user: Nick Coghlan date: Thu Mar 12 13:25:59 2015 +1000 summary: PEP 474: More thoughts on overall infrastructure maintenance files: pep-0474.txt | 57 +++++++++++++++++++++++++++++++++++++++- 1 files changed, 56 insertions(+), 1 deletions(-) diff --git a/pep-0474.txt b/pep-0474.txt --- a/pep-0474.txt +++ b/pep-0474.txt @@ -230,13 +230,68 @@ of interesting technical concerns and challenges. This section covers several of the most significant ones. +Service hosting +--------------- + +The default position of this PEP is that the new forge.python.org service +will be integrated into the existing PSF Salt infrastructure and hosted on +the PSF's Rackspace cloud infrastructure. + +However, other hosting options will also be considered, in particular, +possible deployment as a `Kubernetes `__ hosted web +service on either +`Google Container Engine `__ or +the next generation of Red Hat's +`OpenShift Online `__ service, by using either +GCEPersistentDisk or the open source +`GlusterFS distributed filesystem `__ +to hold the source code repositories. + + +Ongoing infrastructure maintenance +---------------------------------- + +Ongoing infrastructure maintenance is an area of concern within the PSF, +as we currently lack a system administrator mentorship program equivalent to +the `Fedora Infrastructure Apprentice +`__ or +`GNOME Infrastructure Apprentice `__ +programs. + +Instead, systems tend to be maintained largely by developers as a part time +activity on top of their development related contributions, rather than +seeking to recruit folks that are more interested in operations (i.e. +keeping existing systems running well) than they are in development (i.e. +making changes to the services to provide new features or a better user +experience, or to address existing issues). + +While I'd personally like to see the PSF operating such a program at some +point in the future, I don't consider setting one up to be a +feasible near term goal. However, I do consider it feasible to continue +laying the groundwork for such a program by extending the PSF's existing +usage of modern infrastructure technologies like OpenStack and Salt to +cover more services, as well as starting to explore the potential benefits of +containers and container platforms when it comes to maintaining and enhancing +PSF provided services. + +I also plan to look into the question of whether or not an open source cloud +management platform like `ManageIQ `__ may help us +bring our emerging "cloud sprawl" problem across Rackspace, Google, Amazon +and other services more under control. + + User account management ----------------------- Ideally we'd like to be able to offer a single account that spans all python.org services, including Kallithea, Roundup/Rietveld, PyPI and the back end for the new python.org site, but actually implementing that would -be a distinct infrastructure project, independent of this PEP. +be a distinct infrastructure project, independent of this PEP. (It's also +worth noting that the fine-grained control of ACLs offered by such a +capability is a prerequisite for setting up an +`effective system administrator mentorship program +`__) + For the initial rollout of forge.python.org, we will likely create yet another identity silo within the PSF infrastructure. A potentially superior -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Thu Mar 12 09:13:03 2015 From: python-checkins at python.org (victor.stinner) Date: Thu, 12 Mar 2015 08:13:03 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323605=3A_os=2Ewal?= =?utf-8?q?k=28=29_doesn=27t_need_to_call_entry=2Eis=5Fsymlink=28=29_if_fo?= =?utf-8?q?llowlinks?= Message-ID: <20150312081301.118009.50692@psf.io> https://hg.python.org/cpython/rev/1a972140ab62 changeset: 94950:1a972140ab62 user: Victor Stinner date: Thu Mar 12 09:12:48 2015 +0100 summary: Issue #23605: os.walk() doesn't need to call entry.is_symlink() if followlinks is True files: Lib/os.py | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -374,7 +374,10 @@ if is_dir: dirs.append(entry.name) + else: + nondirs.append(entry.name) + if is_dir and not followlinks: try: if entry.is_symlink(): symlinks.add(entry.name) @@ -383,8 +386,6 @@ # entry is not a symbolik link, same behaviour than # os.path.islink(). pass - else: - nondirs.append(entry.name) except OSError as error: # scandir() or iterating into scandir() iterator raised an OSError if onerror is not None: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 10:16:11 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 12 Mar 2015 09:16:11 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIyOTI4?= =?utf-8?q?=3A_Disabled_HTTP_header_injections_in_http=2Eclient=2E?= Message-ID: <20150312091611.1837.3291@psf.io> https://hg.python.org/cpython/rev/bf3e1c9b80e9 changeset: 94952:bf3e1c9b80e9 branch: 3.4 parent: 94948:2b4a04c3681b user: Serhiy Storchaka date: Thu Mar 12 11:13:36 2015 +0200 summary: Issue #22928: Disabled HTTP header injections in http.client. Original patch by Demian Brecht. files: Lib/http/client.py | 37 ++++++++++++++++++ Lib/test/test_httplib.py | 57 ++++++++++++++++++++++++++++ Misc/NEWS | 3 + 3 files changed, 97 insertions(+), 0 deletions(-) diff --git a/Lib/http/client.py b/Lib/http/client.py --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -70,6 +70,7 @@ import email.message import io import os +import re import socket import collections from urllib.parse import urlsplit @@ -217,6 +218,34 @@ _MAXLINE = 65536 _MAXHEADERS = 100 +# Header name/value ABNF (http://tools.ietf.org/html/rfc7230#section-3.2) +# +# VCHAR = %x21-7E +# obs-text = %x80-FF +# header-field = field-name ":" OWS field-value OWS +# field-name = token +# field-value = *( field-content / obs-fold ) +# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +# field-vchar = VCHAR / obs-text +# +# obs-fold = CRLF 1*( SP / HTAB ) +# ; obsolete line folding +# ; see Section 3.2.4 + +# token = 1*tchar +# +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" +# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" +# / DIGIT / ALPHA +# ; any VCHAR, except delimiters +# +# VCHAR defined in http://tools.ietf.org/html/rfc5234#appendix-B.1 + +# the patterns for both name and value are more leniant than RFC +# definitions to allow for backwards compatibility +_is_legal_header_name = re.compile(rb'[^:\s][^:\r\n]*').fullmatch +_is_illegal_header_value = re.compile(rb'\n(?![ \t])|\r(?![ \t\n])').search + class HTTPMessage(email.message.Message): # XXX The only usage of this method is in @@ -1060,12 +1089,20 @@ if hasattr(header, 'encode'): header = header.encode('ascii') + + if not _is_legal_header_name(header): + raise ValueError('Invalid header name %r' % (header,)) + values = list(values) for i, one_value in enumerate(values): if hasattr(one_value, 'encode'): values[i] = one_value.encode('latin-1') elif isinstance(one_value, int): values[i] = str(one_value).encode('ascii') + + if _is_illegal_header_value(values[i]): + raise ValueError('Invalid header value %r' % (values[i],)) + value = b'\r\n\t'.join(values) header = header + b': ' + value self._output(header) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -148,6 +148,33 @@ conn.putheader('Content-length', 42) self.assertIn(b'Content-length: 42', conn._buffer) + conn.putheader('Foo', ' bar ') + self.assertIn(b'Foo: bar ', conn._buffer) + conn.putheader('Bar', '\tbaz\t') + self.assertIn(b'Bar: \tbaz\t', conn._buffer) + conn.putheader('Authorization', 'Bearer mytoken') + self.assertIn(b'Authorization: Bearer mytoken', conn._buffer) + conn.putheader('IterHeader', 'IterA', 'IterB') + self.assertIn(b'IterHeader: IterA\r\n\tIterB', conn._buffer) + conn.putheader('LatinHeader', b'\xFF') + self.assertIn(b'LatinHeader: \xFF', conn._buffer) + conn.putheader('Utf8Header', b'\xc3\x80') + self.assertIn(b'Utf8Header: \xc3\x80', conn._buffer) + conn.putheader('C1-Control', b'next\x85line') + self.assertIn(b'C1-Control: next\x85line', conn._buffer) + conn.putheader('Embedded-Fold-Space', 'is\r\n allowed') + self.assertIn(b'Embedded-Fold-Space: is\r\n allowed', conn._buffer) + conn.putheader('Embedded-Fold-Tab', 'is\r\n\tallowed') + self.assertIn(b'Embedded-Fold-Tab: is\r\n\tallowed', conn._buffer) + conn.putheader('Key Space', 'value') + self.assertIn(b'Key Space: value', conn._buffer) + conn.putheader('KeySpace ', 'value') + self.assertIn(b'KeySpace : value', conn._buffer) + conn.putheader(b'Nonbreak\xa0Space', 'value') + self.assertIn(b'Nonbreak\xa0Space: value', conn._buffer) + conn.putheader(b'\xa0NonbreakSpace', 'value') + self.assertIn(b'\xa0NonbreakSpace: value', conn._buffer) + def test_ipv6host_header(self): # Default host header on IPv6 transaction should wrapped by [] if # its actual IPv6 address @@ -177,6 +204,36 @@ self.assertEqual(resp.getheader('First'), 'val') self.assertEqual(resp.getheader('Second'), 'val') + def test_invalid_headers(self): + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket('') + conn.putrequest('GET', '/') + + # http://tools.ietf.org/html/rfc7230#section-3.2.4, whitespace is no + # longer allowed in header names + cases = ( + (b'Invalid\r\nName', b'ValidValue'), + (b'Invalid\rName', b'ValidValue'), + (b'Invalid\nName', b'ValidValue'), + (b'\r\nInvalidName', b'ValidValue'), + (b'\rInvalidName', b'ValidValue'), + (b'\nInvalidName', b'ValidValue'), + (b' InvalidName', b'ValidValue'), + (b'\tInvalidName', b'ValidValue'), + (b'Invalid:Name', b'ValidValue'), + (b':InvalidName', b'ValidValue'), + (b'ValidName', b'Invalid\r\nValue'), + (b'ValidName', b'Invalid\rValue'), + (b'ValidName', b'Invalid\nValue'), + (b'ValidName', b'InvalidValue\r\n'), + (b'ValidName', b'InvalidValue\r'), + (b'ValidName', b'InvalidValue\n'), + ) + for name, value in cases: + with self.subTest((name, value)): + with self.assertRaisesRegex(ValueError, 'Invalid header'): + conn.putheader(name, value) + class BasicTest(TestCase): def test_status_lines(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,9 @@ Library ------- +- Issue #22928: Disabled HTTP header injections in http.client. + Original patch by Demian Brecht. + - Issue #23615: Modules bz2, tarfile and tokenize now can be reloaded with imp.reload(). Patch by Thomas Kluyver. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 10:16:11 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 12 Mar 2015 09:16:11 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIyOTI4?= =?utf-8?q?=3A_Disabled_HTTP_header_injections_in_httplib=2E?= Message-ID: <20150312091610.70298.9539@psf.io> https://hg.python.org/cpython/rev/1c45047c5102 changeset: 94951:1c45047c5102 branch: 2.7 parent: 94946:36bd5add9732 user: Serhiy Storchaka date: Thu Mar 12 11:12:51 2015 +0200 summary: Issue #22928: Disabled HTTP header injections in httplib. Original patch by Demian Brecht. files: Lib/httplib.py | 40 +++++++++++++++++++- Lib/test/test_httplib.py | 56 ++++++++++++++++++++++++++++ Misc/NEWS | 3 + 3 files changed, 98 insertions(+), 1 deletions(-) diff --git a/Lib/httplib.py b/Lib/httplib.py --- a/Lib/httplib.py +++ b/Lib/httplib.py @@ -68,6 +68,7 @@ from array import array import os +import re import socket from sys import py3kwarning from urlparse import urlsplit @@ -218,6 +219,34 @@ # maximum amount of headers accepted _MAXHEADERS = 100 +# Header name/value ABNF (http://tools.ietf.org/html/rfc7230#section-3.2) +# +# VCHAR = %x21-7E +# obs-text = %x80-FF +# header-field = field-name ":" OWS field-value OWS +# field-name = token +# field-value = *( field-content / obs-fold ) +# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +# field-vchar = VCHAR / obs-text +# +# obs-fold = CRLF 1*( SP / HTAB ) +# ; obsolete line folding +# ; see Section 3.2.4 + +# token = 1*tchar +# +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" +# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" +# / DIGIT / ALPHA +# ; any VCHAR, except delimiters +# +# VCHAR defined in http://tools.ietf.org/html/rfc5234#appendix-B.1 + +# the patterns for both name and value are more leniant than RFC +# definitions to allow for backwards compatibility +_is_legal_header_name = re.compile(r'\A[^:\s][^:\r\n]*\Z').match +_is_illegal_header_value = re.compile(r'\n(?![ \t])|\r(?![ \t\n])').search + class HTTPMessage(mimetools.Message): @@ -983,7 +1012,16 @@ if self.__state != _CS_REQ_STARTED: raise CannotSendHeader() - hdr = '%s: %s' % (header, '\r\n\t'.join([str(v) for v in values])) + header = '%s' % header + if not _is_legal_header_name(header): + raise ValueError('Invalid header name %r' % (header,)) + + values = [str(v) for v in values] + for one_value in values: + if _is_illegal_header_value(one_value): + raise ValueError('Invalid header value %r' % (one_value,)) + + hdr = '%s: %s' % (header, '\r\n\t'.join(values)) self._output(hdr) def endheaders(self, message_body=None): diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -145,6 +145,33 @@ conn.putheader('Content-length',42) self.assertIn('Content-length: 42', conn._buffer) + conn.putheader('Foo', ' bar ') + self.assertIn(b'Foo: bar ', conn._buffer) + conn.putheader('Bar', '\tbaz\t') + self.assertIn(b'Bar: \tbaz\t', conn._buffer) + conn.putheader('Authorization', 'Bearer mytoken') + self.assertIn(b'Authorization: Bearer mytoken', conn._buffer) + conn.putheader('IterHeader', 'IterA', 'IterB') + self.assertIn(b'IterHeader: IterA\r\n\tIterB', conn._buffer) + conn.putheader('LatinHeader', b'\xFF') + self.assertIn(b'LatinHeader: \xFF', conn._buffer) + conn.putheader('Utf8Header', b'\xc3\x80') + self.assertIn(b'Utf8Header: \xc3\x80', conn._buffer) + conn.putheader('C1-Control', b'next\x85line') + self.assertIn(b'C1-Control: next\x85line', conn._buffer) + conn.putheader('Embedded-Fold-Space', 'is\r\n allowed') + self.assertIn(b'Embedded-Fold-Space: is\r\n allowed', conn._buffer) + conn.putheader('Embedded-Fold-Tab', 'is\r\n\tallowed') + self.assertIn(b'Embedded-Fold-Tab: is\r\n\tallowed', conn._buffer) + conn.putheader('Key Space', 'value') + self.assertIn(b'Key Space: value', conn._buffer) + conn.putheader('KeySpace ', 'value') + self.assertIn(b'KeySpace : value', conn._buffer) + conn.putheader(b'Nonbreak\xa0Space', 'value') + self.assertIn(b'Nonbreak\xa0Space: value', conn._buffer) + conn.putheader(b'\xa0NonbreakSpace', 'value') + self.assertIn(b'\xa0NonbreakSpace: value', conn._buffer) + def test_ipv6host_header(self): # Default host header on IPv6 transaction should wrapped by [] if # its actual IPv6 address @@ -174,6 +201,35 @@ self.assertEqual(resp.getheader('First'), 'val') self.assertEqual(resp.getheader('Second'), 'val') + def test_invalid_headers(self): + conn = httplib.HTTPConnection('example.com') + conn.sock = FakeSocket('') + conn.putrequest('GET', '/') + + # http://tools.ietf.org/html/rfc7230#section-3.2.4, whitespace is no + # longer allowed in header names + cases = ( + (b'Invalid\r\nName', b'ValidValue'), + (b'Invalid\rName', b'ValidValue'), + (b'Invalid\nName', b'ValidValue'), + (b'\r\nInvalidName', b'ValidValue'), + (b'\rInvalidName', b'ValidValue'), + (b'\nInvalidName', b'ValidValue'), + (b' InvalidName', b'ValidValue'), + (b'\tInvalidName', b'ValidValue'), + (b'Invalid:Name', b'ValidValue'), + (b':InvalidName', b'ValidValue'), + (b'ValidName', b'Invalid\r\nValue'), + (b'ValidName', b'Invalid\rValue'), + (b'ValidName', b'Invalid\nValue'), + (b'ValidName', b'InvalidValue\r\n'), + (b'ValidName', b'InvalidValue\r'), + (b'ValidName', b'InvalidValue\n'), + ) + for name, value in cases: + with self.assertRaisesRegexp(ValueError, 'Invalid header'): + conn.putheader(name, value) + class BasicTest(TestCase): def test_status_lines(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,9 @@ Library ------- +- Issue #22928: Disabled HTTP header injections in httplib. + Original patch by Demian Brecht. + - Issue #23615: Module tarfile is now can be reloaded with imp.reload(). - Issue #22853: Fixed a deadlock when use multiprocessing.Queue at import time. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 10:16:11 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 12 Mar 2015 09:16:11 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2322928=3A_Disabled_HTTP_header_injections_in_htt?= =?utf-8?b?cC5jbGllbnQu?= Message-ID: <20150312091611.118025.32425@psf.io> https://hg.python.org/cpython/rev/aa4c6992c126 changeset: 94953:aa4c6992c126 parent: 94950:1a972140ab62 parent: 94952:bf3e1c9b80e9 user: Serhiy Storchaka date: Thu Mar 12 11:15:15 2015 +0200 summary: Issue #22928: Disabled HTTP header injections in http.client. Original patch by Demian Brecht. files: Lib/http/client.py | 37 ++++++++++++++++++ Lib/test/test_httplib.py | 57 ++++++++++++++++++++++++++++ Misc/NEWS | 3 + 3 files changed, 97 insertions(+), 0 deletions(-) diff --git a/Lib/http/client.py b/Lib/http/client.py --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -71,6 +71,7 @@ import http import io import os +import re import socket import collections from urllib.parse import urlsplit @@ -109,6 +110,34 @@ _MAXLINE = 65536 _MAXHEADERS = 100 +# Header name/value ABNF (http://tools.ietf.org/html/rfc7230#section-3.2) +# +# VCHAR = %x21-7E +# obs-text = %x80-FF +# header-field = field-name ":" OWS field-value OWS +# field-name = token +# field-value = *( field-content / obs-fold ) +# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +# field-vchar = VCHAR / obs-text +# +# obs-fold = CRLF 1*( SP / HTAB ) +# ; obsolete line folding +# ; see Section 3.2.4 + +# token = 1*tchar +# +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" +# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" +# / DIGIT / ALPHA +# ; any VCHAR, except delimiters +# +# VCHAR defined in http://tools.ietf.org/html/rfc5234#appendix-B.1 + +# the patterns for both name and value are more leniant than RFC +# definitions to allow for backwards compatibility +_is_legal_header_name = re.compile(rb'[^:\s][^:\r\n]*').fullmatch +_is_illegal_header_value = re.compile(rb'\n(?![ \t])|\r(?![ \t\n])').search + class HTTPMessage(email.message.Message): # XXX The only usage of this method is in @@ -1002,12 +1031,20 @@ if hasattr(header, 'encode'): header = header.encode('ascii') + + if not _is_legal_header_name(header): + raise ValueError('Invalid header name %r' % (header,)) + values = list(values) for i, one_value in enumerate(values): if hasattr(one_value, 'encode'): values[i] = one_value.encode('latin-1') elif isinstance(one_value, int): values[i] = str(one_value).encode('ascii') + + if _is_illegal_header_value(values[i]): + raise ValueError('Invalid header value %r' % (values[i],)) + value = b'\r\n\t'.join(values) header = header + b': ' + value self._output(header) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -171,6 +171,33 @@ conn.putheader('Content-length', 42) self.assertIn(b'Content-length: 42', conn._buffer) + conn.putheader('Foo', ' bar ') + self.assertIn(b'Foo: bar ', conn._buffer) + conn.putheader('Bar', '\tbaz\t') + self.assertIn(b'Bar: \tbaz\t', conn._buffer) + conn.putheader('Authorization', 'Bearer mytoken') + self.assertIn(b'Authorization: Bearer mytoken', conn._buffer) + conn.putheader('IterHeader', 'IterA', 'IterB') + self.assertIn(b'IterHeader: IterA\r\n\tIterB', conn._buffer) + conn.putheader('LatinHeader', b'\xFF') + self.assertIn(b'LatinHeader: \xFF', conn._buffer) + conn.putheader('Utf8Header', b'\xc3\x80') + self.assertIn(b'Utf8Header: \xc3\x80', conn._buffer) + conn.putheader('C1-Control', b'next\x85line') + self.assertIn(b'C1-Control: next\x85line', conn._buffer) + conn.putheader('Embedded-Fold-Space', 'is\r\n allowed') + self.assertIn(b'Embedded-Fold-Space: is\r\n allowed', conn._buffer) + conn.putheader('Embedded-Fold-Tab', 'is\r\n\tallowed') + self.assertIn(b'Embedded-Fold-Tab: is\r\n\tallowed', conn._buffer) + conn.putheader('Key Space', 'value') + self.assertIn(b'Key Space: value', conn._buffer) + conn.putheader('KeySpace ', 'value') + self.assertIn(b'KeySpace : value', conn._buffer) + conn.putheader(b'Nonbreak\xa0Space', 'value') + self.assertIn(b'Nonbreak\xa0Space: value', conn._buffer) + conn.putheader(b'\xa0NonbreakSpace', 'value') + self.assertIn(b'\xa0NonbreakSpace: value', conn._buffer) + def test_ipv6host_header(self): # Default host header on IPv6 transaction should wrapped by [] if # its actual IPv6 address @@ -200,6 +227,36 @@ self.assertEqual(resp.getheader('First'), 'val') self.assertEqual(resp.getheader('Second'), 'val') + def test_invalid_headers(self): + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket('') + conn.putrequest('GET', '/') + + # http://tools.ietf.org/html/rfc7230#section-3.2.4, whitespace is no + # longer allowed in header names + cases = ( + (b'Invalid\r\nName', b'ValidValue'), + (b'Invalid\rName', b'ValidValue'), + (b'Invalid\nName', b'ValidValue'), + (b'\r\nInvalidName', b'ValidValue'), + (b'\rInvalidName', b'ValidValue'), + (b'\nInvalidName', b'ValidValue'), + (b' InvalidName', b'ValidValue'), + (b'\tInvalidName', b'ValidValue'), + (b'Invalid:Name', b'ValidValue'), + (b':InvalidName', b'ValidValue'), + (b'ValidName', b'Invalid\r\nValue'), + (b'ValidName', b'Invalid\rValue'), + (b'ValidName', b'Invalid\nValue'), + (b'ValidName', b'InvalidValue\r\n'), + (b'ValidName', b'InvalidValue\r'), + (b'ValidName', b'InvalidValue\n'), + ) + for name, value in cases: + with self.subTest((name, value)): + with self.assertRaisesRegex(ValueError, 'Invalid header'): + conn.putheader(name, value) + class BasicTest(TestCase): def test_status_lines(self): diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,9 @@ Library ------- +- Issue #22928: Disabled HTTP header injections in http.client. + Original patch by Demian Brecht. + - Issue #23615: Modules bz2, tarfile and tokenize now can be reloaded with imp.reload(). Patch by Thomas Kluyver. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 10:32:33 2015 From: python-checkins at python.org (victor.stinner) Date: Thu, 12 Mar 2015 09:32:33 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogTWVyZ2UgMy40ICh0ZXN0X29zKQ==?= Message-ID: <20150312093232.27349.63735@psf.io> https://hg.python.org/cpython/rev/c7a700132018 changeset: 94955:c7a700132018 parent: 94953:aa4c6992c126 parent: 94954:c06ebb57d4ed user: Victor Stinner date: Thu Mar 12 10:32:20 2015 +0100 summary: Merge 3.4 (test_os) files: Lib/test/test_os.py | 113 ++++++++++++++++++++----------- 1 files changed, 73 insertions(+), 40 deletions(-) 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 @@ -763,9 +763,17 @@ class WalkTests(unittest.TestCase): """Tests for os.walk().""" + # Wrapper to hide minor differences between os.walk and os.fwalk + # to tests both functions with the same code base + def walk(self, directory, topdown=True, follow_symlinks=False): + walk_it = os.walk(directory, + topdown=topdown, + followlinks=follow_symlinks) + for root, dirs, files in walk_it: + yield (root, dirs, files) + def setUp(self): - import os - from os.path import join + join = os.path.join # Build: # TESTFN/ @@ -780,36 +788,39 @@ # broken_link # TEST2/ # tmp4 a lone file - walk_path = join(support.TESTFN, "TEST1") - sub1_path = join(walk_path, "SUB1") - sub11_path = join(sub1_path, "SUB11") - sub2_path = join(walk_path, "SUB2") - tmp1_path = join(walk_path, "tmp1") - tmp2_path = join(sub1_path, "tmp2") + self.walk_path = join(support.TESTFN, "TEST1") + self.sub1_path = join(self.walk_path, "SUB1") + self.sub11_path = join(self.sub1_path, "SUB11") + sub2_path = join(self.walk_path, "SUB2") + tmp1_path = join(self.walk_path, "tmp1") + tmp2_path = join(self.sub1_path, "tmp2") tmp3_path = join(sub2_path, "tmp3") - link_path = join(sub2_path, "link") + self.link_path = join(sub2_path, "link") t2_path = join(support.TESTFN, "TEST2") tmp4_path = join(support.TESTFN, "TEST2", "tmp4") - link_path = join(sub2_path, "link") broken_link_path = join(sub2_path, "broken_link") # Create stuff. - os.makedirs(sub11_path) + os.makedirs(self.sub11_path) os.makedirs(sub2_path) os.makedirs(t2_path) + for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path: f = open(path, "w") f.write("I'm " + path + " and proud of it. Blame test_os.\n") f.close() + if support.can_symlink(): - os.symlink(os.path.abspath(t2_path), link_path) + os.symlink(os.path.abspath(t2_path), self.link_path) os.symlink('broken', broken_link_path, True) - sub2_tree = (sub2_path, ["link"], ["broken_link", "tmp3"]) + self.sub2_tree = (sub2_path, ["link"], ["broken_link", "tmp3"]) else: - sub2_tree = (sub2_path, [], ["tmp3"]) - + self.sub2_tree = (sub2_path, [], ["tmp3"]) + + def test_walk_topdown(self): # Walk top-down. - all = list(os.walk(walk_path)) + all = list(os.walk(self.walk_path)) + self.assertEqual(len(all), 4) # We can't know which order SUB1 and SUB2 will appear in. # Not flipped: TESTFN, SUB1, SUB11, SUB2 @@ -817,26 +828,32 @@ flipped = all[0][1][0] != "SUB1" all[0][1].sort() all[3 - 2 * flipped][-1].sort() - self.assertEqual(all[0], (walk_path, ["SUB1", "SUB2"], ["tmp1"])) - self.assertEqual(all[1 + flipped], (sub1_path, ["SUB11"], ["tmp2"])) - self.assertEqual(all[2 + flipped], (sub11_path, [], [])) - self.assertEqual(all[3 - 2 * flipped], sub2_tree) - + self.assertEqual(all[0], (self.walk_path, ["SUB1", "SUB2"], ["tmp1"])) + self.assertEqual(all[1 + flipped], (self.sub1_path, ["SUB11"], ["tmp2"])) + self.assertEqual(all[2 + flipped], (self.sub11_path, [], [])) + self.assertEqual(all[3 - 2 * flipped], self.sub2_tree) + + def test_walk_prune(self): # Prune the search. all = [] - for root, dirs, files in os.walk(walk_path): + for root, dirs, files in self.walk(self.walk_path): all.append((root, dirs, files)) # Don't descend into SUB1. if 'SUB1' in dirs: # Note that this also mutates the dirs we appended to all! dirs.remove('SUB1') + self.assertEqual(len(all), 2) - self.assertEqual(all[0], (walk_path, ["SUB2"], ["tmp1"])) + self.assertEqual(all[0], + (self.walk_path, ["SUB2"], ["tmp1"])) + all[1][-1].sort() - self.assertEqual(all[1], sub2_tree) - + self.assertEqual(all[1], self.sub2_tree) + + def test_walk_bottom_up(self): # Walk bottom-up. - all = list(os.walk(walk_path, topdown=False)) + all = list(self.walk(self.walk_path, topdown=False)) + self.assertEqual(len(all), 4) # We can't know which order SUB1 and SUB2 will appear in. # Not flipped: SUB11, SUB1, SUB2, TESTFN @@ -844,20 +861,28 @@ flipped = all[3][1][0] != "SUB1" all[3][1].sort() all[2 - 2 * flipped][-1].sort() - self.assertEqual(all[3], (walk_path, ["SUB1", "SUB2"], ["tmp1"])) - self.assertEqual(all[flipped], (sub11_path, [], [])) - self.assertEqual(all[flipped + 1], (sub1_path, ["SUB11"], ["tmp2"])) - self.assertEqual(all[2 - 2 * flipped], sub2_tree) - - if support.can_symlink(): - # Walk, following symlinks. - for root, dirs, files in os.walk(walk_path, followlinks=True): - if root == link_path: - self.assertEqual(dirs, []) - self.assertEqual(files, ["tmp4"]) - break - else: - self.fail("Didn't follow symlink with followlinks=True") + self.assertEqual(all[3], + (self.walk_path, ["SUB1", "SUB2"], ["tmp1"])) + self.assertEqual(all[flipped], + (self.sub11_path, [], [])) + self.assertEqual(all[flipped + 1], + (self.sub1_path, ["SUB11"], ["tmp2"])) + self.assertEqual(all[2 - 2 * flipped], + self.sub2_tree) + + def test_walk_symlink(self): + if not support.can_symlink(): + self.skipTest("need symlink support") + + # Walk, following symlinks. + walk_it = self.walk(self.walk_path, follow_symlinks=True) + for root, dirs, files in walk_it: + if root == self.link_path: + self.assertEqual(dirs, []) + self.assertEqual(files, ["tmp4"]) + break + else: + self.fail("Didn't follow symlink with followlinks=True") def tearDown(self): # Tear everything down. This is a decent use for bottom-up on @@ -880,6 +905,14 @@ class FwalkTests(WalkTests): """Tests for os.fwalk().""" + def walk(self, directory, topdown=True, follow_symlinks=False): + walk_it = os.fwalk(directory, + topdown=topdown, + follow_symlinks=follow_symlinks) + for root, dirs, files, root_fd in walk_it: + yield (root, dirs, files) + + def _compare_to_walk(self, walk_kwargs, fwalk_kwargs): """ compare with walk() results. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 10:32:34 2015 From: python-checkins at python.org (victor.stinner) Date: Thu, 12 Mar 2015 09:32:34 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNjA1?= =?utf-8?q?=3A_Refactor_os=2Ewalk=28=29_tests_to_also_run_them_on_os=2Efwa?= =?utf-8?q?lk=28=29?= Message-ID: <20150312093232.7298.94093@psf.io> https://hg.python.org/cpython/rev/c06ebb57d4ed changeset: 94954:c06ebb57d4ed branch: 3.4 parent: 94952:bf3e1c9b80e9 user: Victor Stinner date: Thu Mar 12 10:28:24 2015 +0100 summary: Issue #23605: Refactor os.walk() tests to also run them on os.fwalk() files: Lib/test/test_os.py | 105 +++++++++++++++++++++---------- 1 files changed, 69 insertions(+), 36 deletions(-) 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 @@ -694,9 +694,17 @@ class WalkTests(unittest.TestCase): """Tests for os.walk().""" + # Wrapper to hide minor differences between os.walk and os.fwalk + # to tests both functions with the same code base + def walk(self, directory, topdown=True, follow_symlinks=False): + walk_it = os.walk(directory, + topdown=topdown, + followlinks=follow_symlinks) + for root, dirs, files in walk_it: + yield (root, dirs, files) + def setUp(self): - import os - from os.path import join + join = os.path.join # Build: # TESTFN/ @@ -711,36 +719,39 @@ # broken_link # TEST2/ # tmp4 a lone file - walk_path = join(support.TESTFN, "TEST1") - sub1_path = join(walk_path, "SUB1") - sub11_path = join(sub1_path, "SUB11") - sub2_path = join(walk_path, "SUB2") - tmp1_path = join(walk_path, "tmp1") - tmp2_path = join(sub1_path, "tmp2") + self.walk_path = join(support.TESTFN, "TEST1") + self.sub1_path = join(self.walk_path, "SUB1") + self.sub11_path = join(self.sub1_path, "SUB11") + sub2_path = join(self.walk_path, "SUB2") + tmp1_path = join(self.walk_path, "tmp1") + tmp2_path = join(self.sub1_path, "tmp2") tmp3_path = join(sub2_path, "tmp3") - link_path = join(sub2_path, "link") + self.link_path = join(sub2_path, "link") t2_path = join(support.TESTFN, "TEST2") tmp4_path = join(support.TESTFN, "TEST2", "tmp4") - link_path = join(sub2_path, "link") broken_link_path = join(sub2_path, "broken_link") # Create stuff. - os.makedirs(sub11_path) + os.makedirs(self.sub11_path) os.makedirs(sub2_path) os.makedirs(t2_path) + for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path: f = open(path, "w") f.write("I'm " + path + " and proud of it. Blame test_os.\n") f.close() + if support.can_symlink(): - os.symlink(os.path.abspath(t2_path), link_path) + os.symlink(os.path.abspath(t2_path), self.link_path) os.symlink('broken', broken_link_path, True) - sub2_tree = (sub2_path, ["link"], ["broken_link", "tmp3"]) + self.sub2_tree = (sub2_path, ["link"], ["broken_link", "tmp3"]) else: - sub2_tree = (sub2_path, [], ["tmp3"]) + self.sub2_tree = (sub2_path, [], ["tmp3"]) + def test_walk_topdown(self): # Walk top-down. - all = list(os.walk(walk_path)) + all = list(os.walk(self.walk_path)) + self.assertEqual(len(all), 4) # We can't know which order SUB1 and SUB2 will appear in. # Not flipped: TESTFN, SUB1, SUB11, SUB2 @@ -748,26 +759,32 @@ flipped = all[0][1][0] != "SUB1" all[0][1].sort() all[3 - 2 * flipped][-1].sort() - self.assertEqual(all[0], (walk_path, ["SUB1", "SUB2"], ["tmp1"])) - self.assertEqual(all[1 + flipped], (sub1_path, ["SUB11"], ["tmp2"])) - self.assertEqual(all[2 + flipped], (sub11_path, [], [])) - self.assertEqual(all[3 - 2 * flipped], sub2_tree) + self.assertEqual(all[0], (self.walk_path, ["SUB1", "SUB2"], ["tmp1"])) + self.assertEqual(all[1 + flipped], (self.sub1_path, ["SUB11"], ["tmp2"])) + self.assertEqual(all[2 + flipped], (self.sub11_path, [], [])) + self.assertEqual(all[3 - 2 * flipped], self.sub2_tree) + def test_walk_prune(self): # Prune the search. all = [] - for root, dirs, files in os.walk(walk_path): + for root, dirs, files in self.walk(self.walk_path): all.append((root, dirs, files)) # Don't descend into SUB1. if 'SUB1' in dirs: # Note that this also mutates the dirs we appended to all! dirs.remove('SUB1') + self.assertEqual(len(all), 2) - self.assertEqual(all[0], (walk_path, ["SUB2"], ["tmp1"])) + self.assertEqual(all[0], + (self.walk_path, ["SUB2"], ["tmp1"])) + all[1][-1].sort() - self.assertEqual(all[1], sub2_tree) + self.assertEqual(all[1], self.sub2_tree) + def test_walk_bottom_up(self): # Walk bottom-up. - all = list(os.walk(walk_path, topdown=False)) + all = list(self.walk(self.walk_path, topdown=False)) + self.assertEqual(len(all), 4) # We can't know which order SUB1 and SUB2 will appear in. # Not flipped: SUB11, SUB1, SUB2, TESTFN @@ -775,20 +792,28 @@ flipped = all[3][1][0] != "SUB1" all[3][1].sort() all[2 - 2 * flipped][-1].sort() - self.assertEqual(all[3], (walk_path, ["SUB1", "SUB2"], ["tmp1"])) - self.assertEqual(all[flipped], (sub11_path, [], [])) - self.assertEqual(all[flipped + 1], (sub1_path, ["SUB11"], ["tmp2"])) - self.assertEqual(all[2 - 2 * flipped], sub2_tree) + self.assertEqual(all[3], + (self.walk_path, ["SUB1", "SUB2"], ["tmp1"])) + self.assertEqual(all[flipped], + (self.sub11_path, [], [])) + self.assertEqual(all[flipped + 1], + (self.sub1_path, ["SUB11"], ["tmp2"])) + self.assertEqual(all[2 - 2 * flipped], + self.sub2_tree) - if support.can_symlink(): - # Walk, following symlinks. - for root, dirs, files in os.walk(walk_path, followlinks=True): - if root == link_path: - self.assertEqual(dirs, []) - self.assertEqual(files, ["tmp4"]) - break - else: - self.fail("Didn't follow symlink with followlinks=True") + def test_walk_symlink(self): + if not support.can_symlink(): + self.skipTest("need symlink support") + + # Walk, following symlinks. + walk_it = self.walk(self.walk_path, follow_symlinks=True) + for root, dirs, files in walk_it: + if root == self.link_path: + self.assertEqual(dirs, []) + self.assertEqual(files, ["tmp4"]) + break + else: + self.fail("Didn't follow symlink with followlinks=True") def tearDown(self): # Tear everything down. This is a decent use for bottom-up on @@ -811,6 +836,14 @@ class FwalkTests(WalkTests): """Tests for os.fwalk().""" + def walk(self, directory, topdown=True, follow_symlinks=False): + walk_it = os.fwalk(directory, + topdown=topdown, + follow_symlinks=follow_symlinks) + for root, dirs, files, root_fd in walk_it: + yield (root, dirs, files) + + def _compare_to_walk(self, walk_kwargs, fwalk_kwargs): """ compare with walk() results. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 15:33:19 2015 From: python-checkins at python.org (victor.stinner) Date: Thu, 12 Mar 2015 14:33:19 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323566=3A_enable?= =?utf-8?b?KCksIHJlZ2lzdGVyKCksIGR1bXBfdHJhY2ViYWNrKCkgYW5kIGR1bXBfdHJh?= =?utf-8?q?ceback=5Flater=28=29?= Message-ID: <20150312143319.70288.35090@psf.io> https://hg.python.org/cpython/rev/e78de5b1e3ef changeset: 94956:e78de5b1e3ef user: Victor Stinner date: Thu Mar 12 15:32:03 2015 +0100 summary: Issue #23566: enable(), register(), dump_traceback() and dump_traceback_later() functions of faulthandler now accept file descriptors. Patch by Wei Wu. files: Doc/library/faulthandler.rst | 24 +++ Doc/whatsnew/3.5.rst | 8 + Lib/test/test_faulthandler.py | 158 ++++++++++++++------- Misc/NEWS | 4 + Modules/faulthandler.c | 56 ++++-- 5 files changed, 172 insertions(+), 78 deletions(-) diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst --- a/Doc/library/faulthandler.rst +++ b/Doc/library/faulthandler.rst @@ -47,6 +47,9 @@ Dump the tracebacks of all threads into *file*. If *all_threads* is ``False``, dump only the current thread. + .. versionchanged:: 3.5 + Added support for passing file descriptor to this function. + Fault handler state ------------------- @@ -59,6 +62,12 @@ produce tracebacks for every running thread. Otherwise, dump only the current thread. + The *file* must be kept open until the fault handler is disabled: see + :ref:`issue with file descriptors `. + + .. versionchanged:: 3.5 + Added support for passing file descriptor to this function. + .. function:: disable() Disable the fault handler: uninstall the signal handlers installed by @@ -82,9 +91,16 @@ call replaces previous parameters and resets the timeout. The timer has a sub-second resolution. + The *file* must be kept open until the traceback is dumped or + :func:`cancel_dump_traceback_later` is called: see :ref:`issue with file + descriptors `. + This function is implemented using a watchdog thread and therefore is not available if Python is compiled with threads disabled. + .. versionchanged:: 3.5 + Added support for passing file descriptor to this function. + .. function:: cancel_dump_traceback_later() Cancel the last call to :func:`dump_traceback_later`. @@ -99,8 +115,14 @@ the traceback of all threads, or of the current thread if *all_threads* is ``False``, into *file*. Call the previous handler if chain is ``True``. + The *file* must be kept open until the signal is unregistered by + :func:`unregister`: see :ref:`issue with file descriptors `. + Not available on Windows. + .. versionchanged:: 3.5 + Added support for passing file descriptor to this function. + .. function:: unregister(signum) Unregister a user signal: uninstall the handler of the *signum* signal @@ -110,6 +132,8 @@ Not available on Windows. +.. _faulthandler-fd: + Issue with file descriptors --------------------------- 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 @@ -417,6 +417,14 @@ * :class:`xmlrpc.client.ServerProxy` is now a :term:`context manager`. (Contributed by Claudiu Popa in :issue:`20627`.) +faulthandler +------------ + +* :func:`~faulthandler.enable`, :func:`~faulthandler.register`, + :func:`~faulthandler.dump_traceback` and + :func:`~faulthandler.dump_traceback_later` functions now accept file + descriptors. (Contributed by Wei Wu in :issue:`23566`.) + Optimizations ============= diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -42,7 +42,7 @@ support.unlink(filename) class FaultHandlerTests(unittest.TestCase): - def get_output(self, code, filename=None): + def get_output(self, code, filename=None, fd=None): """ Run the specified code in Python (in a new child process) and read the output from the standard error or from a file (if filename is set). @@ -53,8 +53,11 @@ thread XXX". """ code = dedent(code).strip() + pass_fds = [] + if fd is not None: + pass_fds.append(fd) with support.SuppressCrashReport(): - process = script_helper.spawn_python('-c', code) + process = script_helper.spawn_python('-c', code, pass_fds=pass_fds) stdout, stderr = process.communicate() exitcode = process.wait() output = support.strip_python_stderr(stdout) @@ -64,13 +67,20 @@ with open(filename, "rb") as fp: output = fp.read() output = output.decode('ascii', 'backslashreplace') + elif fd is not None: + self.assertEqual(output, '') + os.lseek(fd, os.SEEK_SET, 0) + with open(fd, "rb", closefd=False) as fp: + output = fp.read() + output = output.decode('ascii', 'backslashreplace') output = re.sub('Current thread 0x[0-9a-f]+', 'Current thread XXX', output) return output.splitlines(), exitcode def check_fatal_error(self, code, line_number, name_regex, - filename=None, all_threads=True, other_regex=None): + filename=None, all_threads=True, other_regex=None, + fd=None): """ Check that the fault handler for fatal errors is enabled and check the traceback from the child process output. @@ -93,7 +103,7 @@ header=re.escape(header))).strip() if other_regex: regex += '|' + other_regex - output, exitcode = self.get_output(code, filename) + output, exitcode = self.get_output(code, filename=filename, fd=fd) output = '\n'.join(output) self.assertRegex(output, regex) self.assertNotEqual(exitcode, 0) @@ -211,6 +221,19 @@ 'Segmentation fault', filename=filename) + def test_enable_fd(self): + with tempfile.TemporaryFile('wb+') as fp: + fd = fp.fileno() + self.check_fatal_error(""" + import faulthandler + import sys + faulthandler.enable(%s) + faulthandler._sigsegv() + """ % fd, + 4, + 'Segmentation fault', + fd=fd) + def test_enable_single_thread(self): self.check_fatal_error(""" import faulthandler @@ -297,7 +320,7 @@ output = subprocess.check_output(args, env=env) self.assertEqual(output.rstrip(), b"True") - def check_dump_traceback(self, filename): + def check_dump_traceback(self, *, filename=None, fd=None): """ Explicitly call dump_traceback() function and check its output. Raise an error if the output doesn't match the expected format. @@ -305,10 +328,16 @@ code = """ import faulthandler + filename = {filename!r} + fd = {fd} + def funcB(): - if {has_filename}: - with open({filename}, "wb") as fp: + if filename: + with open(filename, "wb") as fp: faulthandler.dump_traceback(fp, all_threads=False) + elif fd is not None: + faulthandler.dump_traceback(fd, + all_threads=False) else: faulthandler.dump_traceback(all_threads=False) @@ -318,29 +347,35 @@ funcA() """ code = code.format( - filename=repr(filename), - has_filename=bool(filename), + filename=filename, + fd=fd, ) if filename: - lineno = 6 + lineno = 9 + elif fd is not None: + lineno = 12 else: - lineno = 8 + lineno = 14 expected = [ 'Stack (most recent call first):', ' File "", line %s in funcB' % lineno, - ' File "", line 11 in funcA', - ' File "", line 13 in ' + ' File "", line 17 in funcA', + ' File "", line 19 in ' ] - trace, exitcode = self.get_output(code, filename) + trace, exitcode = self.get_output(code, filename, fd) self.assertEqual(trace, expected) self.assertEqual(exitcode, 0) def test_dump_traceback(self): - self.check_dump_traceback(None) + self.check_dump_traceback() def test_dump_traceback_file(self): with temporary_filename() as filename: - self.check_dump_traceback(filename) + self.check_dump_traceback(filename=filename) + + def test_dump_traceback_fd(self): + with tempfile.TemporaryFile('wb+') as fp: + self.check_dump_traceback(fd=fp.fileno()) def test_truncate(self): maxlen = 500 @@ -433,7 +468,10 @@ with temporary_filename() as filename: self.check_dump_traceback_threads(filename) - def _check_dump_traceback_later(self, repeat, cancel, filename, loops): + @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'), + 'need faulthandler.dump_traceback_later()') + def check_dump_traceback_later(self, repeat=False, cancel=False, loops=1, + *, filename=None, fd=None): """ Check how many times the traceback is written in timeout x 2.5 seconds, or timeout x 3.5 seconds if cancel is True: 1, 2 or 3 times depending @@ -445,6 +483,14 @@ code = """ import faulthandler import time + import sys + + timeout = {timeout} + repeat = {repeat} + cancel = {cancel} + loops = {loops} + filename = {filename!r} + fd = {fd} def func(timeout, repeat, cancel, file, loops): for loop in range(loops): @@ -454,16 +500,14 @@ time.sleep(timeout * 5) faulthandler.cancel_dump_traceback_later() - timeout = {timeout} - repeat = {repeat} - cancel = {cancel} - loops = {loops} - if {has_filename}: - file = open({filename}, "wb") + if filename: + file = open(filename, "wb") + elif fd is not None: + file = sys.stderr.fileno() else: file = None func(timeout, repeat, cancel, file, loops) - if file is not None: + if filename: file.close() """ code = code.format( @@ -471,8 +515,8 @@ repeat=repeat, cancel=cancel, loops=loops, - has_filename=bool(filename), - filename=repr(filename), + filename=filename, + fd=fd, ) trace, exitcode = self.get_output(code, filename) trace = '\n'.join(trace) @@ -482,27 +526,12 @@ if repeat: count *= 2 header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+ \(most recent call first\):\n' % timeout_str - regex = expected_traceback(9, 20, header, min_count=count) + regex = expected_traceback(17, 26, header, min_count=count) self.assertRegex(trace, regex) else: self.assertEqual(trace, '') self.assertEqual(exitcode, 0) - @unittest.skipIf(not hasattr(faulthandler, 'dump_traceback_later'), - 'need faulthandler.dump_traceback_later()') - def check_dump_traceback_later(self, repeat=False, cancel=False, - file=False, twice=False): - if twice: - loops = 2 - else: - loops = 1 - if file: - with temporary_filename() as filename: - self._check_dump_traceback_later(repeat, cancel, - filename, loops) - else: - self._check_dump_traceback_later(repeat, cancel, None, loops) - def test_dump_traceback_later(self): self.check_dump_traceback_later() @@ -513,15 +542,20 @@ self.check_dump_traceback_later(cancel=True) def test_dump_traceback_later_file(self): - self.check_dump_traceback_later(file=True) + with temporary_filename() as filename: + self.check_dump_traceback_later(filename=filename) + + def test_dump_traceback_later_fd(self): + with tempfile.TemporaryFile('wb+') as fp: + self.check_dump_traceback_later(fd=fp.fileno()) def test_dump_traceback_later_twice(self): - self.check_dump_traceback_later(twice=True) + self.check_dump_traceback_later(loops=2) @unittest.skipIf(not hasattr(faulthandler, "register"), "need faulthandler.register") def check_register(self, filename=False, all_threads=False, - unregister=False, chain=False): + unregister=False, chain=False, fd=None): """ Register a handler displaying the traceback on a user signal. Raise the signal and check the written traceback. @@ -537,6 +571,13 @@ import signal import sys + all_threads = {all_threads} + signum = {signum} + unregister = {unregister} + chain = {chain} + filename = {filename!r} + fd = {fd} + def func(signum): os.kill(os.getpid(), signum) @@ -544,19 +585,16 @@ handler.called = True handler.called = False - exitcode = 0 - signum = {signum} - unregister = {unregister} - chain = {chain} - - if {has_filename}: - file = open({filename}, "wb") + if filename: + file = open(filename, "wb") + elif fd is not None: + file = sys.stderr.fileno() else: file = None if chain: signal.signal(signum, handler) faulthandler.register(signum, file=file, - all_threads={all_threads}, chain={chain}) + all_threads=all_threads, chain={chain}) if unregister: faulthandler.unregister(signum) func(signum) @@ -567,17 +605,19 @@ output = sys.stderr print("Error: signal handler not called!", file=output) exitcode = 1 - if file is not None: + else: + exitcode = 0 + if filename: file.close() sys.exit(exitcode) """ code = code.format( - filename=repr(filename), - has_filename=bool(filename), all_threads=all_threads, signum=signum, unregister=unregister, chain=chain, + filename=filename, + fd=fd, ) trace, exitcode = self.get_output(code, filename) trace = '\n'.join(trace) @@ -586,7 +626,7 @@ regex = 'Current thread XXX \(most recent call first\):\n' else: regex = 'Stack \(most recent call first\):\n' - regex = expected_traceback(7, 28, regex) + regex = expected_traceback(14, 32, regex) self.assertRegex(trace, regex) else: self.assertEqual(trace, '') @@ -605,6 +645,10 @@ with temporary_filename() as filename: self.check_register(filename=filename) + def test_register_fd(self): + with tempfile.TemporaryFile('wb+') as fp: + self.check_register(fd=fp.fileno()) + def test_register_threads(self): self.check_register(all_threads=True) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,10 @@ Library ------- +- Issue #23566: enable(), register(), dump_traceback() and + dump_traceback_later() functions of faulthandler now accept file + descriptors. Patch by Wei Wu. + - Issue #22928: Disabled HTTP header injections in http.client. Original patch by Demian Brecht. diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -133,32 +133,46 @@ call its flush() method. If file is NULL or Py_None, use sys.stderr as the new file. + If file is an integer, it will be treated as file descriptor. - On success, return the new file and write the file descriptor into *p_fd. - On error, return NULL. */ + On success, return the file descriptor and write the new file into *file_ptr. + On error, return -1. */ -static PyObject* -faulthandler_get_fileno(PyObject *file, int *p_fd) +static int +faulthandler_get_fileno(PyObject **file_ptr) { PyObject *result; long fd_long; int fd; + PyObject *file = *file_ptr; if (file == NULL || file == Py_None) { file = _PySys_GetObjectId(&PyId_stderr); if (file == NULL) { PyErr_SetString(PyExc_RuntimeError, "unable to get sys.stderr"); - return NULL; + return -1; } if (file == Py_None) { PyErr_SetString(PyExc_RuntimeError, "sys.stderr is None"); - return NULL; + return -1; } } + else if (PyLong_Check(file)) { + fd = _PyLong_AsInt(file); + if (fd == -1 && PyErr_Occurred()) + return -1; + if (fd < 0 || !_PyVerify_fd(fd)) { + PyErr_SetString(PyExc_ValueError, + "file is not a valid file descripter"); + return -1; + } + *file_ptr = NULL; + return fd; + } result = _PyObject_CallMethodId(file, &PyId_fileno, ""); if (result == NULL) - return NULL; + return -1; fd = -1; if (PyLong_Check(result)) { @@ -171,7 +185,7 @@ if (fd == -1) { PyErr_SetString(PyExc_RuntimeError, "file.fileno() is not a valid file descriptor"); - return NULL; + return -1; } result = _PyObject_CallMethodId(file, &PyId_flush, ""); @@ -181,8 +195,8 @@ /* ignore flush() error */ PyErr_Clear(); } - *p_fd = fd; - return file; + *file_ptr = file; + return fd; } /* Get the state of the current thread: only call this function if the current @@ -215,8 +229,8 @@ &file, &all_threads)) return NULL; - file = faulthandler_get_fileno(file, &fd); - if (file == NULL) + fd = faulthandler_get_fileno(&file); + if (fd < 0) return NULL; tstate = get_thread_state(); @@ -339,8 +353,8 @@ "|Oi:enable", kwlist, &file, &all_threads)) return NULL; - file = faulthandler_get_fileno(file, &fd); - if (file == NULL) + fd = faulthandler_get_fileno(&file); + if (fd < 0) return NULL; tstate = get_thread_state(); @@ -348,7 +362,7 @@ return NULL; Py_XDECREF(fatal_error.file); - Py_INCREF(file); + Py_XINCREF(file); fatal_error.file = file; fatal_error.fd = fd; fatal_error.all_threads = all_threads; @@ -553,8 +567,8 @@ if (tstate == NULL) return NULL; - file = faulthandler_get_fileno(file, &fd); - if (file == NULL) + fd = faulthandler_get_fileno(&file); + if (fd < 0) return NULL; /* format the timeout */ @@ -567,7 +581,7 @@ cancel_dump_traceback_later(); Py_XDECREF(thread.file); - Py_INCREF(file); + Py_XINCREF(file); thread.file = file; thread.fd = fd; thread.timeout_us = timeout_us; @@ -737,8 +751,8 @@ if (tstate == NULL) return NULL; - file = faulthandler_get_fileno(file, &fd); - if (file == NULL) + fd = faulthandler_get_fileno(&file); + if (fd < 0) return NULL; if (user_signals == NULL) { @@ -760,7 +774,7 @@ } Py_XDECREF(user->file); - Py_INCREF(file); + Py_XINCREF(file); user->file = file; user->fd = fd; user->all_threads = all_threads; -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 16:05:28 2015 From: python-checkins at python.org (victor.stinner) Date: Thu, 12 Mar 2015 15:05:28 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323644=2C_=2322038?= =?utf-8?q?=3A_Move_=23include_=3Cstdatomic=2Ec=3E_inside_the_extern_=22C?= =?utf-8?b?IiB7IC4uLiB9?= Message-ID: <20150312150524.7278.28534@psf.io> https://hg.python.org/cpython/rev/eb48295e1f8b changeset: 94957:eb48295e1f8b user: Victor Stinner date: Thu Mar 12 16:04:41 2015 +0100 summary: Issue #23644, #22038: Move #include inside the extern "C" { ... } block in pyatomic.h files: Include/pyatomic.h | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Include/pyatomic.h b/Include/pyatomic.h --- a/Include/pyatomic.h +++ b/Include/pyatomic.h @@ -6,14 +6,14 @@ #include "pyconfig.h" +#ifdef __cplusplus +extern "C" { +#endif + #if defined(HAVE_STD_ATOMIC) #include #endif -#ifdef __cplusplus -extern "C" { -#endif - /* This is modeled after the atomics interface from C1x, according to * the draft at * http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1425.pdf. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 17:50:15 2015 From: python-checkins at python.org (berker.peksag) Date: Thu, 12 Mar 2015 16:50:15 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIwNjE3?= =?utf-8?q?=3A_Remove_unused_import_in_test=5Fssl=2E?= Message-ID: <20150312165015.28809.39029@psf.io> https://hg.python.org/cpython/rev/2b7cf33ccd74 changeset: 94958:2b7cf33ccd74 branch: 3.4 parent: 94954:c06ebb57d4ed user: Berker Peksag date: Thu Mar 12 18:50:49 2015 +0200 summary: Issue #20617: Remove unused import in test_ssl. Patch by Mark Lawrence. files: Lib/test/test_ssl.py | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) 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 @@ -18,7 +18,6 @@ import weakref import platform import functools -from unittest import mock ssl = support.import_module("ssl") -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 17:50:15 2015 From: python-checkins at python.org (berker.peksag) Date: Thu, 12 Mar 2015 16:50:15 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2320617=3A_Remove_unused_import_in_test=5Fssl=2E?= Message-ID: <20150312165015.30198.21491@psf.io> https://hg.python.org/cpython/rev/07e8c0ae232c changeset: 94959:07e8c0ae232c parent: 94957:eb48295e1f8b parent: 94958:2b7cf33ccd74 user: Berker Peksag date: Thu Mar 12 18:51:16 2015 +0200 summary: Issue #20617: Remove unused import in test_ssl. Patch by Mark Lawrence. files: Lib/test/test_ssl.py | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) 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 @@ -18,7 +18,6 @@ import weakref import platform import functools -from unittest import mock ssl = support.import_module("ssl") -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Thu Mar 12 19:28:11 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 12 Mar 2015 19:28:11 +0100 Subject: [Python-checkins] Daily reference leaks (a3b889e9d3f3): sum=3 Message-ID: results for a3b889e9d3f3 on branch "default" -------------------------------------------- test_collections leaked [2, -2, 0] references, sum=0 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/reflogLp1THp', '--timeout', '7200', '-x', 'test_eintr'] From python-checkins at python.org Thu Mar 12 19:41:50 2015 From: python-checkins at python.org (berker.peksag) Date: Thu, 12 Mar 2015 18:41:50 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323581=3A_Add_matm?= =?utf-8?q?ul_support_to_MagicMock=2E?= Message-ID: <20150312184150.30092.76464@psf.io> https://hg.python.org/cpython/rev/8c65480a1e19 changeset: 94960:8c65480a1e19 user: Berker Peksag date: Thu Mar 12 20:42:48 2015 +0200 summary: Issue #23581: Add matmul support to MagicMock. Patch by H?kan L?vdahl. files: Lib/unittest/mock.py | 2 +- Lib/unittest/test/testmock/testmagicmethods.py | 11 ++++++++++ Misc/NEWS | 2 + 3 files changed, 14 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 @@ -1668,7 +1668,7 @@ ) numerics = ( - "add sub mul div floordiv mod lshift rshift and xor or pow truediv" + "add sub mul matmul div floordiv mod lshift rshift and xor or pow truediv" ) inplace = ' '.join('i%s' % n for n in numerics.split()) right = ' '.join('r%s' % n for n in numerics.split()) diff --git a/Lib/unittest/test/testmock/testmagicmethods.py b/Lib/unittest/test/testmock/testmagicmethods.py --- a/Lib/unittest/test/testmock/testmagicmethods.py +++ b/Lib/unittest/test/testmock/testmagicmethods.py @@ -424,5 +424,16 @@ self.assertEqual(list(m), []) + def test_matmul(self): + m = MagicMock() + self.assertIsInstance(m @ 1, MagicMock) + m.__matmul__.return_value = 42 + m.__rmatmul__.return_value = 666 + m.__imatmul__.return_value = 24 + self.assertEqual(m @ 1, 42) + self.assertEqual(1 @ m, 666) + m @= 24 + self.assertEqual(m, 24) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,8 @@ Library ------- +- Issue #23581: Add matmul support to MagicMock. Patch by H?kan L?vdahl. + - Issue #23566: enable(), register(), dump_traceback() and dump_traceback_later() functions of faulthandler now accept file descriptors. Patch by Wei Wu. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 19:47:46 2015 From: python-checkins at python.org (berker.peksag) Date: Thu, 12 Mar 2015 18:47:46 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323651=3A_Fix_typo?= =?utf-8?q?_in_allow=5Fabbrev_docs=2E?= Message-ID: <20150312184638.27349.52487@psf.io> https://hg.python.org/cpython/rev/8411ae359c98 changeset: 94961:8411ae359c98 user: Berker Peksag date: Thu Mar 12 20:47:41 2015 +0200 summary: Issue #23651: Fix typo in allow_abbrev docs. Noticed by Nathan West. files: Doc/library/argparse.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -538,7 +538,7 @@ >>> parser = argparse.ArgumentParser(prog='PROG', allow_abbrev=False) >>> parser.add_argument('--foobar', action='store_true') >>> parser.add_argument('--foonley', action='store_false') - >>> parser.parse_args([--foon]) + >>> parser.parse_args(['--foon']) usage: PROG [-h] [--foobar] [--foonley] PROG: error: unrecognized arguments: --foon -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 19:55:43 2015 From: python-checkins at python.org (berker.peksag) Date: Thu, 12 Mar 2015 18:55:43 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Add_versionadded_directive?= =?utf-8?q?s_for_the_matmul_operator=2E?= Message-ID: <20150312185539.70306.90615@psf.io> https://hg.python.org/cpython/rev/855608ce92bc changeset: 94962:855608ce92bc user: Berker Peksag date: Thu Mar 12 20:56:45 2015 +0200 summary: Add versionadded directives for the matmul operator. files: Doc/library/dis.rst | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -366,6 +366,8 @@ Implements ``TOS = TOS1 @ TOS``. + .. versionadded:: 3.5 + .. opcode:: BINARY_FLOOR_DIVIDE @@ -443,6 +445,8 @@ Implements in-place ``TOS = TOS1 @ TOS``. + .. versionadded:: 3.5 + .. opcode:: INPLACE_FLOOR_DIVIDE -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 21:01:56 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 12 Mar 2015 20:01:56 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323641=3A_Cleaned_out_legacy_dunder_names_from_t?= =?utf-8?q?ests_and_docs=2E?= Message-ID: <20150312200155.27347.67035@psf.io> https://hg.python.org/cpython/rev/9332a545ad85 changeset: 94964:9332a545ad85 parent: 94962:855608ce92bc parent: 94963:7522bb14e36a user: Serhiy Storchaka date: Thu Mar 12 22:01:30 2015 +0200 summary: Issue #23641: Cleaned out legacy dunder names from tests and docs. Fixed 2 to 3 porting bug in pynche.ColorDB. Added few tests for __truediv__, __floordiv__ and __matmul__. files: Doc/library/multiprocessing.rst | 2 +- Doc/library/unittest.mock.rst | 12 +- Lib/_pydecimal.py | 13 +- Lib/datetime.py | 7 +- Lib/sqlite3/test/types.py | 15 +--- Lib/test/mapping_tests.py | 2 +- Lib/test/test_abc.py | 4 +- Lib/test/test_augassign.py | 8 -- Lib/test/test_class.py | 44 ++++++++-- Lib/test/test_descr.py | 25 ++---- Lib/test/test_dynamicclassattribute.py | 4 +- Lib/test/test_inspect.py | 2 +- Lib/test/test_itertools.py | 2 +- Lib/test/test_property.py | 4 +- Lib/test/test_unicode.py | 56 ++----------- Modules/_decimal/tests/deccheck.py | 2 +- Modules/mathmodule.c | 2 +- Tools/clinic/clinic.py | 4 +- Tools/pynche/ColorDB.py | 4 +- 19 files changed, 87 insertions(+), 125 deletions(-) diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -1834,7 +1834,7 @@ >>> l = manager.list(range(10)) >>> l._callmethod('__len__') 10 - >>> l._callmethod('__getslice__', (2, 7)) # equiv to `l[2:7]` + >>> l._callmethod('__getitem__', (slice(2, 7),)) # equiv to `l[2:7]` [2, 3, 4, 5, 6] >>> l._callmethod('__getitem__', (20,)) # equiv to `l[20]` Traceback (most recent call last): diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -1621,19 +1621,19 @@ * ``__hash__``, ``__sizeof__``, ``__repr__`` and ``__str__`` * ``__dir__``, ``__format__`` and ``__subclasses__`` * ``__floor__``, ``__trunc__`` and ``__ceil__`` -* Comparisons: ``__cmp__``, ``__lt__``, ``__gt__``, ``__le__``, ``__ge__``, +* Comparisons: ``__lt__``, ``__gt__``, ``__le__``, ``__ge__``, ``__eq__`` and ``__ne__`` * Container methods: ``__getitem__``, ``__setitem__``, ``__delitem__``, - ``__contains__``, ``__len__``, ``__iter__``, ``__getslice__``, - ``__setslice__``, ``__reversed__`` and ``__missing__`` + ``__contains__``, ``__len__``, ``__iter__``, ``__reversed__`` + and ``__missing__`` * Context manager: ``__enter__`` and ``__exit__`` * Unary numeric methods: ``__neg__``, ``__pos__`` and ``__invert__`` * The numeric methods (including right hand and in-place variants): - ``__add__``, ``__sub__``, ``__mul__``, ``__div__``, + ``__add__``, ``__sub__``, ``__mul__``, ``__matmul__``, ``__div__``, ``__truediv__``, ``__floordiv__``, ``__mod__``, ``__divmod__``, ``__lshift__``, ``__rshift__``, ``__and__``, ``__xor__``, ``__or__``, and ``__pow__`` -* Numeric conversion methods: ``__complex__``, ``__int__``, ``__float__``, - ``__index__`` and ``__coerce__`` +* Numeric conversion methods: ``__complex__``, ``__int__``, ``__float__`` + and ``__index__`` * Descriptor methods: ``__get__``, ``__set__`` and ``__delete__`` * Pickling: ``__reduce__``, ``__reduce_ex__``, ``__getinitargs__``, ``__getnewargs__``, ``__getstate__`` and ``__setstate__`` diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -960,13 +960,12 @@ return self._cmp(other) >= 0 def compare(self, other, context=None): - """Compares one to another. - - -1 => a < b - 0 => a = b - 1 => a > b - NaN => one is NaN - Like __cmp__, but returns Decimal instances. + """Compare self to other. Return a decimal value: + + a or b is a NaN ==> Decimal('NaN') + a < b ==> Decimal('-1') + a == b ==> Decimal('0') + a > b ==> Decimal('1') """ other = _convert_other(other, raiseit=True) diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -651,7 +651,7 @@ Operators: __repr__, __str__ - __cmp__, __hash__ + __eq__, __le__, __lt__, __ge__, __gt__, __hash__ __add__, __radd__, __sub__ (add/radd only with timedelta arg) Methods: @@ -786,7 +786,8 @@ """day (1-31)""" return self._day - # Standard conversions, __cmp__, __hash__ (and helpers) + # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__, + # __hash__ (and helpers) def timetuple(self): "Return local time tuple compatible with time.localtime()." @@ -1010,7 +1011,7 @@ Operators: __repr__, __str__ - __cmp__, __hash__ + __eq__, __le__, __lt__, __ge__, __gt__, __hash__ Methods: diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py --- a/Lib/sqlite3/test/types.py +++ b/Lib/sqlite3/test/types.py @@ -88,19 +88,10 @@ _val = _val.decode('utf-8') self.val = _val - def __cmp__(self, other): + def __eq__(self, other): if not isinstance(other, DeclTypesTests.Foo): - raise ValueError - if self.val == other.val: - return 0 - else: - return 1 - - def __eq__(self, other): - c = self.__cmp__(other) - if c is NotImplemented: - return c - return c == 0 + return NotImplemented + return self.val == other.val def __conform__(self, protocol): if protocol is sqlite.PrepareProtocol: diff --git a/Lib/test/mapping_tests.py b/Lib/test/mapping_tests.py --- a/Lib/test/mapping_tests.py +++ b/Lib/test/mapping_tests.py @@ -64,7 +64,7 @@ self.assertEqual(d, d) self.assertNotEqual(p, d) self.assertNotEqual(d, p) - #__non__zero__ + #bool if p: self.fail("Empty mapping must compare to False") if not d: self.fail("Full mapping must compare to True") # keys(), items(), iterkeys() ... diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -194,9 +194,9 @@ # check that the property's __isabstractmethod__ descriptor does the # right thing when presented with a value that fails truth testing: class NotBool(object): - def __nonzero__(self): + def __bool__(self): raise ValueError() - __len__ = __nonzero__ + __len__ = __bool__ with self.assertRaises(ValueError): class F(C): def bar(self): diff --git a/Lib/test/test_augassign.py b/Lib/test/test_augassign.py --- a/Lib/test/test_augassign.py +++ b/Lib/test/test_augassign.py @@ -144,14 +144,6 @@ output.append("__imatmul__ called") return self - def __div__(self, val): - output.append("__div__ called") - def __rdiv__(self, val): - output.append("__rdiv__ called") - def __idiv__(self, val): - output.append("__idiv__ called") - return self - def __floordiv__(self, val): output.append("__floordiv__ called") return self diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -13,8 +13,12 @@ "rsub", "mul", "rmul", + "matmul", + "rmatmul", "truediv", "rtruediv", + "floordiv", + "rfloordiv", "mod", "rmod", "divmod", @@ -174,15 +178,31 @@ 1 * testme self.assertCallStack([("__rmul__", (testme, 1))]) - if 1/2 == 0: - callLst[:] = [] - testme / 1 - self.assertCallStack([("__div__", (testme, 1))]) + callLst[:] = [] + testme @ 1 + self.assertCallStack([("__matmul__", (testme, 1))]) + callLst[:] = [] + 1 @ testme + self.assertCallStack([("__rmatmul__", (testme, 1))]) - callLst[:] = [] - 1 / testme - self.assertCallStack([("__rdiv__", (testme, 1))]) + callLst[:] = [] + testme / 1 + self.assertCallStack([("__truediv__", (testme, 1))]) + + + callLst[:] = [] + 1 / testme + self.assertCallStack([("__rtruediv__", (testme, 1))]) + + callLst[:] = [] + testme // 1 + self.assertCallStack([("__floordiv__", (testme, 1))]) + + + callLst[:] = [] + 1 // testme + self.assertCallStack([("__rfloordiv__", (testme, 1))]) callLst[:] = [] testme % 1 @@ -444,12 +464,16 @@ def __int__(self): return None __float__ = __int__ + __complex__ = __int__ __str__ = __int__ __repr__ = __int__ - __oct__ = __int__ - __hex__ = __int__ + __bytes__ = __int__ + __bool__ = __int__ + __index__ = __int__ + def index(x): + return [][x] - for f in [int, float, str, repr, oct, hex]: + for f in [float, complex, str, repr, bytes, bin, oct, hex, bool, index]: self.assertRaises(TypeError, f, BadTypeClass()) def testHashStuff(self): 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 @@ -21,7 +21,9 @@ 'add': '+', 'sub': '-', 'mul': '*', - 'div': '/', + 'matmul': '@', + 'truediv': '/', + 'floordiv': '//', 'divmod': 'divmod', 'pow': '**', 'lshift': '<<', @@ -52,8 +54,6 @@ 'invert': '~', 'int': 'int', 'float': 'float', - 'oct': 'oct', - 'hex': 'hex', } for name, expr in list(self.unops.items()): @@ -82,12 +82,6 @@ def binop_test(self, a, b, res, expr="a+b", meth="__add__"): d = {'a': a, 'b': b} - # XXX Hack so this passes before 2.3 when -Qnew is specified. - if meth == "__div__" and 1/2 == 0.5: - meth = "__truediv__" - - if meth == '__divmod__': pass - self.assertEqual(eval(expr, d), res) t = type(a) m = getattr(t, meth) @@ -221,7 +215,7 @@ def number_operators(self, a, b, skip=[]): dict = {'a': a, 'b': b} - for name, expr in list(self.binops.items()): + for name, expr in self.binops.items(): if name not in skip: name = "__%s__" % name if hasattr(a, name): @@ -261,7 +255,7 @@ # Testing complex operations... self.number_operators(100.0j, 3.0j, skip=['lt', 'le', 'gt', 'ge', 'int', 'float', - 'divmod', 'mod']) + 'floordiv', 'divmod', 'mod']) class Number(complex): __slots__ = ['prec'] @@ -4177,9 +4171,8 @@ ('__sub__', 'x - y', 'x -= y'), ('__mul__', 'x * y', 'x *= y'), ('__matmul__', 'x @ y', 'x @= y'), - ('__truediv__', 'operator.truediv(x, y)', None), - ('__floordiv__', 'operator.floordiv(x, y)', None), - ('__div__', 'x / y', 'x /= y'), + ('__truediv__', 'x / y', 'x /= y'), + ('__floordiv__', 'x // y', 'x //= y'), ('__mod__', 'x % y', 'x %= y'), ('__divmod__', 'divmod(x, y)', None), ('__pow__', 'x ** y', 'x **= y'), @@ -4241,8 +4234,8 @@ # Also check type_getattro for correctness. class Meta(type): pass - class X(object): - __metaclass__ = Meta + class X(metaclass=Meta): + pass X.a = 42 Meta.a = Descr("a") self.assertEqual(X.a, 42) diff --git a/Lib/test/test_dynamicclassattribute.py b/Lib/test/test_dynamicclassattribute.py --- a/Lib/test/test_dynamicclassattribute.py +++ b/Lib/test/test_dynamicclassattribute.py @@ -158,9 +158,9 @@ # check that the DynamicClassAttribute's __isabstractmethod__ descriptor does the # right thing when presented with a value that fails truth testing: class NotBool(object): - def __nonzero__(self): + def __bool__(self): raise ValueError() - __len__ = __nonzero__ + __len__ = __bool__ with self.assertRaises(ValueError): class C(object): def foo(self): diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -800,7 +800,7 @@ class Meta(type): fish = 'slap' def __dir__(self): - return ['__class__', '__modules__', '__name__', 'fish'] + return ['__class__', '__module__', '__name__', 'fish'] class Class(metaclass=Meta): pass should_find = inspect.Attribute('fish', 'data', Meta, 'slap') 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,7 +698,7 @@ # iter.__next__ failure on inner object self.assertRaises(ExpectedError, gulp, delayed_raise(1)) - # __cmp__ failure + # __eq__ failure class DummyCmp: def __eq__(self, dst): raise ExpectedError diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -140,9 +140,9 @@ # check that the property's __isabstractmethod__ descriptor does the # right thing when presented with a value that fails truth testing: class NotBool(object): - def __nonzero__(self): + def __bool__(self): raise ValueError() - __len__ = __nonzero__ + __len__ = __bool__ with self.assertRaises(ValueError): class C(object): def foo(self): 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 @@ -2017,64 +2017,26 @@ self.fail("Should have raised UnicodeDecodeError") def test_conversion(self): - # Make sure __unicode__() works properly - class Foo0: + # Make sure __str__() works properly + class ObjectToStr: def __str__(self): return "foo" - class Foo1: + class StrSubclassToStr(str): def __str__(self): return "foo" - class Foo2(object): - def __str__(self): - return "foo" - - class Foo3(object): - def __str__(self): - return "foo" - - class Foo4(str): - def __str__(self): - return "foo" - - class Foo5(str): - def __str__(self): - return "foo" - - class Foo6(str): - def __str__(self): - return "foos" - - def __str__(self): - return "foou" - - class Foo7(str): - def __str__(self): - return "foos" - def __str__(self): - return "foou" - - class Foo8(str): + class StrSubclassToStrSubclass(str): def __new__(cls, content=""): return str.__new__(cls, 2*content) def __str__(self): return self - class Foo9(str): - def __str__(self): - return "not unicode" - - self.assertEqual(str(Foo0()), "foo") - self.assertEqual(str(Foo1()), "foo") - self.assertEqual(str(Foo2()), "foo") - self.assertEqual(str(Foo3()), "foo") - self.assertEqual(str(Foo4("bar")), "foo") - self.assertEqual(str(Foo5("bar")), "foo") - self.assertEqual(str(Foo6("bar")), "foou") - self.assertEqual(str(Foo7("bar")), "foou") - self.assertEqual(str(Foo8("foo")), "foofoo") - self.assertEqual(str(Foo9("foo")), "not unicode") + self.assertEqual(str(ObjectToStr()), "foo") + self.assertEqual(str(StrSubclassToStr("bar")), "foo") + s = str(StrSubclassToStrSubclass("foo")) + self.assertEqual(s, "foofoo") + self.assertIs(type(s), StrSubclassToStrSubclass) def test_unicode_repr(self): class s1: diff --git a/Modules/_decimal/tests/deccheck.py b/Modules/_decimal/tests/deccheck.py --- a/Modules/_decimal/tests/deccheck.py +++ b/Modules/_decimal/tests/deccheck.py @@ -127,7 +127,7 @@ # Functions that require a restricted exponent range for reasonable runtimes. UnaryRestricted = [ - '__ceil__', '__floor__', '__int__', '__long__', '__trunc__', + '__ceil__', '__floor__', '__int__', '__trunc__', 'to_integral', 'to_integral_value' ] diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -1021,7 +1021,7 @@ Note 4: A similar implementation is in Modules/cmathmodule.c. Be sure to update both when making changes. - Note 5: The signature of math.fsum() differs from __builtin__.sum() + Note 5: The signature of math.fsum() differs from builtins.sum() because the start argument doesn't make sense in the context of accurate summation. Since the partials table is collapsed before returning a result, sum(seq2, start=sum(seq1)) may not equal the diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1831,9 +1831,9 @@ __hash__ __iadd__ __iand__ -__idivmod__ __ifloordiv__ __ilshift__ +__imatmul__ __imod__ __imul__ __index__ @@ -1850,6 +1850,7 @@ __len__ __lshift__ __lt__ +__matmul__ __mod__ __mul__ __neg__ @@ -1864,6 +1865,7 @@ __repr__ __rfloordiv__ __rlshift__ +__rmatmul__ __rmod__ __rmul__ __ror__ diff --git a/Tools/pynche/ColorDB.py b/Tools/pynche/ColorDB.py --- a/Tools/pynche/ColorDB.py +++ b/Tools/pynche/ColorDB.py @@ -23,7 +23,6 @@ import sys import re from types import * -import operator class BadColor(Exception): pass @@ -230,9 +229,8 @@ return hexname -_maxtuple = (256.0,) * 3 def triplet_to_fractional_rgb(rgbtuple): - return list(map(operator.__div__, rgbtuple, _maxtuple)) + return [x / 256 for x in rgbtuple] def triplet_to_brightness(rgbtuple): -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Thu Mar 12 21:01:59 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Thu, 12 Mar 2015 20:01:59 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzNjQx?= =?utf-8?q?=3A_Cleaned_out_legacy_dunder_names_from_tests_and_docs=2E?= Message-ID: <20150312200155.118027.11304@psf.io> https://hg.python.org/cpython/rev/7522bb14e36a changeset: 94963:7522bb14e36a branch: 3.4 parent: 94958:2b7cf33ccd74 user: Serhiy Storchaka date: Thu Mar 12 21:56:08 2015 +0200 summary: Issue #23641: Cleaned out legacy dunder names from tests and docs. Fixed 2 to 3 porting bug in pynche.ColorDB. files: Doc/library/multiprocessing.rst | 2 +- Doc/library/unittest.mock.rst | 12 +- Lib/datetime.py | 7 +- Lib/decimal.py | 13 +- Lib/sqlite3/test/types.py | 15 +--- Lib/test/mapping_tests.py | 2 +- Lib/test/test_abc.py | 4 +- Lib/test/test_augassign.py | 8 -- Lib/test/test_class.py | 34 ++++++-- Lib/test/test_descr.py | 24 ++---- Lib/test/test_dynamicclassattribute.py | 4 +- Lib/test/test_inspect.py | 2 +- Lib/test/test_itertools.py | 2 +- Lib/test/test_property.py | 4 +- Lib/test/test_unicode.py | 56 ++----------- Modules/_decimal/tests/deccheck.py | 2 +- Modules/mathmodule.c | 2 +- Tools/clinic/clinic.py | 1 - Tools/pynche/ColorDB.py | 4 +- 19 files changed, 73 insertions(+), 125 deletions(-) diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -1831,7 +1831,7 @@ >>> l = manager.list(range(10)) >>> l._callmethod('__len__') 10 - >>> l._callmethod('__getslice__', (2, 7)) # equiv to `l[2:7]` + >>> l._callmethod('__getitem__', (slice(2, 7),)) # equiv to `l[2:7]` [2, 3, 4, 5, 6] >>> l._callmethod('__getitem__', (20,)) # equiv to `l[20]` Traceback (most recent call last): diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -1579,19 +1579,19 @@ * ``__hash__``, ``__sizeof__``, ``__repr__`` and ``__str__`` * ``__dir__``, ``__format__`` and ``__subclasses__`` * ``__floor__``, ``__trunc__`` and ``__ceil__`` -* Comparisons: ``__cmp__``, ``__lt__``, ``__gt__``, ``__le__``, ``__ge__``, +* Comparisons: ``__lt__``, ``__gt__``, ``__le__``, ``__ge__``, ``__eq__`` and ``__ne__`` * Container methods: ``__getitem__``, ``__setitem__``, ``__delitem__``, - ``__contains__``, ``__len__``, ``__iter__``, ``__getslice__``, - ``__setslice__``, ``__reversed__`` and ``__missing__`` + ``__contains__``, ``__len__``, ``__iter__``, ``__reversed__`` + and ``__missing__`` * Context manager: ``__enter__`` and ``__exit__`` * Unary numeric methods: ``__neg__``, ``__pos__`` and ``__invert__`` * The numeric methods (including right hand and in-place variants): - ``__add__``, ``__sub__``, ``__mul__``, ``__div__``, + ``__add__``, ``__sub__``, ``__mul__``, ``__div__``,``__truediv__``, ``__floordiv__``, ``__mod__``, ``__divmod__``, ``__lshift__``, ``__rshift__``, ``__and__``, ``__xor__``, ``__or__``, and ``__pow__`` -* Numeric conversion methods: ``__complex__``, ``__int__``, ``__float__``, - ``__index__`` and ``__coerce__`` +* Numeric conversion methods: ``__complex__``, ``__int__``, ``__float__`` + and ``__index__`` * Descriptor methods: ``__get__``, ``__set__`` and ``__delete__`` * Pickling: ``__reduce__``, ``__reduce_ex__``, ``__getinitargs__``, ``__getnewargs__``, ``__getstate__`` and ``__setstate__`` diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -646,7 +646,7 @@ Operators: __repr__, __str__ - __cmp__, __hash__ + __eq__, __le__, __lt__, __ge__, __gt__, __hash__ __add__, __radd__, __sub__ (add/radd only with timedelta arg) Methods: @@ -776,7 +776,8 @@ """day (1-31)""" return self._day - # Standard conversions, __cmp__, __hash__ (and helpers) + # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__, + # __hash__ (and helpers) def timetuple(self): "Return local time tuple compatible with time.localtime()." @@ -1005,7 +1006,7 @@ Operators: __repr__, __str__ - __cmp__, __hash__ + __eq__, __le__, __lt__, __ge__, __gt__, __hash__ Methods: diff --git a/Lib/decimal.py b/Lib/decimal.py --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -967,13 +967,12 @@ return self._cmp(other) >= 0 def compare(self, other, context=None): - """Compares one to another. - - -1 => a < b - 0 => a = b - 1 => a > b - NaN => one is NaN - Like __cmp__, but returns Decimal instances. + """Compare self to other. Return a decimal value: + + a or b is a NaN ==> Decimal('NaN') + a < b ==> Decimal('-1') + a == b ==> Decimal('0') + a > b ==> Decimal('1') """ other = _convert_other(other, raiseit=True) diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py --- a/Lib/sqlite3/test/types.py +++ b/Lib/sqlite3/test/types.py @@ -88,19 +88,10 @@ _val = _val.decode('utf-8') self.val = _val - def __cmp__(self, other): + def __eq__(self, other): if not isinstance(other, DeclTypesTests.Foo): - raise ValueError - if self.val == other.val: - return 0 - else: - return 1 - - def __eq__(self, other): - c = self.__cmp__(other) - if c is NotImplemented: - return c - return c == 0 + return NotImplemented + return self.val == other.val def __conform__(self, protocol): if protocol is sqlite.PrepareProtocol: diff --git a/Lib/test/mapping_tests.py b/Lib/test/mapping_tests.py --- a/Lib/test/mapping_tests.py +++ b/Lib/test/mapping_tests.py @@ -64,7 +64,7 @@ self.assertEqual(d, d) self.assertNotEqual(p, d) self.assertNotEqual(d, p) - #__non__zero__ + #bool if p: self.fail("Empty mapping must compare to False") if not d: self.fail("Full mapping must compare to True") # keys(), items(), iterkeys() ... diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -194,9 +194,9 @@ # check that the property's __isabstractmethod__ descriptor does the # right thing when presented with a value that fails truth testing: class NotBool(object): - def __nonzero__(self): + def __bool__(self): raise ValueError() - __len__ = __nonzero__ + __len__ = __bool__ with self.assertRaises(ValueError): class F(C): def bar(self): diff --git a/Lib/test/test_augassign.py b/Lib/test/test_augassign.py --- a/Lib/test/test_augassign.py +++ b/Lib/test/test_augassign.py @@ -136,14 +136,6 @@ output.append("__imul__ called") return self - def __div__(self, val): - output.append("__div__ called") - def __rdiv__(self, val): - output.append("__rdiv__ called") - def __idiv__(self, val): - output.append("__idiv__ called") - return self - def __floordiv__(self, val): output.append("__floordiv__ called") return self diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -15,6 +15,8 @@ "rmul", "truediv", "rtruediv", + "floordiv", + "rfloordiv", "mod", "rmod", "divmod", @@ -174,15 +176,23 @@ 1 * testme self.assertCallStack([("__rmul__", (testme, 1))]) - if 1/2 == 0: - callLst[:] = [] - testme / 1 - self.assertCallStack([("__div__", (testme, 1))]) + callLst[:] = [] + testme / 1 + self.assertCallStack([("__truediv__", (testme, 1))]) - callLst[:] = [] - 1 / testme - self.assertCallStack([("__rdiv__", (testme, 1))]) + callLst[:] = [] + 1 / testme + self.assertCallStack([("__rtruediv__", (testme, 1))]) + + callLst[:] = [] + testme // 1 + self.assertCallStack([("__floordiv__", (testme, 1))]) + + + callLst[:] = [] + 1 // testme + self.assertCallStack([("__rfloordiv__", (testme, 1))]) callLst[:] = [] testme % 1 @@ -444,12 +454,16 @@ def __int__(self): return None __float__ = __int__ + __complex__ = __int__ __str__ = __int__ __repr__ = __int__ - __oct__ = __int__ - __hex__ = __int__ + __bytes__ = __int__ + __bool__ = __int__ + __index__ = __int__ + def index(x): + return [][x] - for f in [int, float, str, repr, oct, hex]: + for f in [float, complex, str, repr, bytes, bin, oct, hex, bool, index]: self.assertRaises(TypeError, f, BadTypeClass()) def testHashStuff(self): 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 @@ -21,7 +21,8 @@ 'add': '+', 'sub': '-', 'mul': '*', - 'div': '/', + 'truediv': '/', + 'floordiv': '//', 'divmod': 'divmod', 'pow': '**', 'lshift': '<<', @@ -52,8 +53,6 @@ 'invert': '~', 'int': 'int', 'float': 'float', - 'oct': 'oct', - 'hex': 'hex', } for name, expr in list(self.unops.items()): @@ -82,12 +81,6 @@ def binop_test(self, a, b, res, expr="a+b", meth="__add__"): d = {'a': a, 'b': b} - # XXX Hack so this passes before 2.3 when -Qnew is specified. - if meth == "__div__" and 1/2 == 0.5: - meth = "__truediv__" - - if meth == '__divmod__': pass - self.assertEqual(eval(expr, d), res) t = type(a) m = getattr(t, meth) @@ -221,7 +214,7 @@ def number_operators(self, a, b, skip=[]): dict = {'a': a, 'b': b} - for name, expr in list(self.binops.items()): + for name, expr in self.binops.items(): if name not in skip: name = "__%s__" % name if hasattr(a, name): @@ -261,7 +254,7 @@ # Testing complex operations... self.number_operators(100.0j, 3.0j, skip=['lt', 'le', 'gt', 'ge', 'int', 'float', - 'divmod', 'mod']) + 'floordiv', 'divmod', 'mod']) class Number(complex): __slots__ = ['prec'] @@ -4160,9 +4153,8 @@ ('__add__', 'x + y', 'x += y'), ('__sub__', 'x - y', 'x -= y'), ('__mul__', 'x * y', 'x *= y'), - ('__truediv__', 'operator.truediv(x, y)', None), - ('__floordiv__', 'operator.floordiv(x, y)', None), - ('__div__', 'x / y', 'x /= y'), + ('__truediv__', 'x / y', 'x /= y'), + ('__floordiv__', 'x // y', 'x //= y'), ('__mod__', 'x % y', 'x %= y'), ('__divmod__', 'divmod(x, y)', None), ('__pow__', 'x ** y', 'x **= y'), @@ -4224,8 +4216,8 @@ # Also check type_getattro for correctness. class Meta(type): pass - class X(object): - __metaclass__ = Meta + class X(metaclass=Meta): + pass X.a = 42 Meta.a = Descr("a") self.assertEqual(X.a, 42) diff --git a/Lib/test/test_dynamicclassattribute.py b/Lib/test/test_dynamicclassattribute.py --- a/Lib/test/test_dynamicclassattribute.py +++ b/Lib/test/test_dynamicclassattribute.py @@ -158,9 +158,9 @@ # check that the DynamicClassAttribute's __isabstractmethod__ descriptor does the # right thing when presented with a value that fails truth testing: class NotBool(object): - def __nonzero__(self): + def __bool__(self): raise ValueError() - __len__ = __nonzero__ + __len__ = __bool__ with self.assertRaises(ValueError): class C(object): def foo(self): diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -786,7 +786,7 @@ class Meta(type): fish = 'slap' def __dir__(self): - return ['__class__', '__modules__', '__name__', 'fish'] + return ['__class__', '__module__', '__name__', 'fish'] class Class(metaclass=Meta): pass should_find = inspect.Attribute('fish', 'data', Meta, 'slap') 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,7 +698,7 @@ # iter.__next__ failure on inner object self.assertRaises(ExpectedError, gulp, delayed_raise(1)) - # __cmp__ failure + # __eq__ failure class DummyCmp: def __eq__(self, dst): raise ExpectedError diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -140,9 +140,9 @@ # check that the property's __isabstractmethod__ descriptor does the # right thing when presented with a value that fails truth testing: class NotBool(object): - def __nonzero__(self): + def __bool__(self): raise ValueError() - __len__ = __nonzero__ + __len__ = __bool__ with self.assertRaises(ValueError): class C(object): def foo(self): 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 @@ -1988,64 +1988,26 @@ self.fail("Should have raised UnicodeDecodeError") def test_conversion(self): - # Make sure __unicode__() works properly - class Foo0: + # Make sure __str__() works properly + class ObjectToStr: def __str__(self): return "foo" - class Foo1: + class StrSubclassToStr(str): def __str__(self): return "foo" - class Foo2(object): - def __str__(self): - return "foo" - - class Foo3(object): - def __str__(self): - return "foo" - - class Foo4(str): - def __str__(self): - return "foo" - - class Foo5(str): - def __str__(self): - return "foo" - - class Foo6(str): - def __str__(self): - return "foos" - - def __str__(self): - return "foou" - - class Foo7(str): - def __str__(self): - return "foos" - def __str__(self): - return "foou" - - class Foo8(str): + class StrSubclassToStrSubclass(str): def __new__(cls, content=""): return str.__new__(cls, 2*content) def __str__(self): return self - class Foo9(str): - def __str__(self): - return "not unicode" - - self.assertEqual(str(Foo0()), "foo") - self.assertEqual(str(Foo1()), "foo") - self.assertEqual(str(Foo2()), "foo") - self.assertEqual(str(Foo3()), "foo") - self.assertEqual(str(Foo4("bar")), "foo") - self.assertEqual(str(Foo5("bar")), "foo") - self.assertEqual(str(Foo6("bar")), "foou") - self.assertEqual(str(Foo7("bar")), "foou") - self.assertEqual(str(Foo8("foo")), "foofoo") - self.assertEqual(str(Foo9("foo")), "not unicode") + self.assertEqual(str(ObjectToStr()), "foo") + self.assertEqual(str(StrSubclassToStr("bar")), "foo") + s = str(StrSubclassToStrSubclass("foo")) + self.assertEqual(s, "foofoo") + self.assertIs(type(s), StrSubclassToStrSubclass) def test_unicode_repr(self): class s1: diff --git a/Modules/_decimal/tests/deccheck.py b/Modules/_decimal/tests/deccheck.py --- a/Modules/_decimal/tests/deccheck.py +++ b/Modules/_decimal/tests/deccheck.py @@ -126,7 +126,7 @@ # Functions that require a restricted exponent range for reasonable runtimes. UnaryRestricted = [ - '__ceil__', '__floor__', '__int__', '__long__', '__trunc__', + '__ceil__', '__floor__', '__int__', '__trunc__', 'to_integral', 'to_integral_value' ] diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -992,7 +992,7 @@ Note 4: A similar implementation is in Modules/cmathmodule.c. Be sure to update both when making changes. - Note 5: The signature of math.fsum() differs from __builtin__.sum() + Note 5: The signature of math.fsum() differs from builtins.sum() because the start argument doesn't make sense in the context of accurate summation. Since the partials table is collapsed before returning a result, sum(seq2, start=sum(seq1)) may not equal the diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1830,7 +1830,6 @@ __hash__ __iadd__ __iand__ -__idivmod__ __ifloordiv__ __ilshift__ __imod__ diff --git a/Tools/pynche/ColorDB.py b/Tools/pynche/ColorDB.py --- a/Tools/pynche/ColorDB.py +++ b/Tools/pynche/ColorDB.py @@ -23,7 +23,6 @@ import sys import re from types import * -import operator class BadColor(Exception): pass @@ -230,9 +229,8 @@ return hexname -_maxtuple = (256.0,) * 3 def triplet_to_fractional_rgb(rgbtuple): - return list(map(operator.__div__, rgbtuple, _maxtuple)) + return [x / 256 for x in rgbtuple] def triplet_to_brightness(rgbtuple): -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 01:29:11 2015 From: python-checkins at python.org (berker.peksag) Date: Fri, 13 Mar 2015 00:29:11 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIyMTU0?= =?utf-8?q?=3A_Add_an_example_to_show_context_management_protocol_support_?= =?utf-8?q?of?= Message-ID: <20150313002911.30090.43235@psf.io> https://hg.python.org/cpython/rev/b6fdf8c7a74d changeset: 94965:b6fdf8c7a74d branch: 3.4 parent: 94963:7522bb14e36a user: Berker Peksag date: Fri Mar 13 02:29:54 2015 +0200 summary: Issue #22154: Add an example to show context management protocol support of ZipFile.open(). Patch by Mike Short. files: Doc/library/zipfile.rst | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -210,6 +210,13 @@ password used for encrypted files. Calling :meth:`.open` on a closed ZipFile will raise a :exc:`RuntimeError`. + :func:`~ZipFile.open` is also a context manager and therefore supports the + :keyword:`with` statement:: + + with ZipFile('spam.zip') as myzip: + with myzip.open('eggs.txt') as myfile: + print(myfile.read()) + .. note:: The file-like object is read-only and provides the following methods: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 01:29:12 2015 From: python-checkins at python.org (berker.peksag) Date: Fri, 13 Mar 2015 00:29:12 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2322154=3A_Add_an_example_to_show_context_managem?= =?utf-8?q?ent_protocol_support_of?= Message-ID: <20150313002911.7286.33291@psf.io> https://hg.python.org/cpython/rev/390a2f4dfd4a changeset: 94966:390a2f4dfd4a parent: 94964:9332a545ad85 parent: 94965:b6fdf8c7a74d user: Berker Peksag date: Fri Mar 13 02:30:17 2015 +0200 summary: Issue #22154: Add an example to show context management protocol support of ZipFile.open(). Patch by Mike Short. files: Doc/library/zipfile.rst | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -210,6 +210,13 @@ password used for encrypted files. Calling :meth:`.open` on a closed ZipFile will raise a :exc:`RuntimeError`. + :func:`~ZipFile.open` is also a context manager and therefore supports the + :keyword:`with` statement:: + + with ZipFile('spam.zip') as myzip: + with myzip.open('eggs.txt') as myfile: + print(myfile.read()) + .. note:: The file-like object is read-only and provides the following methods: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 01:55:07 2015 From: python-checkins at python.org (berker.peksag) Date: Fri, 13 Mar 2015 00:55:07 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323081=3A_Document_that_PySequence=5FList_also_a?= =?utf-8?q?ccepts_iterables=2E?= Message-ID: <20150313005507.28817.47851@psf.io> https://hg.python.org/cpython/rev/eb4a0048978c changeset: 94968:eb4a0048978c parent: 94966:390a2f4dfd4a parent: 94967:e31e987207ab user: Berker Peksag date: Fri Mar 13 02:56:12 2015 +0200 summary: Issue #23081: Document that PySequence_List also accepts iterables. Patch by Lars Buitinck. files: Doc/c-api/sequence.rst | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) 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 @@ -107,8 +107,9 @@ .. c:function:: PyObject* PySequence_List(PyObject *o) - Return a list object with the same contents as the arbitrary sequence *o*. The - returned list is guaranteed to be new. + Return a list object with the same contents as the sequence or iterable *o*, + or *NULL* on failure. The returned list is guaranteed to be new. This is + equivalent to the Python expression ``list(o)``. .. c:function:: PyObject* PySequence_Tuple(PyObject *o) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 01:55:07 2015 From: python-checkins at python.org (berker.peksag) Date: Fri, 13 Mar 2015 00:55:07 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzMDgx?= =?utf-8?q?=3A_Document_that_PySequence=5FList_also_accepts_iterables=2E?= Message-ID: <20150313005507.12527.62010@psf.io> https://hg.python.org/cpython/rev/e31e987207ab changeset: 94967:e31e987207ab branch: 3.4 parent: 94965:b6fdf8c7a74d user: Berker Peksag date: Fri Mar 13 02:55:45 2015 +0200 summary: Issue #23081: Document that PySequence_List also accepts iterables. Patch by Lars Buitinck. files: Doc/c-api/sequence.rst | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) 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 @@ -107,8 +107,9 @@ .. c:function:: PyObject* PySequence_List(PyObject *o) - Return a list object with the same contents as the arbitrary sequence *o*. The - returned list is guaranteed to be new. + Return a list object with the same contents as the sequence or iterable *o*, + or *NULL* on failure. The returned list is guaranteed to be new. This is + equivalent to the Python expression ``list(o)``. .. c:function:: PyObject* PySequence_Tuple(PyObject *o) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 04:41:20 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 13 Mar 2015 03:41:20 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <20150313034120.70282.68589@psf.io> https://hg.python.org/cpython/rev/471d16b286db changeset: 94970:471d16b286db parent: 94968:eb4a0048978c parent: 94969:16e676fd77c0 user: Benjamin Peterson date: Thu Mar 12 22:41:14 2015 -0500 summary: merge 3.4 files: Doc/library/zipfile.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -210,7 +210,7 @@ password used for encrypted files. Calling :meth:`.open` on a closed ZipFile will raise a :exc:`RuntimeError`. - :func:`~ZipFile.open` is also a context manager and therefore supports the + :meth:`~ZipFile.open` is also a context manager and therefore supports the :keyword:`with` statement:: with ZipFile('spam.zip') as myzip: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 04:41:20 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 13 Mar 2015 03:41:20 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_use_the_meth_r?= =?utf-8?q?ole_for_ZipFile=2Eopen?= Message-ID: <20150313034120.32081.60546@psf.io> https://hg.python.org/cpython/rev/16e676fd77c0 changeset: 94969:16e676fd77c0 branch: 3.4 parent: 94967:e31e987207ab user: Benjamin Peterson date: Thu Mar 12 22:41:06 2015 -0500 summary: use the meth role for ZipFile.open files: Doc/library/zipfile.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -210,7 +210,7 @@ password used for encrypted files. Calling :meth:`.open` on a closed ZipFile will raise a :exc:`RuntimeError`. - :func:`~ZipFile.open` is also a context manager and therefore supports the + :meth:`~ZipFile.open` is also a context manager and therefore supports the :keyword:`with` statement:: with ZipFile('spam.zip') as myzip: -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 07:32:25 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 13 Mar 2015 06:32:25 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323051=3A_multiprocessing=2EPool_methods_imap=28?= =?utf-8?q?=29_and_imap=5Funordered=28=29_now?= Message-ID: <20150313063225.1853.65967@psf.io> https://hg.python.org/cpython/rev/7891d084a9ad changeset: 94972:7891d084a9ad parent: 94970:471d16b286db parent: 94971:525ccfcc55f7 user: Serhiy Storchaka date: Fri Mar 13 08:30:33 2015 +0200 summary: Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now handle exceptions raised by an iterator. Patch by Alon Diamant and Davin Potts. files: Lib/multiprocessing/pool.py | 37 +++++++++----- Lib/test/_test_multiprocessing.py | 46 +++++++++++++++++++ Misc/ACKS | 2 + Misc/NEWS | 4 + 4 files changed, 75 insertions(+), 14 deletions(-) diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -374,25 +374,34 @@ thread = threading.current_thread() for taskseq, set_length in iter(taskqueue.get, None): + task = None i = -1 - for i, task in enumerate(taskseq): - if thread._state: - util.debug('task handler found thread._state != RUN') - break - try: - put(task) - except Exception as e: - job, ind = task[:2] + try: + for i, task in enumerate(taskseq): + if thread._state: + util.debug('task handler found thread._state != RUN') + break try: - cache[job]._set(ind, (False, e)) - except KeyError: - pass - else: + put(task) + except Exception as e: + job, ind = task[:2] + try: + cache[job]._set(ind, (False, e)) + except KeyError: + pass + else: + if set_length: + util.debug('doing set_length()') + set_length(i+1) + continue + break + except Exception as ex: + job, ind = task[:2] if task else (0, 0) + if job in cache: + cache[job]._set(ind + 1, (False, ex)) if set_length: util.debug('doing set_length()') set_length(i+1) - continue - break else: util.debug('task handler got sentinel') diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -1660,6 +1660,14 @@ def mul(x, y): return x*y +class SayWhenError(ValueError): pass + +def exception_throwing_generator(total, when): + for i in range(total): + if i == when: + raise SayWhenError("Somebody said when") + yield i + class _TestPool(BaseTestCase): @classmethod @@ -1758,6 +1766,25 @@ self.assertEqual(next(it), i*i) self.assertRaises(StopIteration, it.__next__) + def test_imap_handle_iterable_exception(self): + if self.TYPE == 'manager': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + + it = self.pool.imap(sqr, exception_throwing_generator(10, 3), 1) + for i in range(3): + self.assertEqual(next(it), i*i) + self.assertRaises(SayWhenError, it.__next__) + + # SayWhenError seen at start of problematic chunk's results + it = self.pool.imap(sqr, exception_throwing_generator(20, 7), 2) + for i in range(6): + self.assertEqual(next(it), i*i) + self.assertRaises(SayWhenError, it.__next__) + it = self.pool.imap(sqr, exception_throwing_generator(20, 7), 4) + for i in range(4): + self.assertEqual(next(it), i*i) + self.assertRaises(SayWhenError, it.__next__) + def test_imap_unordered(self): it = self.pool.imap_unordered(sqr, list(range(1000))) self.assertEqual(sorted(it), list(map(sqr, list(range(1000))))) @@ -1765,6 +1792,25 @@ it = self.pool.imap_unordered(sqr, list(range(1000)), chunksize=53) self.assertEqual(sorted(it), list(map(sqr, list(range(1000))))) + def test_imap_unordered_handle_iterable_exception(self): + if self.TYPE == 'manager': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + + it = self.pool.imap_unordered(sqr, + exception_throwing_generator(10, 3), + 1) + with self.assertRaises(SayWhenError): + # imap_unordered makes it difficult to anticipate the SayWhenError + for i in range(10): + self.assertEqual(next(it), i*i) + + it = self.pool.imap_unordered(sqr, + exception_throwing_generator(20, 7), + 2) + with self.assertRaises(SayWhenError): + for i in range(20): + self.assertEqual(next(it), i*i) + def test_make_pool(self): self.assertRaises(ValueError, multiprocessing.Pool, -1) self.assertRaises(ValueError, multiprocessing.Pool, 0) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -336,6 +336,7 @@ Caleb Deveraux Catherine Devlin Scott Dial +Alon Diamant Toby Dickenson Mark Dickinson Jack Diederich @@ -1099,6 +1100,7 @@ Iustin Pop Claudiu Popa John Popplewell +Davin Potts Guillaume Pratte Amrit Prem Paul Prescod diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,10 @@ Library ------- +- Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now + handle exceptions raised by an iterator. Patch by Alon Diamant and Davin + Potts. + - Issue #23581: Add matmul support to MagicMock. Patch by H?kan L?vdahl. - Issue #23566: enable(), register(), dump_traceback() and -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 07:32:25 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 13 Mar 2015 06:32:25 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzMDUx?= =?utf-8?q?=3A_multiprocessing=2EPool_methods_imap=28=29_and_imap=5Funorde?= =?utf-8?b?cmVkKCkgbm93?= Message-ID: <20150313063224.70302.78918@psf.io> https://hg.python.org/cpython/rev/525ccfcc55f7 changeset: 94971:525ccfcc55f7 branch: 3.4 parent: 94969:16e676fd77c0 user: Serhiy Storchaka date: Fri Mar 13 08:25:26 2015 +0200 summary: Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now handle exceptions raised by an iterator. Patch by Alon Diamant and Davin Potts. files: Lib/multiprocessing/pool.py | 37 +++++++++----- Lib/test/_test_multiprocessing.py | 46 +++++++++++++++++++ Misc/ACKS | 2 + Misc/NEWS | 4 + 4 files changed, 75 insertions(+), 14 deletions(-) diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -374,25 +374,34 @@ thread = threading.current_thread() for taskseq, set_length in iter(taskqueue.get, None): + task = None i = -1 - for i, task in enumerate(taskseq): - if thread._state: - util.debug('task handler found thread._state != RUN') - break - try: - put(task) - except Exception as e: - job, ind = task[:2] + try: + for i, task in enumerate(taskseq): + if thread._state: + util.debug('task handler found thread._state != RUN') + break try: - cache[job]._set(ind, (False, e)) - except KeyError: - pass - else: + put(task) + except Exception as e: + job, ind = task[:2] + try: + cache[job]._set(ind, (False, e)) + except KeyError: + pass + else: + if set_length: + util.debug('doing set_length()') + set_length(i+1) + continue + break + except Exception as ex: + job, ind = task[:2] if task else (0, 0) + if job in cache: + cache[job]._set(ind + 1, (False, ex)) if set_length: util.debug('doing set_length()') set_length(i+1) - continue - break else: util.debug('task handler got sentinel') diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -1660,6 +1660,14 @@ def mul(x, y): return x*y +class SayWhenError(ValueError): pass + +def exception_throwing_generator(total, when): + for i in range(total): + if i == when: + raise SayWhenError("Somebody said when") + yield i + class _TestPool(BaseTestCase): @classmethod @@ -1758,6 +1766,25 @@ self.assertEqual(next(it), i*i) self.assertRaises(StopIteration, it.__next__) + def test_imap_handle_iterable_exception(self): + if self.TYPE == 'manager': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + + it = self.pool.imap(sqr, exception_throwing_generator(10, 3), 1) + for i in range(3): + self.assertEqual(next(it), i*i) + self.assertRaises(SayWhenError, it.__next__) + + # SayWhenError seen at start of problematic chunk's results + it = self.pool.imap(sqr, exception_throwing_generator(20, 7), 2) + for i in range(6): + self.assertEqual(next(it), i*i) + self.assertRaises(SayWhenError, it.__next__) + it = self.pool.imap(sqr, exception_throwing_generator(20, 7), 4) + for i in range(4): + self.assertEqual(next(it), i*i) + self.assertRaises(SayWhenError, it.__next__) + def test_imap_unordered(self): it = self.pool.imap_unordered(sqr, list(range(1000))) self.assertEqual(sorted(it), list(map(sqr, list(range(1000))))) @@ -1765,6 +1792,25 @@ it = self.pool.imap_unordered(sqr, list(range(1000)), chunksize=53) self.assertEqual(sorted(it), list(map(sqr, list(range(1000))))) + def test_imap_unordered_handle_iterable_exception(self): + if self.TYPE == 'manager': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + + it = self.pool.imap_unordered(sqr, + exception_throwing_generator(10, 3), + 1) + with self.assertRaises(SayWhenError): + # imap_unordered makes it difficult to anticipate the SayWhenError + for i in range(10): + self.assertEqual(next(it), i*i) + + it = self.pool.imap_unordered(sqr, + exception_throwing_generator(20, 7), + 2) + with self.assertRaises(SayWhenError): + for i in range(20): + self.assertEqual(next(it), i*i) + def test_make_pool(self): self.assertRaises(ValueError, multiprocessing.Pool, -1) self.assertRaises(ValueError, multiprocessing.Pool, 0) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -332,6 +332,7 @@ Caleb Deveraux Catherine Devlin Scott Dial +Alon Diamant Toby Dickenson Mark Dickinson Jack Diederich @@ -1081,6 +1082,7 @@ Iustin Pop Claudiu Popa John Popplewell +Davin Potts Guillaume Pratte Amrit Prem Paul Prescod diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,10 @@ Library ------- +- Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now + handle exceptions raised by an iterator. Patch by Alon Diamant and Davin + Potts. + - Issue #22928: Disabled HTTP header injections in http.client. Original patch by Demian Brecht. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 07:32:25 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 13 Mar 2015 06:32:25 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIzMDUx?= =?utf-8?q?=3A_multiprocessing=2EPool_methods_imap=28=29_and_imap=5Funorde?= =?utf-8?b?cmVkKCkgbm93?= Message-ID: <20150313063225.32085.64644@psf.io> https://hg.python.org/cpython/rev/311d52878a65 changeset: 94973:311d52878a65 branch: 2.7 parent: 94951:1c45047c5102 user: Serhiy Storchaka date: Fri Mar 13 08:31:34 2015 +0200 summary: Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now handle exceptions raised by an iterator. Patch by Alon Diamant and Davin Potts. files: Lib/multiprocessing/pool.py | 37 +++++++++----- Lib/test/test_multiprocessing.py | 47 ++++++++++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 4 + 4 files changed, 75 insertions(+), 14 deletions(-) diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -334,25 +334,34 @@ thread = threading.current_thread() for taskseq, set_length in iter(taskqueue.get, None): + task = None i = -1 - for i, task in enumerate(taskseq): - if thread._state: - debug('task handler found thread._state != RUN') - break - try: - put(task) - except Exception as e: - job, ind = task[:2] + try: + for i, task in enumerate(taskseq): + if thread._state: + debug('task handler found thread._state != RUN') + break try: - cache[job]._set(ind, (False, e)) - except KeyError: - pass - else: + put(task) + except Exception as e: + job, ind = task[:2] + try: + cache[job]._set(ind, (False, e)) + except KeyError: + pass + else: + if set_length: + debug('doing set_length()') + set_length(i+1) + continue + break + except Exception as ex: + job, ind = task[:2] if task else (0, 0) + if job in cache: + cache[job]._set(ind + 1, (False, ex)) if set_length: debug('doing set_length()') set_length(i+1) - continue - break else: debug('task handler got sentinel') diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -1122,6 +1122,15 @@ def sqr(x, wait=0.0): time.sleep(wait) return x*x + +class SayWhenError(ValueError): pass + +def exception_throwing_generator(total, when): + for i in range(total): + if i == when: + raise SayWhenError("Somebody said when") + yield i + class _TestPool(BaseTestCase): def test_apply(self): @@ -1177,6 +1186,25 @@ self.assertEqual(it.next(), i*i) self.assertRaises(StopIteration, it.next) + def test_imap_handle_iterable_exception(self): + if self.TYPE == 'manager': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + + it = self.pool.imap(sqr, exception_throwing_generator(10, 3), 1) + for i in range(3): + self.assertEqual(next(it), i*i) + self.assertRaises(SayWhenError, it.next) + + # SayWhenError seen at start of problematic chunk's results + it = self.pool.imap(sqr, exception_throwing_generator(20, 7), 2) + for i in range(6): + self.assertEqual(next(it), i*i) + self.assertRaises(SayWhenError, it.next) + it = self.pool.imap(sqr, exception_throwing_generator(20, 7), 4) + for i in range(4): + self.assertEqual(next(it), i*i) + self.assertRaises(SayWhenError, it.next) + def test_imap_unordered(self): it = self.pool.imap_unordered(sqr, range(1000)) self.assertEqual(sorted(it), map(sqr, range(1000))) @@ -1184,6 +1212,25 @@ it = self.pool.imap_unordered(sqr, range(1000), chunksize=53) self.assertEqual(sorted(it), map(sqr, range(1000))) + def test_imap_unordered_handle_iterable_exception(self): + if self.TYPE == 'manager': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + + it = self.pool.imap_unordered(sqr, + exception_throwing_generator(10, 3), + 1) + with self.assertRaises(SayWhenError): + # imap_unordered makes it difficult to anticipate the SayWhenError + for i in range(10): + self.assertEqual(next(it), i*i) + + it = self.pool.imap_unordered(sqr, + exception_throwing_generator(20, 7), + 2) + with self.assertRaises(SayWhenError): + for i in range(20): + self.assertEqual(next(it), i*i) + def test_make_pool(self): self.assertRaises(ValueError, multiprocessing.Pool, -1) self.assertRaises(ValueError, multiprocessing.Pool, 0) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -325,6 +325,7 @@ Caleb Deveraux Catherine Devlin Scott Dial +Alon Diamant Toby Dickenson Mark Dickinson Jack Diederich diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,10 @@ Library ------- +- Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now + handle exceptions raised by an iterator. Patch by Alon Diamant and Davin + Potts. + - Issue #22928: Disabled HTTP header injections in httplib. Original patch by Demian Brecht. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 08:10:55 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 13 Mar 2015 07:10:55 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogSXNzdWUgIzIzMTM4?= =?utf-8?q?=3A_Fixed_parsing_cookies_with_absent_keys_or_values_in_cookiej?= =?utf-8?b?YXIu?= Message-ID: <20150313071055.28821.52483@psf.io> https://hg.python.org/cpython/rev/c1abcbcfefab changeset: 94975:c1abcbcfefab branch: 3.4 parent: 94971:525ccfcc55f7 user: Serhiy Storchaka date: Fri Mar 13 09:05:01 2015 +0200 summary: Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar. Patch by Demian Brecht. files: Lib/http/cookiejar.py | 46 ++++++++++++++------ Lib/test/test_http_cookiejar.py | 23 ++++++++++ Misc/NEWS | 3 + 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py --- a/Lib/http/cookiejar.py +++ b/Lib/http/cookiejar.py @@ -472,26 +472,42 @@ for ns_header in ns_headers: pairs = [] version_set = False - for ii, param in enumerate(re.split(r";\s*", ns_header)): - param = param.rstrip() - if param == "": continue - if "=" not in param: - k, v = param, None - else: - k, v = re.split(r"\s*=\s*", param, 1) - k = k.lstrip() + + # XXX: The following does not strictly adhere to RFCs in that empty + # names and values are legal (the former will only appear once and will + # be overwritten if multiple occurrences are present). This is + # mostly to deal with backwards compatibility. + for ii, param in enumerate(ns_header.split(';')): + param = param.strip() + + key, sep, val = param.partition('=') + key = key.strip() + + if not key: + if ii == 0: + break + else: + continue + + # allow for a distinction between present and empty and missing + # altogether + val = val.strip() if sep else None + if ii != 0: - lc = k.lower() + lc = key.lower() if lc in known_attrs: - k = lc - if k == "version": + key = lc + + if key == "version": # This is an RFC 2109 cookie. - v = strip_quotes(v) + if val is not None: + val = strip_quotes(val) version_set = True - if k == "expires": + elif key == "expires": # convert expires date to seconds since epoch - v = http2time(strip_quotes(v)) # None if invalid - pairs.append((k, v)) + if val is not None: + val = http2time(strip_quotes(val)) # None if invalid + pairs.append((key, val)) if pairs: if not version_set: diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py --- a/Lib/test/test_http_cookiejar.py +++ b/Lib/test/test_http_cookiejar.py @@ -479,6 +479,9 @@ interact_netscape(c, "http://www.acme.com:80/", 'foo=bar; expires=') interact_netscape(c, "http://www.acme.com:80/", 'spam=eggs; ' 'expires="Foo Bar 25 33:22:11 3022"') + interact_netscape(c, 'http://www.acme.com/', 'fortytwo=') + interact_netscape(c, 'http://www.acme.com/', '=unladenswallow') + interact_netscape(c, 'http://www.acme.com/', 'holyhandgrenade') cookie = c._cookies[".acme.com"]["/"]["spam"] self.assertEqual(cookie.domain, ".acme.com") @@ -505,6 +508,16 @@ self.assertIsNone(foo.expires) self.assertIsNone(spam.expires) + cookie = c._cookies['www.acme.com']['/']['fortytwo'] + self.assertIsNotNone(cookie.value) + self.assertEqual(cookie.value, '') + + # there should be a distinction between a present but empty value + # (above) and a value that's entirely missing (below) + + cookie = c._cookies['www.acme.com']['/']['holyhandgrenade'] + self.assertIsNone(cookie.value) + def test_ns_parser_special_names(self): # names such as 'expires' are not special in first name=value pair # of Set-Cookie: header @@ -1080,6 +1093,13 @@ parse_ns_headers(["foo"]), [[("foo", None), ("version", "0")]] ) + # missing cookie values for parsed attributes + self.assertEqual( + parse_ns_headers(['foo=bar; expires']), + [[('foo', 'bar'), ('expires', None), ('version', '0')]]) + self.assertEqual( + parse_ns_headers(['foo=bar; version']), + [[('foo', 'bar'), ('version', None)]]) # shouldn't add version if header is empty self.assertEqual(parse_ns_headers([""]), []) @@ -1092,6 +1112,8 @@ c.extract_cookies(r, req) return c + future = time2netscape(time.time()+3600) + # none of these bad headers should cause an exception to be raised for headers in [ ["Set-Cookie: "], # actually, nothing wrong with this @@ -1102,6 +1124,7 @@ ["Set-Cookie: b=foo; max-age=oops"], # bad version ["Set-Cookie: b=foo; version=spam"], + ["Set-Cookie:; Expires=%s" % future], ]: c = cookiejar_from_cookie_headers(headers) # these bad cookies shouldn't be set diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,9 @@ Library ------- +- Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar. + Patch by Demian Brecht. + - Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now handle exceptions raised by an iterator. Patch by Alon Diamant and Davin Potts. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 08:10:55 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 13 Mar 2015 07:10:55 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Issue_=2323138=3A_Fixed_parsing_cookies_with_absent_keys?= =?utf-8?q?_or_values_in_cookiejar=2E?= Message-ID: <20150313071055.30080.16774@psf.io> https://hg.python.org/cpython/rev/7cc7c794d1cb changeset: 94976:7cc7c794d1cb parent: 94972:7891d084a9ad parent: 94975:c1abcbcfefab user: Serhiy Storchaka date: Fri Mar 13 09:09:35 2015 +0200 summary: Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar. Patch by Demian Brecht. files: Lib/http/cookiejar.py | 46 ++++++++++++++------ Lib/test/test_http_cookiejar.py | 23 ++++++++++ Misc/NEWS | 3 + 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py --- a/Lib/http/cookiejar.py +++ b/Lib/http/cookiejar.py @@ -472,26 +472,42 @@ for ns_header in ns_headers: pairs = [] version_set = False - for ii, param in enumerate(re.split(r";\s*", ns_header)): - param = param.rstrip() - if param == "": continue - if "=" not in param: - k, v = param, None - else: - k, v = re.split(r"\s*=\s*", param, maxsplit=1) - k = k.lstrip() + + # XXX: The following does not strictly adhere to RFCs in that empty + # names and values are legal (the former will only appear once and will + # be overwritten if multiple occurrences are present). This is + # mostly to deal with backwards compatibility. + for ii, param in enumerate(ns_header.split(';')): + param = param.strip() + + key, sep, val = param.partition('=') + key = key.strip() + + if not key: + if ii == 0: + break + else: + continue + + # allow for a distinction between present and empty and missing + # altogether + val = val.strip() if sep else None + if ii != 0: - lc = k.lower() + lc = key.lower() if lc in known_attrs: - k = lc - if k == "version": + key = lc + + if key == "version": # This is an RFC 2109 cookie. - v = strip_quotes(v) + if val is not None: + val = strip_quotes(val) version_set = True - if k == "expires": + elif key == "expires": # convert expires date to seconds since epoch - v = http2time(strip_quotes(v)) # None if invalid - pairs.append((k, v)) + if val is not None: + val = http2time(strip_quotes(val)) # None if invalid + pairs.append((key, val)) if pairs: if not version_set: diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py --- a/Lib/test/test_http_cookiejar.py +++ b/Lib/test/test_http_cookiejar.py @@ -479,6 +479,9 @@ interact_netscape(c, "http://www.acme.com:80/", 'foo=bar; expires=') interact_netscape(c, "http://www.acme.com:80/", 'spam=eggs; ' 'expires="Foo Bar 25 33:22:11 3022"') + interact_netscape(c, 'http://www.acme.com/', 'fortytwo=') + interact_netscape(c, 'http://www.acme.com/', '=unladenswallow') + interact_netscape(c, 'http://www.acme.com/', 'holyhandgrenade') cookie = c._cookies[".acme.com"]["/"]["spam"] self.assertEqual(cookie.domain, ".acme.com") @@ -505,6 +508,16 @@ self.assertIsNone(foo.expires) self.assertIsNone(spam.expires) + cookie = c._cookies['www.acme.com']['/']['fortytwo'] + self.assertIsNotNone(cookie.value) + self.assertEqual(cookie.value, '') + + # there should be a distinction between a present but empty value + # (above) and a value that's entirely missing (below) + + cookie = c._cookies['www.acme.com']['/']['holyhandgrenade'] + self.assertIsNone(cookie.value) + def test_ns_parser_special_names(self): # names such as 'expires' are not special in first name=value pair # of Set-Cookie: header @@ -1080,6 +1093,13 @@ parse_ns_headers(["foo"]), [[("foo", None), ("version", "0")]] ) + # missing cookie values for parsed attributes + self.assertEqual( + parse_ns_headers(['foo=bar; expires']), + [[('foo', 'bar'), ('expires', None), ('version', '0')]]) + self.assertEqual( + parse_ns_headers(['foo=bar; version']), + [[('foo', 'bar'), ('version', None)]]) # shouldn't add version if header is empty self.assertEqual(parse_ns_headers([""]), []) @@ -1092,6 +1112,8 @@ c.extract_cookies(r, req) return c + future = time2netscape(time.time()+3600) + # none of these bad headers should cause an exception to be raised for headers in [ ["Set-Cookie: "], # actually, nothing wrong with this @@ -1102,6 +1124,7 @@ ["Set-Cookie: b=foo; max-age=oops"], # bad version ["Set-Cookie: b=foo; version=spam"], + ["Set-Cookie:; Expires=%s" % future], ]: c = cookiejar_from_cookie_headers(headers) # these bad cookies shouldn't be set diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,9 @@ Library ------- +- Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar. + Patch by Demian Brecht. + - Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now handle exceptions raised by an iterator. Patch by Alon Diamant and Davin Potts. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 08:10:55 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Fri, 13 Mar 2015 07:10:55 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMi43KTogSXNzdWUgIzIzMTM4?= =?utf-8?q?=3A_Fixed_parsing_cookies_with_absent_keys_or_values_in_cookiej?= =?utf-8?b?YXIu?= Message-ID: <20150313071055.7286.8257@psf.io> https://hg.python.org/cpython/rev/44c1c0cbdc06 changeset: 94974:44c1c0cbdc06 branch: 2.7 user: Serhiy Storchaka date: Fri Mar 13 09:04:34 2015 +0200 summary: Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar. Patch by Demian Brecht. files: Lib/cookielib.py | 46 +++++++++++++++++-------- Lib/test/test_cookielib.py | 23 +++++++++++++ Misc/NEWS | 3 + 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/Lib/cookielib.py b/Lib/cookielib.py --- a/Lib/cookielib.py +++ b/Lib/cookielib.py @@ -464,26 +464,42 @@ for ns_header in ns_headers: pairs = [] version_set = False - for ii, param in enumerate(re.split(r";\s*", ns_header)): - param = param.rstrip() - if param == "": continue - if "=" not in param: - k, v = param, None - else: - k, v = re.split(r"\s*=\s*", param, 1) - k = k.lstrip() + + # XXX: The following does not strictly adhere to RFCs in that empty + # names and values are legal (the former will only appear once and will + # be overwritten if multiple occurrences are present). This is + # mostly to deal with backwards compatibility. + for ii, param in enumerate(ns_header.split(';')): + param = param.strip() + + key, sep, val = param.partition('=') + key = key.strip() + + if not key: + if ii == 0: + break + else: + continue + + # allow for a distinction between present and empty and missing + # altogether + val = val.strip() if sep else None + if ii != 0: - lc = k.lower() + lc = key.lower() if lc in known_attrs: - k = lc - if k == "version": + key = lc + + if key == "version": # This is an RFC 2109 cookie. - v = _strip_quotes(v) + if val is not None: + val = _strip_quotes(val) version_set = True - if k == "expires": + elif key == "expires": # convert expires date to seconds since epoch - v = http2time(_strip_quotes(v)) # None if invalid - pairs.append((k, v)) + if val is not None: + val = http2time(_strip_quotes(val)) # None if invalid + pairs.append((key, val)) if pairs: if not version_set: diff --git a/Lib/test/test_cookielib.py b/Lib/test/test_cookielib.py --- a/Lib/test/test_cookielib.py +++ b/Lib/test/test_cookielib.py @@ -445,6 +445,9 @@ interact_netscape(c, "http://www.acme.com:80/", 'foo=bar; expires=') interact_netscape(c, "http://www.acme.com:80/", 'spam=eggs; ' 'expires="Foo Bar 25 33:22:11 3022"') + interact_netscape(c, 'http://www.acme.com/', 'fortytwo=') + interact_netscape(c, 'http://www.acme.com/', '=unladenswallow') + interact_netscape(c, 'http://www.acme.com/', 'holyhandgrenade') cookie = c._cookies[".acme.com"]["/"]["spam"] self.assertEqual(cookie.domain, ".acme.com") @@ -471,6 +474,16 @@ self.assertIsNone(foo.expires) self.assertIsNone(spam.expires) + cookie = c._cookies['www.acme.com']['/']['fortytwo'] + self.assertIsNotNone(cookie.value) + self.assertEqual(cookie.value, '') + + # there should be a distinction between a present but empty value + # (above) and a value that's entirely missing (below) + + cookie = c._cookies['www.acme.com']['/']['holyhandgrenade'] + self.assertIsNone(cookie.value) + def test_ns_parser_special_names(self): # names such as 'expires' are not special in first name=value pair # of Set-Cookie: header @@ -1092,6 +1105,13 @@ parse_ns_headers(["foo"]), [[("foo", None), ("version", "0")]] ) + # missing cookie values for parsed attributes + self.assertEqual( + parse_ns_headers(['foo=bar; expires']), + [[('foo', 'bar'), ('expires', None), ('version', '0')]]) + self.assertEqual( + parse_ns_headers(['foo=bar; version']), + [[('foo', 'bar'), ('version', None)]]) # shouldn't add version if header is empty self.assertEqual(parse_ns_headers([""]), []) @@ -1106,6 +1126,8 @@ c.extract_cookies(r, req) return c + future = cookielib.time2netscape(time.time()+3600) + # none of these bad headers should cause an exception to be raised for headers in [ ["Set-Cookie: "], # actually, nothing wrong with this @@ -1116,6 +1138,7 @@ ["Set-Cookie: b=foo; max-age=oops"], # bad version ["Set-Cookie: b=foo; version=spam"], + ["Set-Cookie:; Expires=%s" % future], ]: c = cookiejar_from_cookie_headers(headers) # these bad cookies shouldn't be set diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,9 @@ Library ------- +- Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar. + Patch by Demian Brecht. + - Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now handle exceptions raised by an iterator. Patch by Alon Diamant and Davin Potts. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 11:14:30 2015 From: python-checkins at python.org (victor.stinner) Date: Fri, 13 Mar 2015 10:14:30 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323566=3A_Skip_=22?= =?utf-8?q?fd=22_tests_of_test=5Ffaulthandler_on_Windows?= Message-ID: <20150313101429.1845.97184@psf.io> https://hg.python.org/cpython/rev/211e29335e72 changeset: 94978:211e29335e72 user: Victor Stinner date: Fri Mar 13 11:01:30 2015 +0100 summary: Issue #23566: Skip "fd" tests of test_faulthandler on Windows subprocess doesn't support pass_fds on Windows. files: Lib/test/test_faulthandler.py | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -221,6 +221,8 @@ 'Segmentation fault', filename=filename) + @unittest.skipIf(sys.platform == "win32", + "subprocess doesn't support pass_fds on Windows") def test_enable_fd(self): with tempfile.TemporaryFile('wb+') as fp: fd = fp.fileno() @@ -373,6 +375,8 @@ with temporary_filename() as filename: self.check_dump_traceback(filename=filename) + @unittest.skipIf(sys.platform == "win32", + "subprocess doesn't support pass_fds on Windows") def test_dump_traceback_fd(self): with tempfile.TemporaryFile('wb+') as fp: self.check_dump_traceback(fd=fp.fileno()) @@ -545,6 +549,8 @@ with temporary_filename() as filename: self.check_dump_traceback_later(filename=filename) + @unittest.skipIf(sys.platform == "win32", + "subprocess doesn't support pass_fds on Windows") def test_dump_traceback_later_fd(self): with tempfile.TemporaryFile('wb+') as fp: self.check_dump_traceback_later(fd=fp.fileno()) @@ -645,6 +651,8 @@ with temporary_filename() as filename: self.check_register(filename=filename) + @unittest.skipIf(sys.platform == "win32", + "subprocess doesn't support pass_fds on Windows") def test_register_fd(self): with tempfile.TemporaryFile('wb+') as fp: self.check_register(fd=fp.fileno()) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 11:14:30 2015 From: python-checkins at python.org (victor.stinner) Date: Fri, 13 Mar 2015 10:14:30 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_test?= Message-ID: <20150313101429.30178.75375@psf.io> https://hg.python.org/cpython/rev/d927047b1d8e changeset: 94977:d927047b1d8e user: Victor Stinner date: Thu Mar 12 16:19:01 2015 +0100 summary: test files: Doc/library/time.rst | 4 + Lib/test/eintrdata/eintr_tester.py | 17 ++- Lib/test/test_signal.py | 17 +- Modules/timemodule.c | 116 +++++++++------- 4 files changed, 98 insertions(+), 56 deletions(-) diff --git a/Doc/library/time.rst b/Doc/library/time.rst --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -350,6 +350,10 @@ requested by an arbitrary amount because of the scheduling of other activity in the system. + .. versionchanged:: 3.5 + The function now sleeps at least *secs* even if the sleep is interrupted + by a signal (see :pep:`475` for the rationale). + .. function:: strftime(format[, t]) diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py --- a/Lib/test/eintrdata/eintr_tester.py +++ b/Lib/test/eintrdata/eintr_tester.py @@ -252,8 +252,23 @@ lambda path: os.close(os.open(path, os.O_WRONLY))) + at unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") +class TimeEINTRTest(EINTRBaseTest): + """ EINTR tests for the time module. """ + + def test_sleep(self): + t0 = time.monotonic() + time.sleep(2) + signal.alarm(0) + dt = time.monotonic() - t0 + self.assertGreaterEqual(dt, 1.9) + + def test_main(): - support.run_unittest(OSEINTRTest, SocketEINTRTest) + support.run_unittest( + OSEINTRTest, + SocketEINTRTest, + TimeEINTRTest) if __name__ == "__main__": diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -419,17 +419,20 @@ TIMEOUT_HALF = 5 signal.alarm(1) - before_time = time.time() + # We attempt to get a signal during the sleep, # before select is called - time.sleep(TIMEOUT_FULL) - mid_time = time.time() - dt = mid_time - before_time - if dt >= TIMEOUT_HALF: - raise Exception("%s >= %s" % (dt, TIMEOUT_HALF)) + try: + select.select([], [], [], TIMEOUT_FULL) + except InterruptedError: + pass + else: + raise Exception("select() was not interrupted") + + before_time = time.time() select.select([read], [], [], TIMEOUT_FULL) after_time = time.time() - dt = after_time - mid_time + dt = after_time - before_time if dt >= TIMEOUT_HALF: raise Exception("%s >= %s" % (dt, TIMEOUT_HALF)) """, signal.SIGALRM) diff --git a/Modules/timemodule.c b/Modules/timemodule.c --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1386,74 +1386,94 @@ static int floatsleep(double secs) { -/* XXX Should test for MS_WINDOWS first! */ -#if defined(HAVE_SELECT) && !defined(__EMX__) - struct timeval t; + _PyTime_timeval deadline, monotonic; +#ifndef MS_WINDOWS + struct timeval timeout; double frac; - int err; + int err = 0; - frac = fmod(secs, 1.0); - secs = floor(secs); - t.tv_sec = (long)secs; - t.tv_usec = (long)(frac*1000000.0); - Py_BEGIN_ALLOW_THREADS - err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t); - Py_END_ALLOW_THREADS - if (err != 0) { -#ifdef EINTR - if (errno == EINTR) { - if (PyErr_CheckSignals()) - return -1; - } - else -#endif - { - PyErr_SetFromErrno(PyExc_OSError); + _PyTime_monotonic(&deadline); + _PyTime_ADD_SECONDS(deadline, secs); + + while (1) { + frac = fmod(secs, 1.0); + secs = floor(secs); + timeout.tv_sec = (long)secs; + timeout.tv_usec = (long)(frac*1000000.0); + + Py_BEGIN_ALLOW_THREADS + err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout); + Py_END_ALLOW_THREADS + + if (!(err != 0 && errno == EINTR)) + break; + + /* select() was interrupted by a signal */ + if (PyErr_CheckSignals()) return -1; + + _PyTime_monotonic(&monotonic); + secs = _PyTime_INTERVAL(monotonic, deadline); + if (secs <= 0.0) { + err = 0; + errno = 0; + break; } } -#elif defined(__WATCOMC__) && !defined(__QNX__) - /* XXX Can't interrupt this sleep */ - Py_BEGIN_ALLOW_THREADS - delay((int)(secs * 1000 + 0.5)); /* delay() uses milliseconds */ - Py_END_ALLOW_THREADS -#elif defined(MS_WINDOWS) - { - double millisecs = secs * 1000.0; - unsigned long ul_millis; + if (err != 0) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } +#else + double millisecs; + unsigned long ul_millis; + DWORD rc; + HANDLE hInterruptEvent; + + _PyTime_monotonic(&deadline); + _PyTime_ADD_SECONDS(deadline, secs); + + do { + millisecs = secs * 1000.0; if (millisecs > (double)ULONG_MAX) { PyErr_SetString(PyExc_OverflowError, "sleep length is too large"); return -1; } - Py_BEGIN_ALLOW_THREADS + /* Allow sleep(0) to maintain win32 semantics, and as decreed * by Guido, only the main thread can be interrupted. */ ul_millis = (unsigned long)millisecs; - if (ul_millis == 0 || !_PyOS_IsMainThread()) - Sleep(ul_millis); + if (ul_millis == 0 || !_PyOS_IsMainThread()) { + Py_BEGIN_ALLOW_THREADS + Sleep(0); + Py_END_ALLOW_THREADS + break; + } else { - DWORD rc; - HANDLE hInterruptEvent = _PyOS_SigintEvent(); + hInterruptEvent = _PyOS_SigintEvent(); ResetEvent(hInterruptEvent); + + Py_BEGIN_ALLOW_THREADS rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE); - if (rc == WAIT_OBJECT_0) { - Py_BLOCK_THREADS - errno = EINTR; - PyErr_SetFromErrno(PyExc_OSError); + Py_END_ALLOW_THREADS + + if (rc != WAIT_OBJECT_0) + break; + + /* WaitForSingleObjectEx() was interrupted by SIGINT */ + + if (PyErr_CheckSignals()) return -1; - } + + _PyTime_monotonic(&monotonic); + secs = _PyTime_INTERVAL(monotonic, deadline); + if (secs <= 0.0) + break; } - Py_END_ALLOW_THREADS - } -#else - /* XXX Can't interrupt this sleep */ - Py_BEGIN_ALLOW_THREADS - sleep((int)secs); - Py_END_ALLOW_THREADS + } while (1); #endif - return 0; } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 13:41:45 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 13 Mar 2015 12:41:45 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_Add_PEP_489=3A_Redesigning_ex?= =?utf-8?q?tension_module_loading?= Message-ID: <20150313124145.1853.82186@psf.io> https://hg.python.org/peps/rev/96ab7e74068b changeset: 5726:96ab7e74068b user: Brett Cannon date: Fri Mar 13 08:41:41 2015 -0400 summary: Add PEP 489: Redesigning extension module loading files: pep-0489.txt | 400 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 400 insertions(+), 0 deletions(-) diff --git a/pep-0489.txt b/pep-0489.txt new file mode 100644 --- /dev/null +++ b/pep-0489.txt @@ -0,0 +1,400 @@ +PEP: 489 +Title: Redesigning extension module loading +Version: $Revision$ +Last-Modified: $Date$ +Author: Petr Viktorin , + Stefan Behnel , + Nick Coghlan +Discussions-To: import-sig at python.org +Status: Draft +Type: Standards Track +Content-Type: text/x-rst +Created: 11-Aug-2013 +Python-Version: 3.5 +Post-History: 23-Aug-2013, 20-Feb-2015 +Resolution: + + +Abstract +======== + +This PEP proposes a redesign of the way in which extension modules interact +with the import machinery. This was last revised for Python 3.0 in PEP +3121, but did not solve all problems at the time. The goal is to solve them +by bringing extension modules closer to the way Python modules behave; +specifically to hook into the ModuleSpec-based loading mechanism +introduced in PEP 451. + +Extensions that do not require custom memory layout for their module objects +may be executed in arbitrary pre-defined namespaces, paving the way for +extension modules being runnable with Python's ``-m`` switch. +Other extensions can use custom types for their module implementation. +Module types are no longer restricted to types.ModuleType. + +This proposal makes it easy to support properties at the module +level and to safely store arbitrary global state in the module that is +covered by normal garbage collection and supports reloading and +sub-interpreters. +Extension authors are encouraged to take these issues into account +when using the new API. + + + +Motivation +========== + +Python modules and extension modules are not being set up in the same way. +For Python modules, the module is created and set up first, then the module +code is being executed (PEP 302). +A ModuleSpec object (PEP 451) is used to hold information about the module, +and passed to the relevant hooks. +For extensions, i.e. shared libraries, the module +init function is executed straight away and does both the creation and +initialisation. The initialisation function is not passed ModuleSpec +information about the loaded module, such as the __file__ or fully-qualified +name. This hinders relative imports and resource loading. + +This is specifically a problem for Cython generated modules, for which it's +not uncommon that the module init code has the same level of complexity as +that of any 'regular' Python module. Also, the lack of __file__ and __name__ +information hinders the compilation of __init__.py modules, i.e. packages, +especially when relative imports are being used at module init time. + +The other disadvantage of the discrepancy is that existing Python programmers +learning C cannot effectively map concepts between the two domains. +As long as extension modules are fundamentally different from pure Python ones +in the way they're initialised, they are harder for people to pick up without +relying on something like cffi, SWIG or Cython to handle the actual extension +module creation. + +Currently, extension modules are also not added to sys.modules until they are +fully initialized, which means that a (potentially transitive) +re-import of the module will really try to reimport it and thus run into an +infinite loop when it executes the module init function again. +Without the fully qualified module name, it is not trivial to correctly add +the module to sys.modules either. + +Furthermore, the majority of currently existing extension modules has +problems with sub-interpreter support and/or reloading, and, while it is +possible with the current infrastructure to support these +features, it is neither easy nor efficient. +Addressing these issues was the goal of PEP 3121, but many extensions, +including some in the standard library, took the least-effort approach +to porting to Python 3, leaving these issues unresolved. +This PEP keeps the backwards-compatible behavior, which should reduce pressure +and give extension authors adequate time to consider these issues when porting. + + +The current process +=================== + +Currently, extension modules export an initialisation function named +"PyInit_modulename", named after the file name of the shared library. This +function is executed by the import machinery and must return either NULL in +the case of an exception, or a fully initialised module object. The +function receives no arguments, so it has no way of knowing about its +import context. + +During its execution, the module init function creates a module object +based on a PyModuleDef struct. It then continues to initialise it by adding +attributes to the module dict, creating types, etc. + +In the back, the shared library loader keeps a note of the fully qualified +module name of the last module that it loaded, and when a module gets +created that has a matching name, this global variable is used to determine +the fully qualified name of the module object. This is not entirely safe as it +relies on the module init function creating its own module object first, +but this assumption usually holds in practice. + + +The proposal +============ + +The current extension module initialisation will be deprecated in favour of +a new initialisation scheme. Since the current scheme will continue to be +available, existing code will continue to work unchanged, including binary +compatibility. + +Extension modules that support the new initialisation scheme must export +the public symbol "PyModuleExec_modulename", and optionally +"PyModuleCreate_modulename", where "modulename" is the +name of the module. This mimics the previous naming convention for +the "PyInit_modulename" function. + +If defined, these symbols must resolve to C functions with the following +signatures, respectively:: + + int (*PyModuleExecFunction)(PyObject* module) + PyObject* (*PyModuleCreateFunction)(PyObject* module_spec) + + +The PyModuleExec function +------------------------- + +The PyModuleExec function is used to implement "loader.exec_module" +defined in PEP 451. + +It function will be called to initialize a module. (Usually, this amounts to +setting the module's initial attributes.) +This happens in two situations: when the module is first initialized for +a given (sub-)interpreter, and possibly later when the module is reloaded. + +When PyModuleExec is called, the module has already been added to +sys.modules, and import-related attributes specified in +PEP 451 [#pep-0451-attributes]_) have been set on the module. + +The "module" argument receives the module object to initialize. + +If PyModuleCreate is defined, "module" will generally be the the object +returned by it. +It is possible for a custom loader to pass any object to +PyModuleExec, so this function should check and fail with TypeError +if the module's type is unsupported. +Any other assumptions should also be checked. + +If PyModuleCreate is not defined, PyModuleExec is expected to operate +on any Python object for which attributes can be added by PyObject_GetAttr* +and retrieved by PyObject_SetAttr*. +This allows loading an extension into a pre-created module, making it possible +to run it as __main__ in the future, participate in certain lazy-loading +schemes [#lazy_import_concerns]_, or enable other creative uses. + +If PyModuleExec replaces the module's entry in sys.modules, +the new object will be used and returned by importlib machinery. +(This mirrors the behavior of Python modules. Note that for extensions, +implementing PyModuleCreate is usually a better solution for the use cases +this serves.) + +The function must return ``0`` on success, or, on error, set an exception and +return ``-1``. + + +The PyModuleCreate function +--------------------------- + +The optional PyModuleCreate function is used to implement +"loader.create_module" defined in PEP 451. +By exporting it, an extension module indicates that it uses a custom +module object. +This prevents loading the extension in a pre-created module, +but gives greater flexibility in allowing a custom C-level layout +of the module object. +Most extensions will not need to implement this function. + +The "module_spec" argument receives a "ModuleSpec" instance, as defined in +PEP 451. + +When called, this function must create and return a module object, +or set an exception and return NULL. +There is no requirement for the returned object to be an instance of +types.ModuleType. Any type can be used, as long as it supports setting and +getting attributes, including at least the import-related attributes +specified in PEP 451 [#pep-0451-attributes]_. +This follows the current support for allowing arbitrary objects in sys.modules +and makes it easier for extension modules to define a type that exactly matches +their needs for holding module state. + +Note that when this function is called, the module's entry in sys.modules +is not populated yet. Attempting to import the same module again +(possibly transitively), may lead to an infinite loop. +Extension authors are advised to keep PyModuleCreate minimal, an in particular +to not call user code from it. + +If PyModuleCreate is not defined, the default loader will construct +a module object as if with PyModule_New. + + +Initialization helper functions +------------------------------- + +For two initialization tasks previously done by PyModule_Create, +two functions are introduced:: + + int PyModule_SetDocString(PyObject *m, const char *doc) + int PyModule_AddFunctions(PyObject *m, PyMethodDef *functions) + +These set the module docstring, and add the module functions, respectively. +Both will work on any Python object that supports setting attributes. +They return ``0`` on success, and on failure, they set the exception +and return ``-1``. + + +PyCapsule convenience functions +------------------------------- + +Instead of custom module objects, PyCapsule will become the preferred +mechanism for storing per-module C data. +Two new convenience functions will be added to help with this. + +* + :: + + PyObject *PyModule_AddCapsule( + PyObject *module, + const char *module_name, + const char *attribute_name, + void *pointer, + PyCapsule_Destructor destructor) + + Add a new PyCapsule to *module* as *attribute_name*. + The capsule name is formed by joining *module_name* and *attribute_name* + by a dot. + + This convenience function can be used from a module initialization function + instead of separate calls to PyCapsule_New and PyModule_AddObject. + + Returns a borrowed reference to the new capsule, + or NULL (with exception set) on failure. + +* + :: + + void *PyModule_GetCapsulePointer( + PyObject *module, + const char *module_name, + const char *attribute_name) + + Returns the pointer stored in *module* as *attribute_name*, or NULL + (with an exception set) on failure. The capsule name is formed by joining + *module_name* and *attribute_name* by a dot. + + This convenience function can be used instead of separate calls to + PyObject_GetAttr and PyCapsule_GetPointer. + +Extension authors are encouraged to define a macro to +call PyModule_GetCapsulePointer and cast the result to an appropriate type. + + +Generalizing PyModule_* functions +--------------------------------- + +The following functions and macros will be modified to work on any object +that supports attribute access: + + * PyModule_GetNameObject + * PyModule_GetName + * PyModule_GetFilenameObject + * PyModule_GetFilename + * PyModule_AddIntConstant + * PyModule_AddStringConstant + * PyModule_AddIntMacro + * PyModule_AddStringMacro + * PyModule_AddObject + +The PyModule_GetDict function will continue to only work on true module +objects. This means that it should not be used on extension modules that only +define PyModuleExec. + + +Legacy Init +----------- + +If PyModuleExec is not defined, the import machinery will try to initialize +the module using the PyModuleInit hook, as described in PEP 3121. + +If PyModuleExec is defined, PyModuleInit will be ignored. +Modules requiring compatibility with previous versions of CPython may implement +PyModuleInit in addition to the new hook. + + +Subinterpreters and Interpreter Reloading +----------------------------------------- + +Extensions using the new initialization scheme are expected to support +subinterpreters and multiple Py_Initialize/Py_Finalize cycles correctly. +The mechanism is designed to make this easy, but care is still required +on the part of the extension author. +No user-defined functions, methods, or instances may leak to different +interpreters. +To achieve this, all module-level state should be kept in either the module +dict, or in the module object. +A simple rule of thumb is: Do not define any static data, except built-in types +with no mutable or user-settable class attributes. + + +Module Reloading +---------------- + +Reloading an extension module will re-execute its PyModuleInit function. +Similar caveats apply to reloading an extension module as to reloading +a Python module. Notably, attributes or any other state of the module +are not reset before reloading. + +Additionally, due to limitations in shared library loading (both dlopen on +POSIX and LoadModuleEx on Windows), it is not generally possible to load +a modified library after it has changed on disk. +Therefore, reloading extension modules is of limited use. + + +Multiple modules in one library +------------------------------- + +To support multiple Python modules in one shared library, the library +must export appropriate PyModuleExec_ or PyModuleCreate_ hooks +for each exported module. +The modules are loaded using a ModuleSpec with origin set to the name of the +library file, and name set to the module name. + +Note that this mechanism can currently only be used to *load* such modules, +not to *find* them. + +XXX: This is an existing issue; either fix it/wait for a fix or provide +an example of how to load such modules. + + +Implementation +============== + +XXX - not started + + +Open issues +=========== + +We should expose some kind of API in importlib.util (or a better place?) that +can be used to check that a module works with reloading and subinterpreters. + + +Related issues +============== + +The runpy module will need to be modified to take advantage of PEP 451 +and this PEP. This is out of scope for this PEP. + + +Previous Approaches +=================== + +Stefan Behnel's initial proto-PEP [#stefans_protopep]_ +had a "PyInit_modulename" hook that would create a module class, +whose ``__init__`` would be then called to create the module. +This proposal did not correspond to the (then nonexistent) PEP 451, +where module creation and initialization is broken into distinct steps. +It also did not support loading an extension into pre-existing module objects. + +Nick Coghlan proposed the Create annd Exec hooks, and wrote a prototype +implementation [#nicks-prototype]_. +At this time PEP 451 was still not implemented, so the prototype +does not use ModuleSpec. + + +References +========== + +.. [#lazy_import_concerns] + https://mail.python.org/pipermail/python-dev/2013-August/128129.html + +.. [#pep-0451-attributes] + https://www.python.org/dev/peps/pep-0451/#attributes + +.. [#stefans_protopep] + https://mail.python.org/pipermail/python-dev/2013-August/128087.html + +.. [#nicks-prototype] + https://mail.python.org/pipermail/python-dev/2013-August/128101.html + + +Copyright +========= + +This document has been placed in the public domain. -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Fri Mar 13 14:31:42 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 13 Mar 2015 13:31:42 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_Update_rejected/open_ideas_fo?= =?utf-8?q?r_PEP_488_along_with_minor_grammatical_fixes?= Message-ID: <20150313133140.117997.96962@psf.io> https://hg.python.org/peps/rev/b60b8e93b168 changeset: 5727:b60b8e93b168 user: Brett Cannon date: Fri Mar 13 09:31:36 2015 -0400 summary: Update rejected/open ideas for PEP 488 along with minor grammatical fixes files: pep-0488.txt | 67 +++++++++++++++++++++++++++++++-------- 1 files changed, 53 insertions(+), 14 deletions(-) diff --git a/pep-0488.txt b/pep-0488.txt --- a/pep-0488.txt +++ b/pep-0488.txt @@ -125,7 +125,7 @@ --------- As ``importlib.util.cache_from_source()`` is the API that exposes -bytecode file paths as while as being directly used by importlib, it +bytecode file paths as well as being directly used by importlib, it requires the most critical change. As of Python 3.4, the function's signature is:: @@ -160,7 +160,7 @@ ``None`` argument will mean the same as for ``optimization``). A deprecation warning will be raised when ``debug_override`` is given a value other than ``None``, but there are no plans for the complete -removal of the parameter as this time (but removal will be no later +removal of the parameter at this time (but removal will be no later than Python 4). The various module attributes for importlib.machinery which relate to @@ -183,7 +183,7 @@ ``compileall`` functions will be updated as necessary to make sure they follow the new bytecode file name semantics [6]_, [1]_. The CLI for the ``compileall`` module will not be directly affected (the -``-b`` flag will be implicitly as it will no longer generate ``.pyo`` +``-b`` flag will be implicit as it will no longer generate ``.pyo`` files when ``-O`` is specified). @@ -215,14 +215,23 @@ Rejected Ideas ============== -N/A +Completely dropping optimization levels from CPython +---------------------------------------------------- +Some have suggested that instead of accommodating the various +optimization levels in CPython, we should instead drop them +entirely. The argument is that significant performance gains would +occur from runtime optimizations through something like a JIT and not +through pre-execution bytecode optimizations. -Open Issues -=========== +This idea is rejected for this PEP as that ignores the fact that +there are people who do find the pre-existing optimization levels for +CPython useful. It also assumes that no other Python interpreter +would find what this PEP proposes useful. -Formatting of the optimization level in the file name ------------------------------------------------------ + +Alternative formatting of the optimization level in the file name +----------------------------------------------------------------- Using the "opt-" prefix and placing the optimization level between the cache tag and file extension is not critical. All options which @@ -240,9 +249,35 @@ These were initially rejected either because they would change the sort order of bytecode files, possible ambiguity with the cache tag, -or were not self-documenting enough. +or were not self-documenting enough. An informal poll was taken and +people clearly preferred the formatting proposed by the PEP [9]_. +Since this topic is non-technical and of personal choice, the issue +is considered solved. +Embedding the optimization level in the bytecode metadata +--------------------------------------------------------- + +Some have suggested that rather than embedding the optimization level +of bytecode in the file name that it be included in the file's +metadata instead. This would mean every interpreter had a single copy +of bytecode at any time. Changing the optimization level would thus +require rewriting the bytecode, but there would also only be a single +file to care about. + +This has been rejected due to the fact that Python is often installed +as a root-level application and thus modifying the bytecode file for +modules in the standard library are always possible. In this +situation integrators would need to guess at what a reasonable +optimization level was for users for any/all situations. By +allowing multiple optimization levels to co-exist simultaneously it +frees integrators from having to guess what users want and allows +users to utilize the optimization level they want. + + +Open Issues +=========== + Not specifying the optimization level when it is at 0 ----------------------------------------------------- @@ -260,11 +295,12 @@ interpreter didn't support ``-O`` **and** didn't implement the ast module, else users could implement their own optimizations. -Arguments against allow this special case is "explicit is better than -implicit" and "special cases aren't special enough to break the -rules". There are also currently no Python 3 interpreters that don't -support ``-O``, so a potential Python 3 implementation which doesn't -allow bytecode optimization is entirely theoretical at the moment. +Arguments against allowing this special case is "explicit is better +than implicit" and "special cases aren't special enough to break the +rules". + +At this people have weakly supporting this idea while no one has +explicitly come out against it. References @@ -294,6 +330,9 @@ .. [8] ``importlib.util.MAGIC_NUMBER`` (https://docs.python.org/3/library/importlib.html#importlib.util.MAGIC_NUMBER) +.. [9] Informal poll of file name format options on Google+ + (https://plus.google.com/u/0/+BrettCannon/posts/fZynLNwHWGm) + Copyright ========= -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Fri Mar 13 14:32:08 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 13 Mar 2015 13:32:08 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_Update_post_history_for_PEP_4?= =?utf-8?q?88?= Message-ID: <20150313133208.1849.24268@psf.io> https://hg.python.org/peps/rev/b424e2bfd720 changeset: 5728:b424e2bfd720 user: Brett Cannon date: Fri Mar 13 09:32:05 2015 -0400 summary: Update post history for PEP 488 files: pep-0488.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/pep-0488.txt b/pep-0488.txt --- a/pep-0488.txt +++ b/pep-0488.txt @@ -9,6 +9,7 @@ Created: 20-Feb-2015 Post-History: 2015-03-06 + 2015-03-13 Abstract ======== -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Fri Mar 13 15:42:16 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 13 Mar 2015 14:42:16 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Strip_trailing_whitespace?= Message-ID: <20150313144214.32073.87184@psf.io> https://hg.python.org/cpython/rev/65a2b468fb3c changeset: 94980:65a2b468fb3c user: Brett Cannon date: Fri Mar 13 10:42:08 2015 -0400 summary: Strip trailing whitespace files: Doc/library/zipapp.rst | 15 ++++++++------- 1 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Doc/library/zipapp.rst b/Doc/library/zipapp.rst --- a/Doc/library/zipapp.rst +++ b/Doc/library/zipapp.rst @@ -103,7 +103,7 @@ Create an application archive from *source*. The source can be any of the following: - + * The name of a directory, in which case a new application archive will be created from the content of that directory. * The name of an existing application archive file, in which case the file is @@ -113,10 +113,10 @@ * A file object open for reading in bytes mode. The content of the file should be an application archive, and the file object is assumed to be positioned at the start of the archive. - + The *target* argument determines where the resulting archive will be written: - + * If it is the name of a file, the archive will be written to that file. * If it is an open file object, the archive will be written to that @@ -124,7 +124,7 @@ * If the target is omitted (or None), the source must be a directory and the target will be a file with the same name as the source, with a ``.pyz`` extension added. - + The *interpreter* argument specifies the name of the Python interpreter with which the archive will be executed. It is written as a "shebang" line at the start of the archive. On POSIX, this will be @@ -132,7 +132,7 @@ launcher. Omitting the *interpreter* results in no shebang line being written. If an interpreter is specified, and the target is a filename, the executable bit of the target file will be set. - + The *main* argument specifies the name of a callable which will be used as the main program for the archive. It can only be specified if the source is a directory, and the source does not already contain a @@ -142,10 +142,10 @@ is an error to omit *main* if the source is a directory and does not contain a ``__main__.py`` file, as otherwise the resulting archive would not be executable. - + If a file object is specified for *source* or *target*, it is the caller's responsibility to close it after calling create_archive. - + When copying an existing archive, file objects supplied only need ``read`` and ``readline``, or ``write`` methods. When creating an archive from a directory, if the target is a file object it will be @@ -255,3 +255,4 @@ There is no requirement that the tools in this module are used to create application archives - the module is a convenience, but archives in the above format created by any means are acceptable to Python. + -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 15:42:15 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 13 Mar 2015 14:42:15 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323491=3A_Implemen?= =?utf-8?q?t_PEP_441=3A_Improving_Python_Zip_Application_Support?= Message-ID: <20150313144214.30184.74414@psf.io> https://hg.python.org/cpython/rev/d1e9f337fea1 changeset: 94979:d1e9f337fea1 user: Brett Cannon date: Fri Mar 13 10:40:49 2015 -0400 summary: Issue #23491: Implement PEP 441: Improving Python Zip Application Support Thanks to Paul Moore for the PEP and implementation. files: Doc/library/distribution.rst | 1 + Doc/library/zipapp.rst | 257 ++++++++++++++ Doc/whatsnew/3.5.rst | 21 +- Lib/test/test_zipapp.py | 250 +++++++++++++ Lib/zipapp.py | 179 +++++++++ Tools/msi/launcher/launcher_en-US.wxl | 2 + Tools/msi/launcher/launcher_reg.wxs | 14 + 7 files changed, 720 insertions(+), 4 deletions(-) diff --git a/Doc/library/distribution.rst b/Doc/library/distribution.rst --- a/Doc/library/distribution.rst +++ b/Doc/library/distribution.rst @@ -12,3 +12,4 @@ distutils.rst ensurepip.rst venv.rst + zipapp.rst diff --git a/Doc/library/zipapp.rst b/Doc/library/zipapp.rst new file mode 100644 --- /dev/null +++ b/Doc/library/zipapp.rst @@ -0,0 +1,257 @@ +:mod:`zipapp` --- Manage executable python zip archives +======================================================= + +.. module:: zipapp + :synopsis: Manage executable python zip archives + + +.. index:: + single: Executable Zip Files + +.. versionadded:: 3.5 + +**Source code:** :source:`Lib/zipapp.py` + +-------------- + +This module provides tools to manage the creation of zip files containing +Python code, which can be :ref:`executed directly by the Python interpreter +`. The module provides both a +:ref:`zipapp-command-line-interface` and a :ref:`zipapp-python-api`. + + +Basic Example +------------- + +The following example shows how the :ref:`command-line-interface` +can be used to create an executable archive from a directory containing +Python code. When run, the archive will execute the ``main`` function from +the module ``myapp`` in the archive. + +.. code-block:: sh + + $ python -m zipapp myapp -m "myapp:main" + $ python myapp.pyz + + + +.. _zipapp-command-line-interface: + +Command-Line Interface +---------------------- + +When called as a program from the command line, the following form is used: + +.. code-block:: sh + + $ python -m zipapp source [options] + +If *source* is a directory, this will create an archive from the contents of +*source*. If *source* is a file, it should be an archive, and it will be +copied to the target archive (or the contents of its shebang line will be +displayed if the --info option is specified). + +The following options are understood: + +.. program:: zipapp + +.. cmdoption:: -o , --output= + + Write the output to a file named *output*. If this option is not specified, + the output filename will be the same as the input *source*, with the + extension ``.pyz`` added. If an explicit filename is given, it is used as + is (so a ``.pyz`` extension should be included if required). + + An output filename must be specified if the *source* is an archive (and in + that case, *output* must not be the same as *source*). + +.. cmdoption:: -p , --python= + + Add a ``#!`` line to the archive specifying *interpreter* as the command + to run. Also, on POSIX, make the archive executable. The default is to + write no ``#!`` line, and not make the file executable. + +.. cmdoption:: -m , --main= + + Write a ``__main__.py`` file to the archive that executes *mainfn*. The + *mainfn* argument should have the form "pkg.mod:fn", where "pkg.mod" is a + package/module in the archive, and "fn" is a callable in the given module. + The ``__main__.py`` file will execute that callable. + + :option:`--main` cannot be specified when copying an archive. + +.. cmdoption:: --info + + Display the interpreter embedded in the archive, for diagnostic purposes. In + this case, any other options are ignored and SOURCE must be an archive, not a + directory. + +.. cmdoption:: -h, --help + + Print a short usage message and exit. + + +.. _zipapp-python-api: + +Python API +---------- + +The module defines two convenience functions: + + +.. function:: create_archive(source, target=None, interpreter=None, main=None) + + Create an application archive from *source*. The source can be any + of the following: + + * The name of a directory, in which case a new application archive + will be created from the content of that directory. + * The name of an existing application archive file, in which case the file is + copied to the target (modifying it to reflect the value given for the + *interpreter* argument). The file name should include the ``.pyz`` + extension, if required. + * A file object open for reading in bytes mode. The content of the + file should be an application archive, and the file object is + assumed to be positioned at the start of the archive. + + The *target* argument determines where the resulting archive will be + written: + + * If it is the name of a file, the archive will be written to that + file. + * If it is an open file object, the archive will be written to that + file object, which must be open for writing in bytes mode. + * If the target is omitted (or None), the source must be a directory + and the target will be a file with the same name as the source, with + a ``.pyz`` extension added. + + The *interpreter* argument specifies the name of the Python + interpreter with which the archive will be executed. It is written as + a "shebang" line at the start of the archive. On POSIX, this will be + interpreted by the OS, and on Windows it will be handled by the Python + launcher. Omitting the *interpreter* results in no shebang line being + written. If an interpreter is specified, and the target is a + filename, the executable bit of the target file will be set. + + The *main* argument specifies the name of a callable which will be + used as the main program for the archive. It can only be specified if + the source is a directory, and the source does not already contain a + ``__main__.py`` file. The *main* argument should take the form + "pkg.module:callable" and the archive will be run by importing + "pkg.module" and executing the given callable with no arguments. It + is an error to omit *main* if the source is a directory and does not + contain a ``__main__.py`` file, as otherwise the resulting archive + would not be executable. + + If a file object is specified for *source* or *target*, it is the + caller's responsibility to close it after calling create_archive. + + When copying an existing archive, file objects supplied only need + ``read`` and ``readline``, or ``write`` methods. When creating an + archive from a directory, if the target is a file object it will be + passed to the ``zipfile.ZipFile`` class, and must supply the methods + needed by that class. + +.. function:: get_interpreter(archive) + + Return the interpreter specified in the ``#!`` line at the start of the + archive. If there is no ``#!`` line, return :const:`None`. + The *archive* argument can be a filename or a file-like object open + for reading in bytes mode. It is assumed to be at the start of the archive. + + +.. _zipapp-examples: + +Examples +-------- + +Pack up a directory into an archive, and run it. + +.. code-block:: sh + + $ python -m zipapp myapp + $ python myapp.pyz + + +The same can be done using the :func:`create_archive` functon:: + + >>> import zipapp + >>> zipapp.create_archive('myapp.pyz', 'myapp') + +To make the application directly executable on POSIX, specify an interpreter +to use. + +.. code-block:: sh + + $ python -m zipapp myapp -p "/usr/bin/env python" + $ ./myapp.pyz + + +To replace the shebang line on an existing archive, create a modified archive +using the :func:`create_archive` function:: + + >>> import zipapp + >>> zipapp.create_archive('old_archive.pyz', 'new_archive.pyz', '/usr/bin/python3') + +To update the file in place, do the replacement in memory using a :class:`BytesIO` +object, and then overwrite the source afterwards. Note that there is a risk +when overwriting a file in place that an error will result in the loss of +the original file. This code does not protect against such errors, but +production code should do so. Also, this method will only work if the archive +fits in memory:: + + >>> import zipapp + >>> import io + >>> temp = io.BytesIO() + >>> zipapp.create_archive('myapp.pyz', temp, '/usr/bin/python2') + >>> with open('myapp.pyz', 'wb') as f: + >>> f.write(temp.getvalue()) + +Note that if you specify an interpreter and then distribute your application +archive, you need to ensure that the interpreter used is portable. The Python +launcher for Windows supports most common forms of POSIX ``#!`` line, but there +are other issues to consider: + +* If you use "/usr/bin/env python" (or other forms of the "python" command, + such as "/usr/bin/python"), you need to consider that your users may have + either Python 2 or Python 3 as their default, and write your code to work + under both versions. +* If you use an explicit version, for example "/usr/bin/env python3" your + application will not work for users who do not have that version. (This + may be what you want if you have not made your code Python 2 compatible). +* There is no way to say "python X.Y or later", so be careful of using an + exact version like "/usr/bin/env python3.4" as you will need to change your + shebang line for users of Python 3.5, for example. + +The Python Zip Application Archive Format +----------------------------------------- + +Python has been able to execute zip files which contain a ``__main__.py`` file +since version 2.6. In order to be executed by Python, an application archive +simply has to be a standard zip file containing a ``__main__.py`` file which +will be run as the entry point for the application. As usual for any Python +script, the parent of the script (in this case the zip file) will be placed on +:data:`sys.path` and thus further modules can be imported from the zip file. + +The zip file format allows arbitrary data to be prepended to a zip file. The +zip application format uses this ability to prepend a standard POSIX "shebang" +line to the file (``#!/path/to/interpreter``). + +Formally, the Python zip application format is therefore: + +1. An optional shebang line, containing the characters ``b'#!'`` followed by an + interpreter name, and then a newline (``b'\n'``) character. The interpreter + name can be anything acceptable to the OS "shebang" processing, or the Python + launcher on Windows. The interpreter should be encoded in UTF-8 on Windows, + and in :func:`sys.getfilesystemencoding()` on POSIX. +2. Standard zipfile data, as generated by the :mod:`zipfile` module. The + zipfile content *must* include a file called ``__main__.py`` (which must be + in the "root" of the zipfile - i.e., it cannot be in a subdirectory). The + zipfile data can be compressed or uncompressed. + +If an application archive has a shebang line, it may have the executable bit set +on POSIX systems, to allow it to be executed directly. + +There is no requirement that the tools in this module are used to create +application archives - the module is a convenience, but archives in the above +format created by any means are acceptable to Python. 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 @@ -71,7 +71,8 @@ New library modules: -* None yet. +* :mod:`zipapp`: :ref:`Improving Python ZIP Application Support + ` (:pep:`441`). New built-in features: @@ -166,10 +167,22 @@ New Modules =========== -.. module name -.. ----------- +.. _whatsnew-zipapp: -* None yet. +zipapp +------ + +The new :mod:`zipapp` module (specified in :pep:`441`) provides an API and +command line tool for creating executable Python Zip Applications, which +were introduced in Python 2.6 in :issue:`1739468` but which were not well +publicised, either at the time or since. + +With the new module, bundling your application is as simple as putting all +the files, including a ``__main__.py`` file, into a directory ``myapp`` +and running:: + + $ python -m zipapp myapp + $ python myapp.pyz Improved Modules diff --git a/Lib/test/test_zipapp.py b/Lib/test/test_zipapp.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_zipapp.py @@ -0,0 +1,250 @@ +"""Test harness for the zipapp module.""" + +import io +import pathlib +import stat +import sys +import tempfile +import unittest +import zipapp +import zipfile + + +class ZipAppTest(unittest.TestCase): + + """Test zipapp module functionality.""" + + def setUp(self): + tmpdir = tempfile.TemporaryDirectory() + self.addCleanup(tmpdir.cleanup) + self.tmpdir = pathlib.Path(tmpdir.name) + + def test_create_archive(self): + # Test packing a directory. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target)) + self.assertTrue(target.is_file()) + + def test_create_archive_with_subdirs(self): + # Test packing a directory includes entries for subdirectories. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + (source / 'foo').mkdir() + (source / 'bar').mkdir() + (source / 'foo' / '__init__.py').touch() + target = io.BytesIO() + zipapp.create_archive(str(source), target) + target.seek(0) + with zipfile.ZipFile(target, 'r') as z: + self.assertIn('foo/', z.namelist()) + self.assertIn('bar/', z.namelist()) + + def test_create_archive_default_target(self): + # Test packing a directory to the default name. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + zipapp.create_archive(str(source)) + expected_target = self.tmpdir / 'source.pyz' + self.assertTrue(expected_target.is_file()) + + def test_no_main(self): + # Test that packing a directory with no __main__.py fails. + source = self.tmpdir / 'source' + source.mkdir() + (source / 'foo.py').touch() + target = self.tmpdir / 'source.pyz' + with self.assertRaises(zipapp.ZipAppError): + zipapp.create_archive(str(source), str(target)) + + def test_main_and_main_py(self): + # Test that supplying a main argument with __main__.py fails. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + with self.assertRaises(zipapp.ZipAppError): + zipapp.create_archive(str(source), str(target), main='pkg.mod:fn') + + def test_main_written(self): + # Test that the __main__.py is written correctly. + source = self.tmpdir / 'source' + source.mkdir() + (source / 'foo.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target), main='pkg.mod:fn') + with zipfile.ZipFile(str(target), 'r') as z: + self.assertIn('__main__.py', z.namelist()) + self.assertIn(b'pkg.mod.fn()', z.read('__main__.py')) + + def test_main_only_written_once(self): + # Test that we don't write multiple __main__.py files. + # The initial implementation had this bug; zip files allow + # multiple entries with the same name + source = self.tmpdir / 'source' + source.mkdir() + # Write 2 files, as the original bug wrote __main__.py + # once for each file written :-( + # See http://bugs.python.org/review/23491/diff/13982/Lib/zipapp.py#newcode67Lib/zipapp.py:67 + # (line 67) + (source / 'foo.py').touch() + (source / 'bar.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target), main='pkg.mod:fn') + with zipfile.ZipFile(str(target), 'r') as z: + self.assertEqual(1, z.namelist().count('__main__.py')) + + def test_main_validation(self): + # Test that invalid values for main are rejected. + source = self.tmpdir / 'source' + source.mkdir() + target = self.tmpdir / 'source.pyz' + problems = [ + '', 'foo', 'foo:', ':bar', '12:bar', 'a.b.c.:d', + '.a:b', 'a:b.', 'a:.b', 'a:silly name' + ] + for main in problems: + with self.subTest(main=main): + with self.assertRaises(zipapp.ZipAppError): + zipapp.create_archive(str(source), str(target), main=main) + + def test_default_no_shebang(self): + # Test that no shebang line is written to the target by default. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target)) + with target.open('rb') as f: + self.assertNotEqual(f.read(2), b'#!') + + def test_custom_interpreter(self): + # Test that a shebang line with a custom interpreter is written + # correctly. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target), interpreter='python') + with target.open('rb') as f: + self.assertEqual(f.read(2), b'#!') + self.assertEqual(b'python\n', f.readline()) + + def test_pack_to_fileobj(self): + # Test that we can pack to a file object. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = io.BytesIO() + zipapp.create_archive(str(source), target, interpreter='python') + self.assertTrue(target.getvalue().startswith(b'#!python\n')) + + def test_read_shebang(self): + # Test that we can read the shebang line correctly. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target), interpreter='python') + self.assertEqual(zipapp.get_interpreter(str(target)), 'python') + + def test_read_missing_shebang(self): + # Test that reading the shebang line of a file without one returns None. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target)) + self.assertEqual(zipapp.get_interpreter(str(target)), None) + + def test_modify_shebang(self): + # Test that we can change the shebang of a file. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target), interpreter='python') + new_target = self.tmpdir / 'changed.pyz' + zipapp.create_archive(str(target), str(new_target), interpreter='python2.7') + self.assertEqual(zipapp.get_interpreter(str(new_target)), 'python2.7') + + def test_write_shebang_to_fileobj(self): + # Test that we can change the shebang of a file, writing the result to a + # file object. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target), interpreter='python') + new_target = io.BytesIO() + zipapp.create_archive(str(target), new_target, interpreter='python2.7') + self.assertTrue(new_target.getvalue().startswith(b'#!python2.7\n')) + + def test_read_from_fileobj(self): + # Test that we can copy an archive using an open file object. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + temp_archive = io.BytesIO() + zipapp.create_archive(str(source), temp_archive, interpreter='python') + new_target = io.BytesIO() + temp_archive.seek(0) + zipapp.create_archive(temp_archive, new_target, interpreter='python2.7') + self.assertTrue(new_target.getvalue().startswith(b'#!python2.7\n')) + + def test_remove_shebang(self): + # Test that we can remove the shebang from a file. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target), interpreter='python') + new_target = self.tmpdir / 'changed.pyz' + zipapp.create_archive(str(target), str(new_target), interpreter=None) + self.assertEqual(zipapp.get_interpreter(str(new_target)), None) + + def test_content_of_copied_archive(self): + # Test that copying an archive doesn't corrupt it. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = io.BytesIO() + zipapp.create_archive(str(source), target, interpreter='python') + new_target = io.BytesIO() + target.seek(0) + zipapp.create_archive(target, new_target, interpreter=None) + new_target.seek(0) + with zipfile.ZipFile(new_target, 'r') as z: + self.assertEqual(set(z.namelist()), {'__main__.py'}) + + # (Unix only) tests that archives with shebang lines are made executable + @unittest.skipIf(sys.platform == 'win32', + 'Windows does not support an executable bit') + def test_shebang_is_executable(self): + # Test that an archive with a shebang line is made executable. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target), interpreter='python') + self.assertTrue(target.stat().st_mode & stat.S_IEXEC) + + @unittest.skipIf(sys.platform == 'win32', + 'Windows does not support an executable bit') + def test_no_shebang_is_not_executable(self): + # Test that an archive with no shebang line is not made executable. + source = self.tmpdir / 'source' + source.mkdir() + (source / '__main__.py').touch() + target = self.tmpdir / 'source.pyz' + zipapp.create_archive(str(source), str(target), interpreter=None) + self.assertFalse(target.stat().st_mode & stat.S_IEXEC) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/zipapp.py b/Lib/zipapp.py new file mode 100644 --- /dev/null +++ b/Lib/zipapp.py @@ -0,0 +1,179 @@ +import contextlib +import os +import pathlib +import shutil +import stat +import sys +import zipfile + +__all__ = ['ZipAppError', 'create_archive', 'get_interpreter'] + + +# The __main__.py used if the users specifies "-m module:fn". +# Note that this will always be written as UTF-8 (module and +# function names can be non-ASCII in Python 3). +# We add a coding cookie even though UTF-8 is the default in Python 3 +# because the resulting archive may be intended to be run under Python 2. +MAIN_TEMPLATE = """\ +# -*- coding: utf-8 -*- +import {module} +{module}.{fn}() +""" + + +# The Windows launcher defaults to UTF-8 when parsing shebang lines if the +# file has no BOM. So use UTF-8 on Windows. +# On Unix, use the filesystem encoding. +if sys.platform.startswith('win'): + shebang_encoding = 'utf-8' +else: + shebang_encoding = sys.getfilesystemencoding() + + +class ZipAppError(ValueError): + pass + + + at contextlib.contextmanager +def _maybe_open(archive, mode): + if isinstance(archive, str): + with open(archive, mode) as f: + yield f + else: + yield archive + + +def _write_file_prefix(f, interpreter): + """Write a shebang line.""" + if interpreter: + shebang = b'#!%b\n' % (interpreter.encode(shebang_encoding),) + f.write(shebang) + + +def _copy_archive(archive, new_archive, interpreter=None): + """Copy an application archive, modifying the shebang line.""" + with _maybe_open(archive, 'rb') as src: + # Skip the shebang line from the source. + # Read 2 bytes of the source and check if they are #!. + first_2 = src.read(2) + if first_2 == b'#!': + # Discard the initial 2 bytes and the rest of the shebang line. + first_2 = b'' + src.readline() + + with _maybe_open(new_archive, 'wb') as dst: + _write_file_prefix(dst, interpreter) + # If there was no shebang, "first_2" contains the first 2 bytes + # of the source file, so write them before copying the rest + # of the file. + dst.write(first_2) + shutil.copyfileobj(src, dst) + + if interpreter and isinstance(new_archive, str): + os.chmod(new_archive, os.stat(new_archive).st_mode | stat.S_IEXEC) + + +def create_archive(source, target=None, interpreter=None, main=None): + """Create an application archive from SOURCE. + + The SOURCE can be the name of a directory, or a filename or a file-like + object referring to an existing archive. + + The content of SOURCE is packed into an application archive in TARGET, + which can be a filename or a file-like object. If SOURCE is a directory, + TARGET can be omitted and will default to the name of SOURCE with .pyz + appended. + + The created application archive will have a shebang line specifying + that it should run with INTERPRETER (there will be no shebang line if + INTERPRETER is None), and a __main__.py which runs MAIN (if MAIN is + not specified, an existing __main__.py will be used). It is an to specify + MAIN for anything other than a directory source with no __main__.py, and it + is an error to omit MAIN if the directory has no __main__.py. + """ + # Are we copying an existing archive? + if not (isinstance(source, str) and os.path.isdir(source)): + _copy_archive(source, target, interpreter) + return + + # We are creating a new archive from a directory + has_main = os.path.exists(os.path.join(source, '__main__.py')) + if main and has_main: + raise ZipAppError( + "Cannot specify entry point if the source has __main__.py") + if not (main or has_main): + raise ZipAppError("Archive has no entry point") + + main_py = None + if main: + # Check that main has the right format + mod, sep, fn = main.partition(':') + mod_ok = all(part.isidentifier() for part in mod.split('.')) + fn_ok = all(part.isidentifier() for part in fn.split('.')) + if not (sep == ':' and mod_ok and fn_ok): + raise ZipAppError("Invalid entry point: " + main) + main_py = MAIN_TEMPLATE.format(module=mod, fn=fn) + + if target is None: + target = source + '.pyz' + + with _maybe_open(target, 'wb') as fd: + _write_file_prefix(fd, interpreter) + with zipfile.ZipFile(fd, 'w') as z: + root = pathlib.Path(source) + for child in root.rglob('*'): + arcname = str(child.relative_to(root)) + z.write(str(child), arcname) + if main_py: + z.writestr('__main__.py', main_py.encode('utf-8')) + + if interpreter and isinstance(target, str): + os.chmod(target, os.stat(target).st_mode | stat.S_IEXEC) + + +def get_interpreter(archive): + with _maybe_open(archive, 'rb') as f: + if f.read(2) == b'#!': + return f.readline().strip().decode(shebang_encoding) + + +def main(): + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument('--output', '-o', default=None, + help="The name of the output archive. " + "Required if SOURCE is an archive.") + parser.add_argument('--python', '-p', default=None, + help="The name of the Python interpreter to use " + "(default: no shebang line).") + parser.add_argument('--main', '-m', default=None, + help="The main function of the application " + "(default: use an existing __main__.py).") + parser.add_argument('--info', default=False, action='store_true', + help="Display the interpreter from the archive.") + parser.add_argument('source', + help="Source directory (or existing archive).") + + args = parser.parse_args() + + # Handle `python -m zipapp archive.pyz --info`. + if args.info: + if not os.path.isfile(args.source): + raise SystemExit("Can only get info for an archive file") + interpreter = get_interpreter(args.source) + print("Interpreter: {}".format(interpreter or "")) + sys.exit(0) + + if os.path.isfile(args.source): + if args.output is None or os.path.samefile(args.source, args.output): + raise SystemExit("In-place editing of archives is not supported") + if args.main: + raise SystemExit("Cannot change the main function when copying") + + create_archive(args.source, args.output, + interpreter=args.python, main=args.main) + + +if __name__ == '__main__': + main() diff --git a/Tools/msi/launcher/launcher_en-US.wxl b/Tools/msi/launcher/launcher_en-US.wxl --- a/Tools/msi/launcher/launcher_en-US.wxl +++ b/Tools/msi/launcher/launcher_en-US.wxl @@ -5,4 +5,6 @@ Python File Python File (no console) Compiled Python File + Python Zip Application File + Python Zip Application File (no console) diff --git a/Tools/msi/launcher/launcher_reg.wxs b/Tools/msi/launcher/launcher_reg.wxs --- a/Tools/msi/launcher/launcher_reg.wxs +++ b/Tools/msi/launcher/launcher_reg.wxs @@ -26,6 +26,20 @@ + + + + + + + + + + + + + + -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 16:13:25 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 13 Mar 2015 15:13:25 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Add_some_periods_to_the_en?= =?utf-8?q?ds_of_a_couple_comments?= Message-ID: <20150313151325.30086.83092@psf.io> https://hg.python.org/cpython/rev/e352f78aa012 changeset: 94981:e352f78aa012 user: Brett Cannon date: Fri Mar 13 11:13:20 2015 -0400 summary: Add some periods to the ends of a couple comments files: Lib/zipapp.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/zipapp.py b/Lib/zipapp.py --- a/Lib/zipapp.py +++ b/Lib/zipapp.py @@ -96,7 +96,7 @@ _copy_archive(source, target, interpreter) return - # We are creating a new archive from a directory + # We are creating a new archive from a directory. has_main = os.path.exists(os.path.join(source, '__main__.py')) if main and has_main: raise ZipAppError( @@ -106,7 +106,7 @@ main_py = None if main: - # Check that main has the right format + # Check that main has the right format. mod, sep, fn = main.partition(':') mod_ok = all(part.isidentifier() for part in mod.split('.')) fn_ok = all(part.isidentifier() for part in fn.split('.')) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 16:18:03 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 13 Mar 2015 15:18:03 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_Mark_PEPs_441_and_486_final?= Message-ID: <20150313151730.70282.44804@psf.io> https://hg.python.org/peps/rev/1ce7ff1449d7 changeset: 5729:1ce7ff1449d7 user: Brett Cannon date: Fri Mar 13 11:17:26 2015 -0400 summary: Mark PEPs 441 and 486 final files: pep-0441.txt | 2 +- pep-0486.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pep-0441.txt b/pep-0441.txt --- a/pep-0441.txt +++ b/pep-0441.txt @@ -5,7 +5,7 @@ Author: Daniel Holth , Paul Moore Discussions-To: https://mail.python.org/pipermail/python-dev/2015-February/138277.html -Status: Accepted +Status: Final Type: Standards Track Content-Type: text/x-rst Created: 30 March 2013 diff --git a/pep-0486.txt b/pep-0486.txt --- a/pep-0486.txt +++ b/pep-0486.txt @@ -3,7 +3,7 @@ Version: $Revision$ Last-Modified: $Date$ Author: Paul Moore -Status: Accepted +Status: Final Type: Standards Track Content-Type: text/x-rst Created: 12-Feb-2015 -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Fri Mar 13 17:50:22 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 13 Mar 2015 16:50:22 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Make_the_case_?= =?utf-8?q?to_only_support_Python_2=2E7_when_supporting_2/3_simultaneously?= Message-ID: <20150313165022.117997.23552@psf.io> https://hg.python.org/cpython/rev/cde032ab4e17 changeset: 94982:cde032ab4e17 branch: 3.4 parent: 94975:c1abcbcfefab user: Brett Cannon date: Fri Mar 13 12:49:44 2015 -0400 summary: Make the case to only support Python 2.7 when supporting 2/3 simultaneously files: Doc/howto/pyporting.rst | 26 +++++++++++++------------- 1 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Doc/howto/pyporting.rst b/Doc/howto/pyporting.rst --- a/Doc/howto/pyporting.rst +++ b/Doc/howto/pyporting.rst @@ -28,15 +28,14 @@ To make your project be single-source Python 2/3 compatible, the basic steps are: -#. Update your code to drop support for Python 2.5 or older (supporting only - Python 2.7 is ideal) +#. Only worry about supporting Python 2.7 #. Make sure you have good test coverage (coverage.py_ can help; ``pip install coverage``) #. Learn the differences between Python 2 & 3 #. Use Modernize_ or Futurize_ to update your code (``pip install modernize`` or ``pip install future``, respectively) #. Use Pylint_ to help make sure you don't regress on your Python 3 support - (if only supporting Python 2.7/3.4 or newer; ``pip install pylint``) + (``pip install pylint``) #. Use caniusepython3_ to find out which of your dependencies are blocking your use of Python 3 (``pip install caniusepython3``) #. Once your dependencies are no longer blocking you, use continuous integration @@ -67,26 +66,27 @@ your code to support Python 2 & 3 simultaneously. -Drop support for Python 2.5 and older (at least) ------------------------------------------------- +Drop support for Python 2.6 and older +------------------------------------- While you can make Python 2.5 work with Python 3, it is **much** easier if you -only have to work with Python 2.6 or newer (and easier still if you only have -to work with Python 2.7). If dropping Python 2.5 is not an option then the six_ -project can help you support Python 2.5 & 3 simultaneously +only have to work with Python 2.7. If dropping Python 2.5 is not an +option then the six_ project can help you support Python 2.5 & 3 simultaneously (``pip install six``). Do realize, though, that nearly all the projects listed in this HOWTO will not be available to you. -If you are able to only support Python 2.6 or newer, then the required changes +If you are able to skip Python 2.5 and older, then the required changes to your code should continue to look and feel like idiomatic Python code. At worst you will have to use a function instead of a method in some instances or have to import a function instead of using a built-in one, but otherwise the overall transformation should not feel foreign to you. -But please aim for Python 2.7. Bugfixes for that version of Python will continue -until 2020 while Python 2.6 is no longer supported. There are also some tools -mentioned in this HOWTO which do not support Python 2.6 (e.g., Pylint_), and -this will become more commonplace as time goes on. +But you should aim for only supporting Python 2.7. Python 2.6 is no longer +supported and thus is not receiving bugfixes. This means **you** will have to +work around any issues you come across with Python 2.6. There are also some +tools mentioned in this HOWTO which do not support Python 2.6 (e.g., Pylint_), +and this will become more commonplace as time goes on. It will simply be easier +for you if you only support the versions of Python that you have to support. Make sure you specify the proper version support in your ``setup.py`` file -------------------------------------------------------------------------- -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 17:50:22 2015 From: python-checkins at python.org (brett.cannon) Date: Fri, 13 Mar 2015 16:50:22 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogTWVyZ2UgMy40?= Message-ID: <20150313165022.1833.67204@psf.io> https://hg.python.org/cpython/rev/d33a43154794 changeset: 94983:d33a43154794 parent: 94981:e352f78aa012 parent: 94982:cde032ab4e17 user: Brett Cannon date: Fri Mar 13 12:50:16 2015 -0400 summary: Merge 3.4 files: Doc/howto/pyporting.rst | 26 +++++++++++++------------- 1 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Doc/howto/pyporting.rst b/Doc/howto/pyporting.rst --- a/Doc/howto/pyporting.rst +++ b/Doc/howto/pyporting.rst @@ -28,15 +28,14 @@ To make your project be single-source Python 2/3 compatible, the basic steps are: -#. Update your code to drop support for Python 2.5 or older (supporting only - Python 2.7 is ideal) +#. Only worry about supporting Python 2.7 #. Make sure you have good test coverage (coverage.py_ can help; ``pip install coverage``) #. Learn the differences between Python 2 & 3 #. Use Modernize_ or Futurize_ to update your code (``pip install modernize`` or ``pip install future``, respectively) #. Use Pylint_ to help make sure you don't regress on your Python 3 support - (if only supporting Python 2.7/3.4 or newer; ``pip install pylint``) + (``pip install pylint``) #. Use caniusepython3_ to find out which of your dependencies are blocking your use of Python 3 (``pip install caniusepython3``) #. Once your dependencies are no longer blocking you, use continuous integration @@ -67,26 +66,27 @@ your code to support Python 2 & 3 simultaneously. -Drop support for Python 2.5 and older (at least) ------------------------------------------------- +Drop support for Python 2.6 and older +------------------------------------- While you can make Python 2.5 work with Python 3, it is **much** easier if you -only have to work with Python 2.6 or newer (and easier still if you only have -to work with Python 2.7). If dropping Python 2.5 is not an option then the six_ -project can help you support Python 2.5 & 3 simultaneously +only have to work with Python 2.7. If dropping Python 2.5 is not an +option then the six_ project can help you support Python 2.5 & 3 simultaneously (``pip install six``). Do realize, though, that nearly all the projects listed in this HOWTO will not be available to you. -If you are able to only support Python 2.6 or newer, then the required changes +If you are able to skip Python 2.5 and older, then the required changes to your code should continue to look and feel like idiomatic Python code. At worst you will have to use a function instead of a method in some instances or have to import a function instead of using a built-in one, but otherwise the overall transformation should not feel foreign to you. -But please aim for Python 2.7. Bugfixes for that version of Python will continue -until 2020 while Python 2.6 is no longer supported. There are also some tools -mentioned in this HOWTO which do not support Python 2.6 (e.g., Pylint_), and -this will become more commonplace as time goes on. +But you should aim for only supporting Python 2.7. Python 2.6 is no longer +supported and thus is not receiving bugfixes. This means **you** will have to +work around any issues you come across with Python 2.6. There are also some +tools mentioned in this HOWTO which do not support Python 2.6 (e.g., Pylint_), +and this will become more commonplace as time goes on. It will simply be easier +for you if you only support the versions of Python that you have to support. Make sure you specify the proper version support in your ``setup.py`` file -------------------------------------------------------------------------- -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Fri Mar 13 19:09:45 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 13 Mar 2015 19:09:45 +0100 Subject: [Python-checkins] Daily reference leaks (471d16b286db): sum=3 Message-ID: results for 471d16b286db 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/reflogBp1Kzx', '--timeout', '7200', '-x', 'test_eintr'] From python-checkins at python.org Fri Mar 13 20:32:50 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 13 Mar 2015 19:32:50 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_the_default_is?= =?utf-8?q?_sys=2Emaxsize_not_sys=2Emaxint_=28closes_=2323645=29?= Message-ID: <20150313193236.13225.62118@psf.io> https://hg.python.org/cpython/rev/3b91d834160f changeset: 94984:3b91d834160f branch: 2.7 parent: 94974:44c1c0cbdc06 user: Benjamin Peterson date: Fri Mar 13 14:32:31 2015 -0500 summary: the default is sys.maxsize not sys.maxint (closes #23645) files: Doc/reference/datamodel.rst | 18 +++++++++--------- 1 files changed, 9 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 @@ -1993,15 +1993,15 @@ :meth:`__getslice__`. Therefore, you have to override it in derived classes when implementing slicing.) - Called to implement evaluation of ``self[i:j]``. The returned object should be - of the same type as *self*. Note that missing *i* or *j* in the slice - expression are replaced by zero or ``sys.maxint``, respectively. If negative - indexes are used in the slice, the length of the sequence is added to that - index. If the instance does not implement the :meth:`__len__` method, an - :exc:`AttributeError` is raised. No guarantee is made that indexes adjusted this - way are not still negative. Indexes which are greater than the length of the - sequence are not modified. If no :meth:`__getslice__` is found, a slice object - is created instead, and passed to :meth:`__getitem__` instead. + Called to implement evaluation of ``self[i:j]``. The returned object should + be of the same type as *self*. Note that missing *i* or *j* in the slice + expression are replaced by zero or :attr:`sys.maxsize`, respectively. If + negative indexes are used in the slice, the length of the sequence is added + to that index. If the instance does not implement the :meth:`__len__` method, + an :exc:`AttributeError` is raised. No guarantee is made that indexes + adjusted this way are not still negative. Indexes which are greater than the + length of the sequence are not modified. If no :meth:`__getslice__` is found, + a slice object is created instead, and passed to :meth:`__getitem__` instead. .. method:: object.__setslice__(self, i, j, sequence) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 22:21:47 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 13 Mar 2015 21:21:47 +0000 Subject: [Python-checkins] =?utf-8?b?Y3B5dGhvbiAoMy40KTogX19nZXRzbGljZV9f?= =?utf-8?q?_certainly_won=27t_appear_in_the_output?= Message-ID: <20150313212147.30072.44555@psf.io> https://hg.python.org/cpython/rev/c9650423c336 changeset: 94985:c9650423c336 branch: 3.4 parent: 94982:cde032ab4e17 user: Benjamin Peterson date: Fri Mar 13 16:21:23 2015 -0500 summary: __getslice__ certainly won't appear in the output files: Doc/library/modulefinder.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/modulefinder.rst b/Doc/library/modulefinder.rst --- a/Doc/library/modulefinder.rst +++ b/Doc/library/modulefinder.rst @@ -104,7 +104,7 @@ re: __module__,finditer,_expand itertools: __main__: re,itertools,baconhameggs - sre_parse: __getslice__,_PATTERNENDERS,SRE_FLAG_UNICODE + sre_parse: _PATTERNENDERS,SRE_FLAG_UNICODE array: types: __module__,IntType,TypeType --------------------------------------------------- -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Fri Mar 13 22:21:47 2015 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 13 Mar 2015 21:21:47 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?b?KTogbWVyZ2UgMy40?= Message-ID: <20150313212147.86371.18672@psf.io> https://hg.python.org/cpython/rev/7e86b296deeb changeset: 94986:7e86b296deeb parent: 94983:d33a43154794 parent: 94985:c9650423c336 user: Benjamin Peterson date: Fri Mar 13 16:21:29 2015 -0500 summary: merge 3.4 files: Doc/library/modulefinder.rst | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Doc/library/modulefinder.rst b/Doc/library/modulefinder.rst --- a/Doc/library/modulefinder.rst +++ b/Doc/library/modulefinder.rst @@ -104,7 +104,7 @@ re: __module__,finditer,_expand itertools: __main__: re,itertools,baconhameggs - sre_parse: __getslice__,_PATTERNENDERS,SRE_FLAG_UNICODE + sre_parse: _PATTERNENDERS,SRE_FLAG_UNICODE array: types: __module__,IntType,TypeType --------------------------------------------------- -- Repository URL: https://hg.python.org/cpython From solipsis at pitrou.net Sat Mar 14 19:23:08 2015 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 14 Mar 2015 19:23:08 +0100 Subject: [Python-checkins] Daily reference leaks (7e86b296deeb): sum=61 Message-ID: results for 7e86b296deeb on branch "default" -------------------------------------------- test_asyncio leaked [0, 3, 0] memory blocks, sum=3 test_functools leaked [0, 0, 3] memory blocks, sum=3 test_multiprocessing_fork leaked [38, 0, 0] references, sum=38 test_multiprocessing_fork leaked [17, 0, 0] memory blocks, sum=17 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/cpython/refleaks/reflog23jpm8', '--timeout', '7200', '-x', 'test_eintr'] From python-checkins at python.org Sat Mar 14 19:40:12 2015 From: python-checkins at python.org (steve.dower) Date: Sat, 14 Mar 2015 18:40:12 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Fixes_incorrect_use_of_GetLastError_where_errno_should_b?= =?utf-8?q?e_used=2E?= Message-ID: <20150314184011.82212.85529@psf.io> https://hg.python.org/cpython/rev/4b0d12f0d8fa changeset: 94988:4b0d12f0d8fa parent: 94986:7e86b296deeb parent: 94987:6409b4048b10 user: Steve Dower date: Sat Mar 14 11:39:18 2015 -0700 summary: Fixes incorrect use of GetLastError where errno should be used. files: Python/fileutils.c | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Python/fileutils.c b/Python/fileutils.c --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -733,7 +733,7 @@ handle = (HANDLE)_get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { if (raise) - PyErr_SetFromWindowsErr(0); + PyErr_SetFromErrno(PyExc_OSError); return -1; } @@ -788,10 +788,10 @@ if (atomic_flag_works != NULL && !inheritable) { if (*atomic_flag_works == -1) { - int inheritable = get_inheritable(fd, raise); - if (inheritable == -1) + int isInheritable = get_inheritable(fd, raise); + if (isInheritable == -1) return -1; - *atomic_flag_works = !inheritable; + *atomic_flag_works = !isInheritable; } if (*atomic_flag_works) @@ -808,7 +808,7 @@ handle = (HANDLE)_get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { if (raise) - PyErr_SetFromWindowsErr(0); + PyErr_SetFromErrno(PyExc_OSError); return -1; } @@ -1167,7 +1167,7 @@ #ifdef MS_WINDOWS handle = (HANDLE)_get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { - PyErr_SetFromWindowsErr(0); + PyErr_SetFromErrno(PyExc_OSError); return -1; } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 14 19:40:12 2015 From: python-checkins at python.org (steve.dower) Date: Sat, 14 Mar 2015 18:40:12 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fixes_incorrec?= =?utf-8?q?t_use_of_GetLastError_where_errno_should_be_used=2E?= Message-ID: <20150314184011.86349.36643@psf.io> https://hg.python.org/cpython/rev/6409b4048b10 changeset: 94987:6409b4048b10 branch: 3.4 parent: 94985:c9650423c336 user: Steve Dower date: Sat Mar 14 11:38:27 2015 -0700 summary: Fixes incorrect use of GetLastError where errno should be used. files: Python/fileutils.c | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Python/fileutils.c b/Python/fileutils.c --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -593,7 +593,7 @@ handle = (HANDLE)_get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { if (raise) - PyErr_SetFromWindowsErr(0); + PyErr_SetFromErrno(PyExc_OSError); return -1; } @@ -648,10 +648,10 @@ if (atomic_flag_works != NULL && !inheritable) { if (*atomic_flag_works == -1) { - int inheritable = get_inheritable(fd, raise); - if (inheritable == -1) + int isInheritable = get_inheritable(fd, raise); + if (isInheritable == -1) return -1; - *atomic_flag_works = !inheritable; + *atomic_flag_works = !isInheritable; } if (*atomic_flag_works) @@ -668,7 +668,7 @@ handle = (HANDLE)_get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { if (raise) - PyErr_SetFromWindowsErr(0); + PyErr_SetFromErrno(PyExc_OSError); return -1; } @@ -1027,7 +1027,7 @@ #ifdef MS_WINDOWS handle = (HANDLE)_get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { - PyErr_SetFromWindowsErr(0); + PyErr_SetFromErrno(PyExc_OSError); return -1; } -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 14 19:49:20 2015 From: python-checkins at python.org (steve.dower) Date: Sat, 14 Mar 2015 18:49:20 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=2323606=3A_Temporar?= =?utf-8?q?ily_suppress_test_for_CRT_name=2E?= Message-ID: <20150314184919.28831.61950@psf.io> https://hg.python.org/cpython/rev/fabbe6093567 changeset: 94989:fabbe6093567 user: Steve Dower date: Sat Mar 14 11:48:44 2015 -0700 summary: Issue #23606: Temporarily suppress test for CRT name. files: Lib/ctypes/test/test_loading.py | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) 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 @@ -52,7 +52,9 @@ @unittest.skipUnless(os.name in ("nt", "ce"), 'test specific to Windows (NT/CE)') def test_load_library(self): - self.assertIsNotNone(libc_name) + # CRT is no longer directly loadable. See issue23606 for the + # discussion about alternative approaches. + #self.assertIsNotNone(libc_name) if test.support.verbose: print(find_library("kernel32")) print(find_library("user32")) -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 14 20:35:07 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 14 Mar 2015 19:35:07 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=28merge_3=2E4_-=3E_default?= =?utf-8?q?=29=3A_Fix_minor_docs_markup_errors=2E?= Message-ID: <20150314193507.27349.74740@psf.io> https://hg.python.org/cpython/rev/a03cc14db96a changeset: 94992:a03cc14db96a parent: 94989:fabbe6093567 parent: 94991:5f49f79d5a83 user: Serhiy Storchaka date: Sat Mar 14 21:34:25 2015 +0200 summary: Fix minor docs markup errors. files: Doc/library/decimal.rst | 2 +- Doc/library/profile.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -263,7 +263,7 @@ Context() constructor. To make an alternate active, use the :func:`setcontext` function. -In accordance with the standard, the :mod:`Decimal` module provides two ready to +In accordance with the standard, the :mod:`decimal` module provides two ready to use standard contexts, :const:`BasicContext` and :const:`ExtendedContext`. The former is especially useful for debugging because many of the traps are enabled: diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -638,7 +638,7 @@ pr = cProfile.Profile(your_integer_time_func, 0.001) - As the :mod:`cProfile.Profile` class cannot be calibrated, custom timer + As the :class:`cProfile.Profile` class cannot be calibrated, custom timer functions should be used with care and should be as fast as possible. For the best results with a custom timer, it might be necessary to hard-code it in the C source of the internal :mod:`_lsprof` module. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 14 20:35:06 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 14 Mar 2015 19:35:06 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=282=2E7=29=3A_Fix_minor_docs?= =?utf-8?q?_markup_errors=2E?= Message-ID: <20150314193506.88862.98527@psf.io> https://hg.python.org/cpython/rev/b617790557b3 changeset: 94990:b617790557b3 branch: 2.7 parent: 94984:3b91d834160f user: Serhiy Storchaka date: Sat Mar 14 21:32:41 2015 +0200 summary: Fix minor docs markup errors. files: Doc/library/decimal.rst | 2 +- Doc/library/profile.rst | 2 +- Doc/tutorial/datastructures.rst | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -236,7 +236,7 @@ Context() constructor. To make an alternate active, use the :func:`setcontext` function. -In accordance with the standard, the :mod:`Decimal` module provides two ready to +In accordance with the standard, the :mod:`decimal` module provides two ready to use standard contexts, :const:`BasicContext` and :const:`ExtendedContext`. The former is especially useful for debugging because many of the traps are enabled: diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -663,7 +663,7 @@ pr = cProfile.Profile(your_integer_time_func, 0.001) - As the :mod:`cProfile.Profile` class cannot be calibrated, custom timer + As the :class:`cProfile.Profile` class cannot be calibrated, custom timer functions should be used with care and should be as fast as possible. For the best results with a custom timer, it might be necessary to hard-code it in the C source of the internal :mod:`_lsprof` module. diff --git a/Doc/tutorial/datastructures.rst b/Doc/tutorial/datastructures.rst --- a/Doc/tutorial/datastructures.rst +++ b/Doc/tutorial/datastructures.rst @@ -179,9 +179,9 @@ ``filter(function, sequence)`` returns a sequence consisting of those items from the sequence for which ``function(item)`` is true. If *sequence* is a -:class:`string` or :class:`tuple`, the result will be of the same type; -otherwise, it is always a :class:`list`. For example, to compute a sequence of -numbers divisible by 3 or 5:: +:class:`str`, :class:`unicode` or :class:`tuple`, the result will be of the +same type; otherwise, it is always a :class:`list`. For example, to compute a +sequence of numbers divisible by 3 or 5:: >>> def f(x): return x % 3 == 0 or x % 5 == 0 ... -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 14 20:35:07 2015 From: python-checkins at python.org (serhiy.storchaka) Date: Sat, 14 Mar 2015 19:35:07 +0000 Subject: [Python-checkins] =?utf-8?q?cpython_=283=2E4=29=3A_Fix_minor_docs?= =?utf-8?q?_markup_errors=2E?= Message-ID: <20150314193507.23866.15683@psf.io> https://hg.python.org/cpython/rev/5f49f79d5a83 changeset: 94991:5f49f79d5a83 branch: 3.4 parent: 94987:6409b4048b10 user: Serhiy Storchaka date: Sat Mar 14 21:32:57 2015 +0200 summary: Fix minor docs markup errors. files: Doc/library/decimal.rst | 2 +- Doc/library/profile.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -261,7 +261,7 @@ Context() constructor. To make an alternate active, use the :func:`setcontext` function. -In accordance with the standard, the :mod:`Decimal` module provides two ready to +In accordance with the standard, the :mod:`decimal` module provides two ready to use standard contexts, :const:`BasicContext` and :const:`ExtendedContext`. The former is especially useful for debugging because many of the traps are enabled: diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -638,7 +638,7 @@ pr = cProfile.Profile(your_integer_time_func, 0.001) - As the :mod:`cProfile.Profile` class cannot be calibrated, custom timer + As the :class:`cProfile.Profile` class cannot be calibrated, custom timer functions should be used with care and should be as fast as possible. For the best results with a custom timer, it might be necessary to hard-code it in the C source of the internal :mod:`_lsprof` module. -- Repository URL: https://hg.python.org/cpython From python-checkins at python.org Sat Mar 14 20:47:16 2015 From: python-checkins at python.org (ethan.furman) Date: Sat, 14 Mar 2015 19:47:16 +0000 Subject: [Python-checkins] =?utf-8?q?peps=3A_add_=25r_as_being_supported?= =?utf-8?q?=2C_plus_minor_pep8_cleanup?= Message-ID: <20150314194716.23870.83813@psf.io> https://hg.python.org/peps/rev/7fe79194a4f2 changeset: 5730:7fe79194a4f2 user: Ethan Furman date: Sat Mar 14 12:46:53 2015 -0700 summary: add %r as being supported, plus minor pep8 cleanup files: pep-0461.txt | 33 ++++++++++++++++----------------- 1 files changed, 16 insertions(+), 17 deletions(-) diff --git a/pep-0461.txt b/pep-0461.txt --- a/pep-0461.txt +++ b/pep-0461.txt @@ -93,7 +93,7 @@ b'a' ``%b`` will insert a series of bytes. These bytes are collected in one of two -ways: +ways:: - input type supports ``Py_buffer`` [4]_? use it to collect the necessary bytes @@ -142,6 +142,9 @@ format; or any situation where defining ``__bytes__`` would not be appropriate but a readable/informative representation is needed [6]_. +``%r`` is included as a synonym for ``%a`` for the sole purpose of making 2/3 +code bases easier to maintain. Python 3 only code use ``%a`` [7]_. + Examples:: >>> b'%a' % 3.14 @@ -154,25 +157,19 @@ b"'def'" -Unsupported codes ------------------ - -``%r`` (which calls ``__repr__`` and returns a ``str``) is not supported -(``%a`` is a good alternative, though). - Compatibility with Python 2 =========================== -As noted above, ``%s`` is being included solely to help ease migration from, -and/or have a single code base with, Python 2. This is important as there -are modules both in the wild and behind closed doors that currently use the -Python 2 ``str`` type as a ``bytes`` container, and hence are using ``%s`` -as a bytes interpolator. +As noted above, ``%s`` and ``%r`` are being included solely to help ease +migration from, and/or have a single code base with, Python 2. This is +important as there are modules both in the wild and behind closed doors that +currently use the Python 2 ``str`` type as a ``bytes`` container, and hence +are using ``%s`` as a bytes interpolator. -However, ``%b`` should be used in new, Python 3 only code, so ``%s`` will -immediately be deprecated, but not removed from the 3.x series. - +However, ``%b`` and ``%a`` should be used in new, Python 3 only code, so ``%s`` +and ``%r`` will immediately be deprecated, but not removed from the 3.x series +[7]_. Proposed variations =================== @@ -198,7 +195,7 @@ ``__format_bytes__``, etc.; such methods are not needed at this time, but can be visited again later if real-world use shows deficiencies with this solution. -A competing PEP, ``PEP 460 Add binary interpolation and formatting`` [7]_, +A competing PEP, ``PEP 460 Add binary interpolation and formatting`` [8]_, also exists. @@ -238,7 +235,9 @@ examples: ``memoryview``, ``array.array``, ``bytearray``, ``bytes`` .. [5] http://docs.python.org/3/reference/datamodel.html#object.__bytes__ .. [6] https://mail.python.org/pipermail/python-dev/2014-February/132750.html -.. [7] http://python.org/dev/peps/pep-0460/ +.. [7] http://bugs.python.org/issue23467 -- originally ``%r`` was not allowed, + but was added for consistency during the 3.5 alpha stage. +.. [8] http://python.org/dev/peps/pep-0460/ Copyright -- Repository URL: https://hg.python.org/peps From python-checkins at python.org Sun Mar 15 00:18:47 2015 From: python-checkins at python.org (berker.peksag) Date: Sat, 14 Mar 2015 23:18:47 +0000 Subject: [Python-checkins] =?utf-8?q?cpython=3A_Issue_=232052=3A_Add_chars?= =?utf-8?q?et_parameter_to_HtmlDiff=2Emake=5Ffile=28=29=2E?= Message-ID: <20150314231847.28827.20916@psf.io> https://hg.python.org/cpython/rev/e058423d3ca4 changeset: 94993:e058423d3ca4 user: Berker Peksag date: Sun Mar 15 01:18:47 2015 +0200 summary: Issue #2052: Add charset parameter to HtmlDiff.make_file(). files: Doc/library/difflib.rst | 7 +++- Doc/whatsnew/3.5.rst | 8 ++++ Lib/difflib.py | 19 +++++---- Lib/test/test_difflib.py | 35 +++++++++++++++++++ Lib/test/test_difflib_expect.html | 2 +- Misc/NEWS | 2 + 6 files changed, 63 insertions(+), 10 deletions(-) diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -104,7 +104,8 @@ The following methods are public: - .. method:: make_file(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5) + .. method:: make_file(fromlines, tolines, fromdesc='', todesc='', context=False, \ + numlines=5, *, charset='utf-8') Compares *fromlines* and *tolines* (lists of strings) and returns a string which is a complete HTML file containing a table showing line by line differences with @@ -123,6 +124,10 @@ the next difference highlight at the top of the browser without any leading context). + .. versionchanged:: 3.5 + *charset* keyword-only argument was added. The default charset of + HTML document changed from ``'ISO-8859-1'`` to ``'utf-8'``. + .. method:: make_table(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5) Compares *fromlines* and *tolines* (lists of strings) and returns a string which 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 @@ -225,6 +225,14 @@ don't provide any options to redirect it. (Contributed by Berker Peksag in :issue:`22389`.) +difflib +------- + +* The charset of the HTML document generated by :meth:`difflib.HtmlDiff.make_file` + can now be customized by using *charset* keyword-only parameter. The default + charset of HTML document changed from ``'ISO-8859-1'`` to ``'utf-8'``. + (Contributed by Berker Peksag in :issue:`2052`.) + distutils --------- diff --git a/Lib/difflib.py b/Lib/difflib.py --- a/Lib/difflib.py +++ b/Lib/difflib.py @@ -1598,7 +1598,7 @@ + content="text/html; charset=%(charset)s" /> @@ -1685,8 +1685,8 @@ self._linejunk = linejunk self._charjunk = charjunk - def make_file(self,fromlines,tolines,fromdesc='',todesc='',context=False, - numlines=5): + def make_file(self, fromlines, tolines, fromdesc='', todesc='', + context=False, numlines=5, *, charset='utf-8'): """Returns HTML file of side by side comparison with change highlights Arguments: @@ -1701,13 +1701,16 @@ When context is False, controls the number of lines to place the "next" link anchors before the next change (so click of "next" link jumps to just before the change). + charset -- charset of the HTML document """ - return self._file_template % dict( - styles = self._styles, - legend = self._legend, - table = self.make_table(fromlines,tolines,fromdesc,todesc, - context=context,numlines=numlines)) + return (self._file_template % dict( + styles=self._styles, + legend=self._legend, + table=self.make_table(fromlines, tolines, fromdesc, todesc, + context=context, numlines=numlines), + charset=charset + )).encode(charset, 'xmlcharrefreplace').decode(charset) def _tab_newline_replace(self,fromlines,tolines): """Returns from/to line lists with tabs expanded and newlines removed. 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 @@ -107,6 +107,20 @@ 5. Flat is better than nested. """ +patch914575_nonascii_from1 = """ + 1. Beautiful is beTTer than ugly. + 2. Explicit is better than ?mpl?c?t. + 3. Simple is better than complex. + 4. Complex is better than complicated. +""" + +patch914575_nonascii_to1 = """ + 1. Beautiful is better than ?gly. + 3. S?mple is better than complex. + 4. Complicated is better than c?mplex. + 5. Flat is better than nested. +""" + patch914575_from2 = """ \t\tLine 1: preceeded by from:[tt] to:[ssss] \t\tLine 2: preceeded by from:[sstt] to:[sssst] @@ -223,6 +237,27 @@ new = [(i%2 and "K:%d" or "V:B:%d") % i for i in range(limit*2)] difflib.SequenceMatcher(None, old, new).get_opcodes() + def test_make_file_default_charset(self): + html_diff = difflib.HtmlDiff() + output = html_diff.make_file(patch914575_from1.splitlines(), + patch914575_to1.splitlines()) + self.assertIn('content="text/html; charset=utf-8"', output) + + def test_make_file_iso88591_charset(self): + html_diff = difflib.HtmlDiff() + output = html_diff.make_file(patch914575_from1.splitlines(), + patch914575_to1.splitlines(), + charset='iso-8859-1') + self.assertIn('content="text/html; charset=iso-8859-1"', output) + + def test_make_file_usascii_charset_with_nonascii_input(self): + html_diff = difflib.HtmlDiff() + output = html_diff.make_file(patch914575_nonascii_from1.splitlines(), + patch914575_nonascii_to1.splitlines(), + charset='us-ascii') + self.assertIn('content="text/html; charset=us-ascii"', output) + self.assertIn('ımplıcıt', output) + class TestOutputFormat(unittest.TestCase): def test_tab_delimiter(self): diff --git a/Lib/test/test_difflib_expect.html b/Lib/test/test_difflib_expect.html --- a/Lib/test/test_difflib_expect.html +++ b/Lib/test/test_difflib_expect.html @@ -6,7 +6,7 @@ + content="text/html; charset=utf-8" />