summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/imp.py8
-rw-r--r--Lib/test/imp_dummy.py3
-rw-r--r--Lib/test/test_imp.py24
-rw-r--r--Lib/test/test_time.py13
-rw-r--r--Lib/test/test_warnings/__init__.py (renamed from Lib/test/test_warnings.py)37
-rw-r--r--Lib/test/test_warnings/__main__.py3
-rw-r--r--Lib/test/test_warnings/data/import_warning.py3
-rw-r--r--Lib/test/test_warnings/data/stacklevel.py (renamed from Lib/test/warning_tests.py)0
-rw-r--r--Lib/warnings.py31
-rwxr-xr-xLib/webbrowser.py120
-rw-r--r--Misc/NEWS14
-rw-r--r--Modules/_testmultiphase.c10
-rw-r--r--Modules/timemodule.c16
-rw-r--r--Python/_warnings.c72
14 files changed, 210 insertions, 144 deletions
diff --git a/Lib/imp.py b/Lib/imp.py
index 2cd6440..f6fff44 100644
--- a/Lib/imp.py
+++ b/Lib/imp.py
@@ -334,6 +334,12 @@ if create_dynamic:
"""
import importlib.machinery
loader = importlib.machinery.ExtensionFileLoader(name, path)
- return loader.load_module()
+
+ # Issue #24748: Skip the sys.modules check in _load_module_shim;
+ # always load new extension
+ spec = importlib.machinery.ModuleSpec(
+ name=name, loader=loader, origin=path)
+ return _load(spec)
+
else:
load_dynamic = None
diff --git a/Lib/test/imp_dummy.py b/Lib/test/imp_dummy.py
new file mode 100644
index 0000000..2a4deb4
--- /dev/null
+++ b/Lib/test/imp_dummy.py
@@ -0,0 +1,3 @@
+# Fodder for test of issue24748 in test_imp
+
+dummy_name = True
diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py
index 47bf1de..ee9ee1a 100644
--- a/Lib/test/test_imp.py
+++ b/Lib/test/test_imp.py
@@ -3,6 +3,7 @@ try:
except ImportError:
_thread = None
import importlib
+import importlib.util
import os
import os.path
import shutil
@@ -275,6 +276,29 @@ class ImportTests(unittest.TestCase):
self.skipTest("found module doesn't appear to be a C extension")
imp.load_module(name, None, *found[1:])
+ @requires_load_dynamic
+ def test_issue24748_load_module_skips_sys_modules_check(self):
+ name = 'test.imp_dummy'
+ try:
+ del sys.modules[name]
+ except KeyError:
+ pass
+ try:
+ module = importlib.import_module(name)
+ spec = importlib.util.find_spec('_testmultiphase')
+ module = imp.load_dynamic(name, spec.origin)
+ self.assertEqual(module.__name__, name)
+ self.assertEqual(module.__spec__.name, name)
+ self.assertEqual(module.__spec__.origin, spec.origin)
+ self.assertRaises(AttributeError, getattr, module, 'dummy_name')
+ self.assertEqual(module.int_const, 1969)
+ self.assertIs(sys.modules[name], module)
+ finally:
+ try:
+ del sys.modules[name]
+ except KeyError:
+ pass
+
@unittest.skipIf(sys.dont_write_bytecode,
"test meaningful only when writing bytecode")
def test_bug7732(self):
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index 6334e02..6bcd212 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -174,6 +174,19 @@ class TimeTestCase(unittest.TestCase):
def test_strftime_bounding_check(self):
self._bounds_checking(lambda tup: time.strftime('', tup))
+ def test_strftime_format_check(self):
+ # Test that strftime does not crash on invalid format strings
+ # that may trigger a buffer overread. When not triggered,
+ # strftime may succeed or raise ValueError depending on
+ # the platform.
+ for x in [ '', 'A', '%A', '%AA' ]:
+ for y in range(0x0, 0x10):
+ for z in [ '%', 'A%', 'AA%', '%A%', 'A%A%', '%#' ]:
+ try:
+ time.strftime(x * y + z)
+ except ValueError:
+ pass
+
def test_default_values_for_zero(self):
# Make sure that using all zeros uses the proper default
# values. No test for daylight savings since strftime() does
diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings/__init__.py
index 03d9958..991a249 100644
--- a/Lib/test/test_warnings.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -7,7 +7,7 @@ import unittest
from test import support
from test.support.script_helper import assert_python_ok, assert_python_failure
-from test import warning_tests
+from test.test_warnings.data import stacklevel as warning_tests
import warnings as original_warnings
@@ -44,7 +44,6 @@ class BaseTest:
"""Basic bookkeeping required for testing."""
def setUp(self):
- self.old_unittest_module = unittest.case.warnings
# The __warningregistry__ needs to be in a pristine state for tests
# to work properly.
if '__warningregistry__' in globals():
@@ -56,15 +55,10 @@ class BaseTest:
# The 'warnings' module must be explicitly set so that the proper
# interaction between _warnings and 'warnings' can be controlled.
sys.modules['warnings'] = self.module
- # Ensure that unittest.TestCase.assertWarns() uses the same warnings
- # module than warnings.catch_warnings(). Otherwise,
- # warnings.catch_warnings() will be unable to remove the added filter.
- unittest.case.warnings = self.module
super(BaseTest, self).setUp()
def tearDown(self):
sys.modules['warnings'] = original_warnings
- unittest.case.warnings = self.old_unittest_module
super(BaseTest, self).tearDown()
class PublicAPITests(BaseTest):
@@ -194,11 +188,11 @@ class FilterTests(BaseTest):
self.module.resetwarnings()
self.module.filterwarnings("once", category=UserWarning)
message = UserWarning("FilterTests.test_once")
- self.module.warn_explicit(message, UserWarning, "test_warnings.py",
+ self.module.warn_explicit(message, UserWarning, "__init__.py",
42)
self.assertEqual(w[-1].message, message)
del w[:]
- self.module.warn_explicit(message, UserWarning, "test_warnings.py",
+ self.module.warn_explicit(message, UserWarning, "__init__.py",
13)
self.assertEqual(len(w), 0)
self.module.warn_explicit(message, UserWarning, "test_warnings2.py",
@@ -304,10 +298,10 @@ class WarnTests(BaseTest):
module=self.module) as w:
warning_tests.inner("spam1")
self.assertEqual(os.path.basename(w[-1].filename),
- "warning_tests.py")
+ "stacklevel.py")
warning_tests.outer("spam2")
self.assertEqual(os.path.basename(w[-1].filename),
- "warning_tests.py")
+ "stacklevel.py")
def test_stacklevel(self):
# Test stacklevel argument
@@ -317,25 +311,36 @@ class WarnTests(BaseTest):
module=self.module) as w:
warning_tests.inner("spam3", stacklevel=1)
self.assertEqual(os.path.basename(w[-1].filename),
- "warning_tests.py")
+ "stacklevel.py")
warning_tests.outer("spam4", stacklevel=1)
self.assertEqual(os.path.basename(w[-1].filename),
- "warning_tests.py")
+ "stacklevel.py")
warning_tests.inner("spam5", stacklevel=2)
self.assertEqual(os.path.basename(w[-1].filename),
- "test_warnings.py")
+ "__init__.py")
warning_tests.outer("spam6", stacklevel=2)
self.assertEqual(os.path.basename(w[-1].filename),
- "warning_tests.py")
+ "stacklevel.py")
warning_tests.outer("spam6.5", stacklevel=3)
self.assertEqual(os.path.basename(w[-1].filename),
- "test_warnings.py")
+ "__init__.py")
warning_tests.inner("spam7", stacklevel=9999)
self.assertEqual(os.path.basename(w[-1].filename),
"sys")
+ def test_stacklevel_import(self):
+ # Issue #24305: With stacklevel=2, module-level warnings should work.
+ support.unload('test.test_warnings.data.import_warning')
+ with warnings_state(self.module):
+ with original_warnings.catch_warnings(record=True,
+ module=self.module) as w:
+ self.module.simplefilter('always')
+ import test.test_warnings.data.import_warning
+ self.assertEqual(len(w), 1)
+ self.assertEqual(w[0].filename, __file__)
+
def test_missing_filename_not_main(self):
# If __file__ is not specified and __main__ is not the module name,
# then __file__ should be set to the module name.
diff --git a/Lib/test/test_warnings/__main__.py b/Lib/test/test_warnings/__main__.py
new file mode 100644
index 0000000..44e52ec
--- /dev/null
+++ b/Lib/test/test_warnings/__main__.py
@@ -0,0 +1,3 @@
+import unittest
+
+unittest.main('test.test_warnings')
diff --git a/Lib/test/test_warnings/data/import_warning.py b/Lib/test/test_warnings/data/import_warning.py
new file mode 100644
index 0000000..d6ea2ce
--- /dev/null
+++ b/Lib/test/test_warnings/data/import_warning.py
@@ -0,0 +1,3 @@
+import warnings
+
+warnings.warn('module-level warning', DeprecationWarning, stacklevel=2) \ No newline at end of file
diff --git a/Lib/test/warning_tests.py b/Lib/test/test_warnings/data/stacklevel.py
index d0519ef..d0519ef 100644
--- a/Lib/test/warning_tests.py
+++ b/Lib/test/test_warnings/data/stacklevel.py
diff --git a/Lib/warnings.py b/Lib/warnings.py
index 16246b4..1d4fb20 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -160,6 +160,20 @@ def _getcategory(category):
return cat
+def _is_internal_frame(frame):
+ """Signal whether the frame is an internal CPython implementation detail."""
+ filename = frame.f_code.co_filename
+ return 'importlib' in filename and '_bootstrap' in filename
+
+
+def _next_external_frame(frame):
+ """Find the next frame that doesn't involve CPython internals."""
+ frame = frame.f_back
+ while frame is not None and _is_internal_frame(frame):
+ frame = frame.f_back
+ return frame
+
+
# Code typically replaced by _warnings
def warn(message, category=None, stacklevel=1):
"""Issue a warning, or maybe ignore it or raise an exception."""
@@ -174,13 +188,23 @@ def warn(message, category=None, stacklevel=1):
"not '{:s}'".format(type(category).__name__))
# Get context information
try:
- caller = sys._getframe(stacklevel)
+ if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)):
+ # If frame is too small to care or if the warning originated in
+ # internal code, then do not try to hide any frames.
+ frame = sys._getframe(stacklevel)
+ else:
+ frame = sys._getframe(1)
+ # Look for one frame less since the above line starts us off.
+ for x in range(stacklevel-1):
+ frame = _next_external_frame(frame)
+ if frame is None:
+ raise ValueError
except ValueError:
globals = sys.__dict__
lineno = 1
else:
- globals = caller.f_globals
- lineno = caller.f_lineno
+ globals = frame.f_globals
+ lineno = frame.f_lineno
if '__name__' in globals:
module = globals['__name__']
else:
@@ -374,7 +398,6 @@ try:
defaultaction = _defaultaction
onceregistry = _onceregistry
_warnings_defaults = True
-
except ImportError:
filters = []
defaultaction = "default"
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
index 6deed92..845f1d0 100755
--- a/Lib/webbrowser.py
+++ b/Lib/webbrowser.py
@@ -495,23 +495,10 @@ if os.environ.get("TERM"):
#
if sys.platform[:3] == "win":
-
class WindowsDefault(BaseBrowser):
- # Windows Default opening arguments.
-
- cmd = "start"
- newwindow = ""
- newtab = ""
-
def open(self, url, new=0, autoraise=True):
- # Format the command for optional arguments and add the url.
- if new == 1:
- self.cmd += " " + self.newwindow
- elif new == 2:
- self.cmd += " " + self.newtab
- self.cmd += " " + url
try:
- subprocess.call(self.cmd, shell=True)
+ os.startfile(url)
except OSError:
# [Error 22] No application is associated with the specified
# file for this operation: '<URL>'
@@ -519,108 +506,19 @@ if sys.platform[:3] == "win":
else:
return True
-
- # Windows Sub-Classes for commonly used browsers.
-
- class InternetExplorer(WindowsDefault):
- """Launcher class for Internet Explorer browser"""
-
- cmd = "start iexplore.exe"
- newwindow = ""
- newtab = ""
-
-
- class WinChrome(WindowsDefault):
- """Launcher class for windows specific Google Chrome browser"""
-
- cmd = "start chrome.exe"
- newwindow = "-new-window"
- newtab = "-new-tab"
-
-
- class WinFirefox(WindowsDefault):
- """Launcher class for windows specific Firefox browser"""
-
- cmd = "start firefox.exe"
- newwindow = "-new-window"
- newtab = "-new-tab"
-
-
- class WinOpera(WindowsDefault):
- """Launcher class for windows specific Opera browser"""
-
- cmd = "start opera"
- newwindow = ""
- newtab = ""
-
-
- class WinSeaMonkey(WindowsDefault):
- """Launcher class for windows specific SeaMonkey browser"""
-
- cmd = "start seamonkey"
- newwinow = ""
- newtab = ""
-
-
_tryorder = []
_browsers = {}
- # First try to use the default Windows browser.
+ # First try to use the default Windows browser
register("windows-default", WindowsDefault)
- def find_windows_browsers():
- """ Access the windows registry to determine
- what browsers are on the system.
- """
-
- import winreg
- HKLM = winreg.HKEY_LOCAL_MACHINE
- subkey = r'Software\Clients\StartMenuInternet'
- read32 = winreg.KEY_READ | winreg.KEY_WOW64_32KEY
- read64 = winreg.KEY_READ | winreg.KEY_WOW64_64KEY
- key32 = winreg.OpenKey(HKLM, subkey, access=read32)
- key64 = winreg.OpenKey(HKLM, subkey, access=read64)
-
- # Return a list of browsers found in the registry
- # Check if there are any different browsers in the
- # 32 bit location instead of the 64 bit location.
- browsers = []
- i = 0
- while True:
- try:
- browsers.append(winreg.EnumKey(key32, i))
- except EnvironmentError:
- break
- i += 1
-
- i = 0
- while True:
- try:
- browsers.append(winreg.EnumKey(key64, i))
- except EnvironmentError:
- break
- i += 1
-
- winreg.CloseKey(key32)
- winreg.CloseKey(key64)
-
- return browsers
-
- # Detect some common windows browsers
- for browser in find_windows_browsers():
- browser = browser.lower()
- if "iexplore" in browser:
- register("iexplore", None, InternetExplorer("iexplore"))
- elif "chrome" in browser:
- register("chrome", None, WinChrome("chrome"))
- elif "firefox" in browser:
- register("firefox", None, WinFirefox("firefox"))
- elif "opera" in browser:
- register("opera", None, WinOpera("opera"))
- elif "seamonkey" in browser:
- register("seamonkey", None, WinSeaMonkey("seamonkey"))
- else:
- register(browser, None, WindowsDefault(browser))
+ # Detect some common Windows browsers, fallback to IE
+ iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
+ "Internet Explorer\\IEXPLORE.EXE")
+ for browser in ("firefox", "firebird", "seamonkey", "mozilla",
+ "netscape", "opera", iexplore):
+ if shutil.which(browser):
+ register(browser, None, BackgroundBrowser(browser))
#
# Platform support for MacOS
diff --git a/Misc/NEWS b/Misc/NEWS
index 64c9af7..8a09be7 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1,4 +1,4 @@
-+++++++++++
++++++++++++
Python News
+++++++++++
@@ -88,6 +88,9 @@ Release date: 2015-09-06
Core and Builtins
-----------------
+- Issue #24305: Prevent import subsystem stack frames from being counted
+ by the warnings.warn(stacklevel=) parameter.
+
- Issue #24912: Prevent __class__ assignment to immutable built-in objects.
- Issue #24975: Fix AST compilation for PEP 448 syntax.
@@ -95,9 +98,15 @@ Core and Builtins
Library
-------
+- Issue #24917: time_strftime() buffer over-read.
- Issue #23144: Make sure that HTMLParser.feed() returns all the data, even
when convert_charrefs is True.
+- Issue #24748: To resolve a compatibility problem found with py2exe and
+ pywin32, imp.load_dynamic() once again ignores previously loaded modules
+ to support Python modules replacing themselves with extension modules.
+ Patch by Petr Viktorin.
+
- Issue #24635: Fixed a bug in typing.py where isinstance([], typing.Iterable)
would return True once, then False on subsequent calls.
@@ -386,9 +395,6 @@ Library
- Issue #14373: C implementation of functools.lru_cache() now can be used with
methods.
-- Issue #8232: webbrowser support incomplete on Windows. Patch by Brandon
- Milam
-
- Issue #24347: Set KeyError if PyDict_GetItemWithError returns NULL.
- Issue #24348: Drop superfluous incref/decref.
diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c
index 2919687..2005205 100644
--- a/Modules/_testmultiphase.c
+++ b/Modules/_testmultiphase.c
@@ -582,3 +582,13 @@ PyInit__testmultiphase_exec_unreported_exception(PyObject *spec)
{
return PyModuleDef_Init(&def_exec_unreported_exception);
}
+
+/*** Helper for imp test ***/
+
+static PyModuleDef imp_dummy_def = TEST_MODULE_DEF("imp_dummy", main_slots, testexport_methods);
+
+PyMODINIT_FUNC
+PyInit_imp_dummy(PyObject *spec)
+{
+ return PyModuleDef_Init(&imp_dummy_def);
+}
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index 197d2c0..eca67d9 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -610,14 +610,15 @@ time_strftime(PyObject *self, PyObject *args)
#if defined(MS_WINDOWS) && !defined(HAVE_WCSFTIME)
/* check that the format string contains only valid directives */
- for(outbuf = strchr(fmt, '%');
+ for (outbuf = strchr(fmt, '%');
outbuf != NULL;
outbuf = strchr(outbuf+2, '%'))
{
- if (outbuf[1]=='#')
+ if (outbuf[1] == '#')
++outbuf; /* not documented by python, */
- if ((outbuf[1] == 'y') && buf.tm_year < 0)
- {
+ if (outbuf[1] == '\0')
+ break;
+ if ((outbuf[1] == 'y') && buf.tm_year < 0) {
PyErr_SetString(PyExc_ValueError,
"format %y requires year >= 1900 on Windows");
Py_DECREF(format);
@@ -625,10 +626,12 @@ time_strftime(PyObject *self, PyObject *args)
}
}
#elif (defined(_AIX) || defined(sun)) && defined(HAVE_WCSFTIME)
- for(outbuf = wcschr(fmt, '%');
+ for (outbuf = wcschr(fmt, '%');
outbuf != NULL;
outbuf = wcschr(outbuf+2, '%'))
{
+ if (outbuf[1] == L'\0')
+ break;
/* Issue #19634: On AIX, wcsftime("y", (1899, 1, 1, 0, 0, 0, 0, 0, 0))
returns "0/" instead of "99" */
if (outbuf[1] == L'y' && buf.tm_year < 0) {
@@ -659,7 +662,8 @@ time_strftime(PyObject *self, PyObject *args)
#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__)
err = errno;
#endif
- if (buflen > 0 || i >= 256 * fmtlen) {
+ if (buflen > 0 || fmtlen == 0 ||
+ (fmtlen > 4 && i >= 256 * fmtlen)) {
/* If the buffer is 256 times as long as the format,
it's probably not failing for lack of room!
More likely, the format yields an empty result,
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 22f617a..9ca8314 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -513,6 +513,64 @@ warn_explicit(PyObject *category, PyObject *message,
return result; /* Py_None or NULL. */
}
+static int
+is_internal_frame(PyFrameObject *frame)
+{
+ static PyObject *importlib_string = NULL;
+ static PyObject *bootstrap_string = NULL;
+ PyObject *filename;
+ int contains;
+
+ if (importlib_string == NULL) {
+ importlib_string = PyUnicode_FromString("importlib");
+ if (importlib_string == NULL) {
+ return 0;
+ }
+
+ bootstrap_string = PyUnicode_FromString("_bootstrap");
+ if (bootstrap_string == NULL) {
+ Py_DECREF(importlib_string);
+ return 0;
+ }
+ Py_INCREF(importlib_string);
+ Py_INCREF(bootstrap_string);
+ }
+
+ if (frame == NULL || frame->f_code == NULL ||
+ frame->f_code->co_filename == NULL) {
+ return 0;
+ }
+ filename = frame->f_code->co_filename;
+ if (!PyUnicode_Check(filename)) {
+ return 0;
+ }
+ contains = PyUnicode_Contains(filename, importlib_string);
+ if (contains < 0) {
+ return 0;
+ }
+ else if (contains > 0) {
+ contains = PyUnicode_Contains(filename, bootstrap_string);
+ if (contains < 0) {
+ return 0;
+ }
+ else if (contains > 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static PyFrameObject *
+next_external_frame(PyFrameObject *frame)
+{
+ do {
+ frame = frame->f_back;
+ } while (frame != NULL && is_internal_frame(frame));
+
+ return frame;
+}
+
/* filename, module, and registry are new refs, globals is borrowed */
/* Returns 0 on error (no new refs), 1 on success */
static int
@@ -523,8 +581,18 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
/* Setup globals and lineno. */
PyFrameObject *f = PyThreadState_GET()->frame;
- while (--stack_level > 0 && f != NULL)
- f = f->f_back;
+ // Stack level comparisons to Python code is off by one as there is no
+ // warnings-related stack level to avoid.
+ if (stack_level <= 0 || is_internal_frame(f)) {
+ while (--stack_level > 0 && f != NULL) {
+ f = f->f_back;
+ }
+ }
+ else {
+ while (--stack_level > 0 && f != NULL) {
+ f = next_external_frame(f);
+ }
+ }
if (f == NULL) {
globals = PyThreadState_Get()->interp->sysdict;