From 0f4f048fa5ff06438a4624bc1dc02aa458a79748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Fran=C3=A7ois=20Natali?= Date: Mon, 13 Oct 2014 19:19:26 +0100 Subject: [PATCH 1/3] Issue #22435: Fix a file descriptor leak when SocketServer bind fails. --- Lib/socketserver.py | 8 ++++++-- Lib/test/test_socketserver.py | 10 ++++++++++ Misc/NEWS | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Lib/socketserver.py b/Lib/socketserver.py index 2f395fac242..73399115b6a 100644 --- a/Lib/socketserver.py +++ b/Lib/socketserver.py @@ -426,8 +426,12 @@ class TCPServer(BaseServer): self.socket = socket.socket(self.address_family, self.socket_type) if bind_and_activate: - self.server_bind() - self.server_activate() + try: + self.server_bind() + self.server_activate() + except: + self.server_close() + raise def server_bind(self): """Called by constructor to bind the socket. diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 0617b30a69e..0276f993f97 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -302,6 +302,16 @@ class SocketServerTest(unittest.TestCase): t.join() s.server_close() + def test_tcpserver_bind_leak(self): + # Issue #22435: the server socket wouldn't be closed if bind()/listen() + # failed. + # Create many servers for which bind() will fail, to see if this result + # in FD exhaustion. + for i in range(1024): + with self.assertRaises(OverflowError): + socketserver.TCPServer((HOST, -1), + socketserver.StreamRequestHandler) + def test_main(): if imp.lock_held(): diff --git a/Misc/NEWS b/Misc/NEWS index ab5a4cb3789..e5030cd5d81 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -27,6 +27,8 @@ Core and Builtins Library ------- +- Issue #22435: Fix a file descriptor leak when SocketServer bind fails. + - Issue #13096: Fixed segfault in CTypes POINTER handling of large values. From 0373a106a1ca9ce67cd99bf0a93ab7c3abd15cb6 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Mon, 13 Oct 2014 20:19:45 +0200 Subject: [PATCH 2/3] Issue #17636: Circular imports involving relative imports are now supported. --- .../__init__.py} | 42 ++++++++++++++++++- Lib/test/test_import/__main__.py | 3 ++ .../data/circular_imports/basic.py | 2 + .../data/circular_imports/basic2.py | 1 + .../data/circular_imports/indirect.py | 1 + .../data/circular_imports/rebinding.py | 3 ++ .../data/circular_imports/rebinding2.py | 3 ++ .../data/circular_imports/subpackage.py | 2 + .../circular_imports/subpkg/subpackage2.py | 2 + .../data/circular_imports/subpkg/util.py | 2 + .../test_import/data/circular_imports/util.py | 2 + Misc/NEWS | 3 ++ Python/ceval.c | 22 +++++++++- 13 files changed, 85 insertions(+), 3 deletions(-) rename Lib/test/{test_import.py => test_import/__init__.py} (96%) create mode 100644 Lib/test/test_import/__main__.py create mode 100644 Lib/test/test_import/data/circular_imports/basic.py create mode 100644 Lib/test/test_import/data/circular_imports/basic2.py create mode 100644 Lib/test/test_import/data/circular_imports/indirect.py create mode 100644 Lib/test/test_import/data/circular_imports/rebinding.py create mode 100644 Lib/test/test_import/data/circular_imports/rebinding2.py create mode 100644 Lib/test/test_import/data/circular_imports/subpackage.py create mode 100644 Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py create mode 100644 Lib/test/test_import/data/circular_imports/subpkg/util.py create mode 100644 Lib/test/test_import/data/circular_imports/util.py diff --git a/Lib/test/test_import.py b/Lib/test/test_import/__init__.py similarity index 96% rename from Lib/test/test_import.py rename to Lib/test/test_import/__init__.py index b4842c54b56..fd21fa29e7c 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import/__init__.py @@ -568,7 +568,7 @@ class RelativeImportTests(unittest.TestCase): def test_relimport_star(self): # This will import * from .test_import. - from . import relimport + from .. import relimport self.assertTrue(hasattr(relimport, "RelativeImportTests")) def test_issue3221(self): @@ -1068,6 +1068,46 @@ class ImportTracebackTests(unittest.TestCase): __isolated=False) +class CircularImportTests(unittest.TestCase): + + """See the docstrings of the modules being imported for the purpose of the + test.""" + + def tearDown(self): + """Make sure no modules pre-exist in sys.modules which are being used to + test.""" + for key in list(sys.modules.keys()): + if key.startswith('test.test_import.data.circular_imports'): + del sys.modules[key] + + def test_direct(self): + try: + import test.test_import.data.circular_imports.basic + except ImportError: + self.fail('circular import through relative imports failed') + + def test_indirect(self): + try: + import test.test_import.data.circular_imports.indirect + except ImportError: + self.fail('relative import in module contributing to circular ' + 'import failed') + + def test_subpackage(self): + try: + import test.test_import.data.circular_imports.subpackage + except ImportError: + self.fail('circular import involving a subpackage failed') + + def test_rebinding(self): + try: + import test.test_import.data.circular_imports.rebinding as rebinding + except ImportError: + self.fail('circular import with rebinding of module attribute failed') + from test.test_import.data.circular_imports.subpkg import util + self.assertIs(util.util, rebinding.util) + + if __name__ == '__main__': # Test needs to be a package, so we can do relative imports. unittest.main() diff --git a/Lib/test/test_import/__main__.py b/Lib/test/test_import/__main__.py new file mode 100644 index 00000000000..ce26cbdc63b --- /dev/null +++ b/Lib/test/test_import/__main__.py @@ -0,0 +1,3 @@ +import unittest + +unittest.main('test.test_import') \ No newline at end of file diff --git a/Lib/test/test_import/data/circular_imports/basic.py b/Lib/test/test_import/data/circular_imports/basic.py new file mode 100644 index 00000000000..3e41e395dbb --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/basic.py @@ -0,0 +1,2 @@ +"""Circular imports through direct, relative imports.""" +from . import basic2 diff --git a/Lib/test/test_import/data/circular_imports/basic2.py b/Lib/test/test_import/data/circular_imports/basic2.py new file mode 100644 index 00000000000..00bd2f29f3b --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/basic2.py @@ -0,0 +1 @@ +from . import basic diff --git a/Lib/test/test_import/data/circular_imports/indirect.py b/Lib/test/test_import/data/circular_imports/indirect.py new file mode 100644 index 00000000000..6925788d603 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/indirect.py @@ -0,0 +1 @@ +from . import basic, basic2 diff --git a/Lib/test/test_import/data/circular_imports/rebinding.py b/Lib/test/test_import/data/circular_imports/rebinding.py new file mode 100644 index 00000000000..f51d1c60ee0 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/rebinding.py @@ -0,0 +1,3 @@ +"""Test the binding of names when a circular import shares the same name as an +attribute.""" +from .rebinding2 import util \ No newline at end of file diff --git a/Lib/test/test_import/data/circular_imports/rebinding2.py b/Lib/test/test_import/data/circular_imports/rebinding2.py new file mode 100644 index 00000000000..62ca7268603 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/rebinding2.py @@ -0,0 +1,3 @@ +from .subpkg import util +from . import rebinding +util = util.util \ No newline at end of file diff --git a/Lib/test/test_import/data/circular_imports/subpackage.py b/Lib/test/test_import/data/circular_imports/subpackage.py new file mode 100644 index 00000000000..cc36c3c3950 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/subpackage.py @@ -0,0 +1,2 @@ +"""Circular import involving a sub-package.""" +from .subpkg import subpackage2 \ No newline at end of file diff --git a/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py b/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py new file mode 100644 index 00000000000..07e0bc05457 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py @@ -0,0 +1,2 @@ +#from .util import util +from .. import subpackage \ No newline at end of file diff --git a/Lib/test/test_import/data/circular_imports/subpkg/util.py b/Lib/test/test_import/data/circular_imports/subpkg/util.py new file mode 100644 index 00000000000..f1505e9d5ad --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/subpkg/util.py @@ -0,0 +1,2 @@ +def util(): + pass \ No newline at end of file diff --git a/Lib/test/test_import/data/circular_imports/util.py b/Lib/test/test_import/data/circular_imports/util.py new file mode 100644 index 00000000000..f1505e9d5ad --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/util.py @@ -0,0 +1,2 @@ +def util(): + pass \ No newline at end of file diff --git a/Misc/NEWS b/Misc/NEWS index 1453637d01e..590e26e133e 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Release date: TBA Core and Builtins ----------------- +- Issue #17636: Circular imports involving relative imports are now + supported. + - Issue #22604: Fix assertion error in debug mode when dividing a complex number by (nan+0j). diff --git a/Python/ceval.c b/Python/ceval.c index 2dbf591d45f..4b1d6ca9697 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4693,11 +4693,29 @@ static PyObject * import_from(PyObject *v, PyObject *name) { PyObject *x; + _Py_IDENTIFIER(__name__); + PyObject *fullmodname, *pkgname; x = PyObject_GetAttr(v, name); - if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { + if (x != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) + return x; + /* Issue #17636: in case this failed because of a circular relative + import, try to fallback on reading the module directly from + sys.modules. */ + PyErr_Clear(); + pkgname = _PyObject_GetAttrId(v, &PyId___name__); + if (pkgname == NULL) + return NULL; + fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name); + Py_DECREF(pkgname); + if (fullmodname == NULL) + return NULL; + x = PyDict_GetItem(PyImport_GetModuleDict(), fullmodname); + if (x == NULL) PyErr_Format(PyExc_ImportError, "cannot import name %R", name); - } + else + Py_INCREF(x); + Py_DECREF(fullmodname); return x; } From 44fff77684c9088d47304ae0f844d3ac5d271a26 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Mon, 13 Oct 2014 20:21:12 +0200 Subject: [PATCH 3/3] Whitespace --- Lib/test/test_import/__main__.py | 2 +- Lib/test/test_import/data/circular_imports/rebinding.py | 2 +- Lib/test/test_import/data/circular_imports/rebinding2.py | 2 +- Lib/test/test_import/data/circular_imports/subpackage.py | 2 +- .../test_import/data/circular_imports/subpkg/subpackage2.py | 2 +- Lib/test/test_import/data/circular_imports/subpkg/util.py | 2 +- Lib/test/test_import/data/circular_imports/util.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_import/__main__.py b/Lib/test/test_import/__main__.py index ce26cbdc63b..24f02a171ad 100644 --- a/Lib/test/test_import/__main__.py +++ b/Lib/test/test_import/__main__.py @@ -1,3 +1,3 @@ import unittest -unittest.main('test.test_import') \ No newline at end of file +unittest.main('test.test_import') diff --git a/Lib/test/test_import/data/circular_imports/rebinding.py b/Lib/test/test_import/data/circular_imports/rebinding.py index f51d1c60ee0..2b773755591 100644 --- a/Lib/test/test_import/data/circular_imports/rebinding.py +++ b/Lib/test/test_import/data/circular_imports/rebinding.py @@ -1,3 +1,3 @@ """Test the binding of names when a circular import shares the same name as an attribute.""" -from .rebinding2 import util \ No newline at end of file +from .rebinding2 import util diff --git a/Lib/test/test_import/data/circular_imports/rebinding2.py b/Lib/test/test_import/data/circular_imports/rebinding2.py index 62ca7268603..57a9e6945d6 100644 --- a/Lib/test/test_import/data/circular_imports/rebinding2.py +++ b/Lib/test/test_import/data/circular_imports/rebinding2.py @@ -1,3 +1,3 @@ from .subpkg import util from . import rebinding -util = util.util \ No newline at end of file +util = util.util diff --git a/Lib/test/test_import/data/circular_imports/subpackage.py b/Lib/test/test_import/data/circular_imports/subpackage.py index cc36c3c3950..7b412f76fc7 100644 --- a/Lib/test/test_import/data/circular_imports/subpackage.py +++ b/Lib/test/test_import/data/circular_imports/subpackage.py @@ -1,2 +1,2 @@ """Circular import involving a sub-package.""" -from .subpkg import subpackage2 \ No newline at end of file +from .subpkg import subpackage2 diff --git a/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py b/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py index 07e0bc05457..17b893a1a2f 100644 --- a/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py +++ b/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py @@ -1,2 +1,2 @@ #from .util import util -from .. import subpackage \ No newline at end of file +from .. import subpackage diff --git a/Lib/test/test_import/data/circular_imports/subpkg/util.py b/Lib/test/test_import/data/circular_imports/subpkg/util.py index f1505e9d5ad..343bd843b2e 100644 --- a/Lib/test/test_import/data/circular_imports/subpkg/util.py +++ b/Lib/test/test_import/data/circular_imports/subpkg/util.py @@ -1,2 +1,2 @@ def util(): - pass \ No newline at end of file + pass diff --git a/Lib/test/test_import/data/circular_imports/util.py b/Lib/test/test_import/data/circular_imports/util.py index f1505e9d5ad..343bd843b2e 100644 --- a/Lib/test/test_import/data/circular_imports/util.py +++ b/Lib/test/test_import/data/circular_imports/util.py @@ -1,2 +1,2 @@ def util(): - pass \ No newline at end of file + pass