From ba98788bc545232a0e1b9458179253cf120176f6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 15 Mar 2012 13:57:27 -0500 Subject: bump to 3.1.5rc2 --- Include/patchlevel.h | 4 ++-- Lib/distutils/__init__.py | 2 +- Lib/idlelib/idlever.py | 2 +- Misc/NEWS | 5 +++++ Misc/RPM/python-3.1.spec | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 9fc6e97..ac162ab 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 1 #define PY_MICRO_VERSION 5 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_GAMMA -#define PY_RELEASE_SERIAL 1 +#define PY_RELEASE_SERIAL 2 /* Version as a string */ -#define PY_VERSION "3.1.5rc1" +#define PY_VERSION "3.1.5rc2" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository). Empty diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py index 0e42762..6ab1662 100644 --- a/Lib/distutils/__init__.py +++ b/Lib/distutils/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.5rc1" +__version__ = "3.1.5rc2" #--end constants-- diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py index 049da68..09d42d4 100644 --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1 +1 @@ -IDLE_VERSION = "3.1.5rc1" +IDLE_VERSION = "3.1.5rc2" diff --git a/Misc/NEWS b/Misc/NEWS index 07b981c..935b067 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -18,6 +18,11 @@ Core and Builtins Library ------- +- Issue #14234: CVE-2012-0876: Randomize hashes of xml attributes in the hash + table internal to the pyexpat module's copy of the expat library to avoid a + denial of service due to hash collisions. Patch by David Malcolm with some + modifications by the expat project. + - Issue #14001: CVE-2012-0845: xmlrpc: Fix an endless loop in SimpleXMLRPCServer upon malformed POST request. diff --git a/Misc/RPM/python-3.1.spec b/Misc/RPM/python-3.1.spec index c89c1da..235da2d 100644 --- a/Misc/RPM/python-3.1.spec +++ b/Misc/RPM/python-3.1.spec @@ -34,7 +34,7 @@ %define name python #--start constants-- -%define version 3.1.5rc1 +%define version 3.1.5rc2 %define libvers 3.1 #--end constants-- %define release 1pydotorg -- cgit v0.12 From d6c75b5a03fc98cf351dc6747bf9f676fb41a435 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 15 Mar 2012 13:58:31 -0500 Subject: Added tag v3.1.5rc2 for changeset 75db2bc69fc9 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 71d5cc9..b1c8242 100644 --- a/.hgtags +++ b/.hgtags @@ -78,3 +78,4 @@ a4f75773c0060cee38b0bb651a7aba6f56b0e996 v3.1.3 32fcb9e94985cb19ce37ba9543f091c0dbe9d7dd v3.1.4rc1 c918ec9f3a76d6afedfbb5d455004de880443a3d v3.1.4 ee26aca3219cf4bb0b93352e83edcc9cb28c7802 v3.1.5rc1 +75db2bc69fc9a3e4801e94e3e19801cb096208d8 v3.1.5rc2 -- cgit v0.12 From 8ac95ee62af237d21c695578cb19f54ddc1234a8 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Wed, 4 Apr 2012 17:31:16 -0400 Subject: Fix test_site from modifying sysconfig._CONFIG_VARS. --- Lib/test/test_site.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index ba42649..29286c7 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -39,6 +39,7 @@ class HelperFunctionsTests(unittest.TestCase): self.old_base = site.USER_BASE self.old_site = site.USER_SITE self.old_prefixes = site.PREFIXES + self.original_vars = sysconfig._CONFIG_VARS self.old_vars = copy(sysconfig._CONFIG_VARS) def tearDown(self): @@ -47,7 +48,9 @@ class HelperFunctionsTests(unittest.TestCase): site.USER_BASE = self.old_base site.USER_SITE = self.old_site site.PREFIXES = self.old_prefixes - sysconfig._CONFIG_VARS = self.old_vars + sysconfig._CONFIG_VARS = self.original_vars + sysconfig._CONFIG_VARS.clear() + sysconfig._CONFIG_VARS.update(self.old_vars) def test_makepath(self): # Test makepath() have an absolute path for its first return value -- cgit v0.12 From fee3fc748e4c6e00cdd44c079e54cacef00690cf Mon Sep 17 00:00:00 2001 From: Sandro Tosi Date: Thu, 5 Apr 2012 22:51:54 +0200 Subject: Issue #14502: release() and unlocked lock generates a ThreadError --- Doc/library/threading.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index 9b3affd..0738e22 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -430,7 +430,7 @@ All methods are executed atomically. are blocked waiting for the lock to become unlocked, allow exactly one of them to proceed. - Do not call this method when the lock is unlocked. + When invoked on an unlocked lock, a :exc:`ThreadError` is raised. There is no return value. -- cgit v0.12 From d3af6344ef43df20c91be8275a5e874dc0589830 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Thu, 5 Apr 2012 22:59:13 -0400 Subject: #14492: fix some bugs in Tools/scripts/pdeps.py. Initial patch by Popa Claudiu. --- Lib/test/test_tools.py | 27 +++++++++++++++++++++++++++ Tools/scripts/pdeps.py | 10 +++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_tools.py b/Lib/test/test_tools.py index 83a1e0d..cfe13ac 100644 --- a/Lib/test/test_tools.py +++ b/Lib/test/test_tools.py @@ -6,8 +6,10 @@ Tools directory of a Python checkout or tarball, such as reindent.py. import os import sys +import imp import unittest import sysconfig +import tempfile from test import support from test.script_helper import assert_python_ok @@ -72,6 +74,31 @@ class TestSundryScripts(unittest.TestCase): import analyze_dxp +class PdepsTests(unittest.TestCase): + + @classmethod + def setUpClass(self): + path = os.path.join(scriptsdir, 'pdeps.py') + self.pdeps = imp.load_source('pdeps', path) + + @classmethod + def tearDownClass(self): + if 'pdeps' in sys.modules: + del sys.modules['pdeps'] + + def test_process_errors(self): + # Issue #14492: m_import.match(line) can be None. + with tempfile.TemporaryDirectory() as tmpdir: + fn = os.path.join(tmpdir, 'foo') + with open(fn, 'w') as stream: + stream.write("#!/this/will/fail") + self.pdeps.process(fn, {}) + + def test_inverse_attribute_error(self): + # Issue #14492: this used to fail with an AttributeError. + self.pdeps.inverse({'a': []}) + + def test_main(): support.run_unittest(*[obj for obj in globals().values() if isinstance(obj, type)]) diff --git a/Tools/scripts/pdeps.py b/Tools/scripts/pdeps.py index 938f31c..f8218ac 100755 --- a/Tools/scripts/pdeps.py +++ b/Tools/scripts/pdeps.py @@ -76,10 +76,9 @@ def process(filename, table): nextline = fp.readline() if not nextline: break line = line[:-1] + nextline - if m_import.match(line) >= 0: - (a, b), (a1, b1) = m_import.regs[:2] - elif m_from.match(line) >= 0: - (a, b), (a1, b1) = m_from.regs[:2] + m_found = m_import.match(line) or m_from.match(line) + if m_found: + (a, b), (a1, b1) = m_found.regs[:2] else: continue words = line[a1:b1].split(',') # print '#', line, words @@ -87,6 +86,7 @@ def process(filename, table): word = word.strip() if word not in list: list.append(word) + fp.close() # Compute closure (this is in fact totally general) @@ -123,7 +123,7 @@ def closure(table): def inverse(table): inv = {} for key in table.keys(): - if not inv.has_key(key): + if key not in inv: inv[key] = [] for item in table[key]: store(inv, item, key) -- cgit v0.12 From f3be68e0a824d165c37c899ad9674918a870dc45 Mon Sep 17 00:00:00 2001 From: Sandro Tosi Date: Fri, 6 Apr 2012 11:15:06 +0200 Subject: Issue #14502: it's RuntimeError on 3.3 --- Doc/library/threading.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index 955fce2..d272731 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -452,7 +452,7 @@ All methods are executed atomically. are blocked waiting for the lock to become unlocked, allow exactly one of them to proceed. - When invoked on an unlocked lock, a :exc:`ThreadError` is raised. + When invoked on an unlocked lock, a :exc:`RuntimeError` is raised. There is no return value. -- cgit v0.12 From b2e58185e5ca329b16177ad2095b35adaaafdd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Fri, 6 Apr 2012 14:37:45 +0000 Subject: Set a time threshold in test_asyncore.capture_server so that tests don't deadlock if the main thread fails before sending all the data. --- Lib/test/test_asyncore.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index 1123c25..42a2525 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -74,15 +74,16 @@ def capture_server(evt, buf, serv): pass else: n = 200 - while n > 0: - r, w, e = select.select([conn], [], []) + start = time.time() + while n > 0 and time.time() - start < 3.0: + r, w, e = select.select([conn], [], [], 0.1) if r: + n -= 1 data = conn.recv(10) # keep everything except for the newline terminator buf.write(data.replace(b'\n', b'')) if b'\n' in data: break - n -= 1 time.sleep(0.01) conn.close() -- cgit v0.12 From d62cd5627fc5c01571c018b8a11b152614862eff Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 6 Apr 2012 13:13:08 -0400 Subject: Issue #14500: Fix importlib.test.import_.test_packages to clean up after itself properly. --- Lib/importlib/test/import_/test_packages.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/importlib/test/import_/test_packages.py b/Lib/importlib/test/import_/test_packages.py index 9590d5f..58be433 100644 --- a/Lib/importlib/test/import_/test_packages.py +++ b/Lib/importlib/test/import_/test_packages.py @@ -3,6 +3,7 @@ from . import util as import_util import sys import unittest import importlib +from test import support class ParentModuleTests(unittest.TestCase): @@ -38,7 +39,10 @@ class ParentModuleTests(unittest.TestCase): module_code={'mod': module_injection}) with mock_modules as mock: with util.import_state(meta_path=[mock]): - submodule = import_util.import_(subname) + try: + submodule = import_util.import_(subname) + finally: + support.unload(subname) def test_main(): -- cgit v0.12 From c37a4fdf5472b9460cc38885342ef59bce05753f Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 6 Apr 2012 13:17:25 -0400 Subject: bump to 3.1.5 final --- Include/patchlevel.h | 6 +++--- Lib/distutils/__init__.py | 2 +- Lib/idlelib/idlever.py | 2 +- Misc/NEWS | 2 +- Misc/RPM/python-3.1.spec | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 0bfb782..7d88be0 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -19,11 +19,11 @@ #define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 1 #define PY_MICRO_VERSION 5 -#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_GAMMA -#define PY_RELEASE_SERIAL 2 +#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL +#define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.1.5rc2" +#define PY_VERSION "3.1.5" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository). Empty diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py index 6ab1662..d79bfa1 100644 --- a/Lib/distutils/__init__.py +++ b/Lib/distutils/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.5rc2" +__version__ = "3.1.5" #--end constants-- diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py index 09d42d4..5e3095b 100644 --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1 +1 @@ -IDLE_VERSION = "3.1.5rc2" +IDLE_VERSION = "3.1.5" diff --git a/Misc/NEWS b/Misc/NEWS index 935b067..1b48772 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -5,7 +5,7 @@ Python News What's New in Python 3.1.5? =========================== -*Release date: 2012-02-23* +*Release date: 2012-04-08* Core and Builtins ----------------- diff --git a/Misc/RPM/python-3.1.spec b/Misc/RPM/python-3.1.spec index 235da2d..38c1b0c 100644 --- a/Misc/RPM/python-3.1.spec +++ b/Misc/RPM/python-3.1.spec @@ -34,7 +34,7 @@ %define name python #--start constants-- -%define version 3.1.5rc2 +%define version 3.1.5 %define libvers 3.1 #--end constants-- %define release 1pydotorg -- cgit v0.12 From 7fddb4b2f650208b9667f000b37603ee651c58e7 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 6 Apr 2012 13:17:30 -0400 Subject: Added tag v3.1.5 for changeset 7395330e495e --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index b1c8242..48eb117 100644 --- a/.hgtags +++ b/.hgtags @@ -79,3 +79,4 @@ a4f75773c0060cee38b0bb651a7aba6f56b0e996 v3.1.3 c918ec9f3a76d6afedfbb5d455004de880443a3d v3.1.4 ee26aca3219cf4bb0b93352e83edcc9cb28c7802 v3.1.5rc1 75db2bc69fc9a3e4801e94e3e19801cb096208d8 v3.1.5rc2 +7395330e495ec3316862ca1f6ce0aaf7bdf6785b v3.1.5 -- cgit v0.12 From dcb89f4f09c9d0d2fcca412db726f43a0268efce Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 6 Apr 2012 13:20:01 -0400 Subject: port 3.1.5rc2 tag --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index ac0b0a4..a5fac8b 100644 --- a/.hgtags +++ b/.hgtags @@ -78,6 +78,7 @@ a4f75773c0060cee38b0bb651a7aba6f56b0e996 v3.1.3 32fcb9e94985cb19ce37ba9543f091c0dbe9d7dd v3.1.4rc1 c918ec9f3a76d6afedfbb5d455004de880443a3d v3.1.4 ee26aca3219cf4bb0b93352e83edcc9cb28c7802 v3.1.5rc1 +75db2bc69fc9a3e4801e94e3e19801cb096208d8 v3.1.5rc2 7395330e495ec3316862ca1f6ce0aaf7bdf6785b v3.1.5 b37b7834757492d009b99cf0ca4d42d2153d7fac v3.2a1 56d4373cecb73c8b45126ba7b045b3c7b3f94b0b v3.2a2 -- cgit v0.12 From 0f9eec19ee1652a61d4b2e860e599c617d88b707 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Sat, 7 Apr 2012 07:09:57 +0200 Subject: Don't Py_DECREF NULL variable in io.IncrementalNewlineDecoder. Found with Clang's Static Analyzer. --- Misc/NEWS | 2 ++ Modules/_io/textio.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS b/Misc/NEWS index b4a0091..29bf33a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -19,6 +19,8 @@ Core and Builtins Library ------- +- Don't Py_DECREF NULL variable in io.IncrementalNewlineDecoder. + - Issue #8515: Set __file__ when run file in IDLE. Initial patch by Bruce Frederiksen. diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 833a527..ae105e5 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -460,7 +460,7 @@ _PyIncrementalNewlineDecoder_decode(PyObject *_self, output = PyUnicode_FromKindAndData(kind, translated, out); PyMem_Free(translated); if (!output) - goto error; + return NULL; } self->seennl |= seennl; } -- cgit v0.12 From 10f383a9376df13635bb53d5885d43297d0022cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sat, 7 Apr 2012 11:23:31 +0000 Subject: Issue #14310: inter-process socket duplication for windows --- Doc/library/socket.rst | 25 ++++++++++++ Lib/socket.py | 10 ++++- Lib/test/test_socket.py | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ Misc/NEWS | 3 ++ Modules/socketmodule.c | 73 +++++++++++++++++++++++++++++---- 5 files changed, 208 insertions(+), 8 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 69fa378..0315507 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -680,6 +680,16 @@ The module :mod:`socket` exports the following constants and functions: .. versionadded:: 3.3 +.. function:: fromshare(data) + + Instantiate a socket from data obtained from :meth:`~socket.share`. + The socket is assumed to be in blocking mode. + + Availability: Windows. + + .. versionadded:: 3.3 + + .. data:: SocketType This is a Python type object that represents the socket object type. It is the @@ -1082,6 +1092,21 @@ correspond to Unix system calls applicable to sockets. are disallowed. If *how* is :const:`SHUT_RDWR`, further sends and receives are disallowed. + +.. method:: socket.share(process_id) + + :platform: Windows + + Duplacet a socket and prepare it for sharing with a target process. The + target process must be provided with *process_id*. The resulting bytes object + can then be passed to the target process using some form of interprocess + communication and the socket can be recreated there using :func:`fromshare`. + Once this method has been called, it is safe to close the socket since + the operating system has already duplicated it for the target process. + + .. versionadded:: 3.3 + + Note that there are no methods :meth:`read` or :meth:`write`; use :meth:`~socket.recv` and :meth:`~socket.send` without *flags* argument instead. diff --git a/Lib/socket.py b/Lib/socket.py index 8b2d1cd..1378b0f 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -12,6 +12,7 @@ Functions: socket() -- create a new socket object socketpair() -- create a pair of new socket objects [*] fromfd() -- create a socket object from an open file descriptor [*] +fromshare() -- create a socket object from data received from socket.share() [*] gethostname() -- return the current hostname gethostbyname() -- map a hostname to its IP number gethostbyaddr() -- map an IP number or hostname to DNS info @@ -209,7 +210,6 @@ class socket(_socket.socket): self._closed = True return super().detach() - def fromfd(fd, family, type, proto=0): """ fromfd(fd, family, type[, proto]) -> socket object @@ -219,6 +219,14 @@ def fromfd(fd, family, type, proto=0): nfd = dup(fd) return socket(family, type, proto, nfd) +if hasattr(_socket.socket, "share"): + def fromshare(info): + """ fromshare(info) -> socket object + + Create a socket object from a the bytes object returned by + socket.share(pid). + """ + return socket(0, 0, 0, info) if hasattr(_socket, "socketpair"): diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 17c5486..6da423a 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -26,6 +26,10 @@ try: import fcntl except ImportError: fcntl = False +try: + import multiprocessing +except ImportError: + multiprocessing = False HOST = support.HOST MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') ## test unicode string and carriage return @@ -4643,6 +4647,106 @@ class NonblockConstantTest(unittest.TestCase): socket.setdefaulttimeout(t) +@unittest.skipUnless(os.name == "nt", "Windows specific") +@unittest.skipUnless(multiprocessing, "need multiprocessing") +class TestSocketSharing(SocketTCPTest): + # This must be classmethod and not staticmethod or multiprocessing + # won't be able to bootstrap it. + @classmethod + def remoteProcessServer(cls, q): + # Recreate socket from shared data + sdata = q.get() + message = q.get() + + s = socket.fromshare(sdata) + s2, c = s.accept() + + # Send the message + s2.sendall(message) + s2.close() + s.close() + + def testShare(self): + # Transfer the listening server socket to another process + # and service it from there. + + # Create process: + q = multiprocessing.Queue() + p = multiprocessing.Process(target=self.remoteProcessServer, args=(q,)) + p.start() + + # Get the shared socket data + data = self.serv.share(p.pid) + + # Pass the shared socket to the other process + addr = self.serv.getsockname() + self.serv.close() + q.put(data) + + # The data that the server will send us + message = b"slapmahfro" + q.put(message) + + # Connect + s = socket.create_connection(addr) + # listen for the data + m = [] + while True: + data = s.recv(100) + if not data: + break + m.append(data) + s.close() + received = b"".join(m) + self.assertEqual(received, message) + p.join() + + def testShareLength(self): + data = self.serv.share(os.getpid()) + self.assertRaises(ValueError, socket.fromshare, data[:-1]) + self.assertRaises(ValueError, socket.fromshare, data+b"foo") + + def compareSockets(self, org, other): + # socket sharing is expected to work only for blocking socket + # since the internal python timout value isn't transfered. + self.assertEqual(org.gettimeout(), None) + self.assertEqual(org.gettimeout(), other.gettimeout()) + + self.assertEqual(org.family, other.family) + self.assertEqual(org.type, other.type) + # If the user specified "0" for proto, then + # internally windows will have picked the correct value. + # Python introspection on the socket however will still return + # 0. For the shared socket, the python value is recreated + # from the actual value, so it may not compare correctly. + if org.proto != 0: + self.assertEqual(org.proto, other.proto) + + def testShareLocal(self): + data = self.serv.share(os.getpid()) + s = socket.fromshare(data) + try: + self.compareSockets(self.serv, s) + finally: + s.close() + + def testTypes(self): + families = [socket.AF_INET, socket.AF_INET6] + types = [socket.SOCK_STREAM, socket.SOCK_DGRAM] + for f in families: + for t in types: + source = socket.socket(f, t) + try: + data = source.share(os.getpid()) + shared = socket.fromshare(data) + try: + self.compareSockets(source, shared) + finally: + shared.close() + finally: + source.close() + + def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, TestExceptions, BufferIOTest, BasicTCPTest2, BasicUDPTest, UDPTimeoutTest ] @@ -4699,6 +4803,7 @@ def test_main(): # These are slow when setitimer() is not available InterruptedRecvTimeoutTest, InterruptedSendTimeoutTest, + TestSocketSharing, ]) thread_info = support.threading_setup() diff --git a/Misc/NEWS b/Misc/NEWS index 29bf33a..5164fbf 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -232,6 +232,9 @@ Library - Issue #14210: pdb now has tab-completion not only for command names, but also for their arguments, wherever possible. +- Issue #14310: Sockets can now be with other processes on Windows using + the api socket.socket.share() and socket.fromshare(). + Build ----- diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 59a2f28..936e930 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -3771,6 +3771,34 @@ PyDoc_STRVAR(sock_ioctl_doc, Control the socket with WSAIoctl syscall. Currently supported 'cmd' values are\n\ SIO_RCVALL: 'option' must be one of the socket.RCVALL_* constants.\n\ SIO_KEEPALIVE_VALS: 'option' is a tuple of (onoff, timeout, interval)."); +#endif + +#if defined(MS_WINDOWS) +static PyObject* +sock_share(PySocketSockObject *s, PyObject *arg) +{ + WSAPROTOCOL_INFO info; + DWORD processId; + int result; + + if (!PyArg_ParseTuple(arg, "I", &processId)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = WSADuplicateSocket(s->sock_fd, processId, &info); + Py_END_ALLOW_THREADS + if (result == SOCKET_ERROR) + return set_error(); + return PyBytes_FromStringAndSize((const char*)&info, sizeof(info)); +} +PyDoc_STRVAR(sock_share_doc, +"share(process_id) -> bytes\n\ +\n\ +Share the socket with another process. The target process id\n\ +must be provided and the resulting bytes object passed to the target\n\ +process. There the shared socket can be instantiated by calling\n\ +socket.fromshare()."); + #endif @@ -3803,6 +3831,10 @@ static PyMethodDef sock_methods[] = { {"ioctl", (PyCFunction)sock_ioctl, METH_VARARGS, sock_ioctl_doc}, #endif +#if defined(MS_WINDOWS) + {"share", (PyCFunction)sock_share, METH_VARARGS, + sock_share_doc}, +#endif {"listen", (PyCFunction)sock_listen, METH_O, listen_doc}, {"recv", (PyCFunction)sock_recv, METH_VARARGS, @@ -3930,13 +3962,40 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) return -1; if (fdobj != NULL && fdobj != Py_None) { - fd = PyLong_AsSocket_t(fdobj); - if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) - return -1; - if (fd == INVALID_SOCKET) { - PyErr_SetString(PyExc_ValueError, - "can't use invalid socket value"); - return -1; +#ifdef MS_WINDOWS + /* recreate a socket that was duplicated */ + if (PyBytes_Check(fdobj)) { + WSAPROTOCOL_INFO info; + if (PyBytes_GET_SIZE(fdobj) != sizeof(info)) { + PyErr_Format(PyExc_ValueError, + "socket descriptor string has wrong size, " + "should be %zu bytes.", sizeof(info)); + return -1; + } + memcpy(&info, PyBytes_AS_STRING(fdobj), sizeof(info)); + Py_BEGIN_ALLOW_THREADS + fd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, &info, 0, WSA_FLAG_OVERLAPPED); + Py_END_ALLOW_THREADS + if (fd == INVALID_SOCKET) { + set_error(); + return -1; + } + family = info.iAddressFamily; + type = info.iSocketType; + proto = info.iProtocol; + } + else +#endif + { + fd = PyLong_AsSocket_t(fdobj); + if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) + return -1; + if (fd == INVALID_SOCKET) { + PyErr_SetString(PyExc_ValueError, + "can't use invalid socket value"); + return -1; + } } } else { -- cgit v0.12