Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d69cfe8

Browse files
committed
Issue #15064: Implement context manager protocol for multiprocessing types
1 parent 0f88427 commit d69cfe8

6 files changed

Lines changed: 89 additions & 1 deletion

File tree

Doc/library/multiprocessing.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,10 @@ Connection objects are usually created using :func:`Pipe` -- see also
834834
Connection objects themselves can now be transferred between processes
835835
using :meth:`Connection.send` and :meth:`Connection.recv`.
836836

837+
.. versionadded:: 3.3
838+
Connection objects now support the context manager protocol -- see
839+
:ref:`typecontextmanager`. :meth:`__enter__` returns the
840+
connection object, and :meth:`__exit__` calls :meth:`close`.
837841

838842
For example:
839843

@@ -1277,6 +1281,9 @@ their parent process exits. The manager classes are defined in the
12771281

12781282
The address used by the manager.
12791283

1284+
Manager objects support the context manager protocol -- see
1285+
:ref:`typecontextmanager`. :meth:`__enter__` returns the
1286+
manager object, and :meth:`__exit__` calls :meth:`shutdown`.
12801287

12811288
.. class:: SyncManager
12821289

@@ -1747,6 +1754,11 @@ with the :class:`Pool` class.
17471754
Wait for the worker processes to exit. One must call :meth:`close` or
17481755
:meth:`terminate` before using :meth:`join`.
17491756

1757+
.. versionadded:: 3.3
1758+
Pool objects now support the context manager protocol -- see
1759+
:ref:`typecontextmanager`. :meth:`__enter__` returns the pool
1760+
object, and :meth:`__exit__` calls :meth:`terminate`.
1761+
17501762

17511763
.. class:: AsyncResult
17521764

@@ -1911,6 +1923,11 @@ multiple connections at the same time.
19111923
The address from which the last accepted connection came. If this is
19121924
unavailable then it is ``None``.
19131925

1926+
.. versionadded:: 3.3
1927+
Listener objects now support the context manager protocol -- see
1928+
:ref:`typecontextmanager`. :meth:`__enter__` returns the
1929+
listener object, and :meth:`__exit__` calls :meth:`close`.
1930+
19141931
.. function:: wait(object_list, timeout=None)
19151932

19161933
Wait till an object in *object_list* is ready. Returns the list of

Lib/multiprocessing/connection.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,12 @@ def poll(self, timeout=0.0):
257257
self._check_readable()
258258
return self._poll(timeout)
259259

260+
def __enter__(self):
261+
return self
262+
263+
def __exit__(self, exc_type, exc_value, exc_tb):
264+
self.close()
265+
260266

261267
if _winapi:
262268

@@ -436,6 +442,8 @@ def accept(self):
436442
437443
Returns a `Connection` object.
438444
'''
445+
if self._listener is None:
446+
raise IOError('listener is closed')
439447
c = self._listener.accept()
440448
if self._authkey:
441449
deliver_challenge(c, self._authkey)
@@ -446,11 +454,19 @@ def close(self):
446454
'''
447455
Close the bound socket or named pipe of `self`.
448456
'''
449-
return self._listener.close()
457+
if self._listener is not None:
458+
self._listener.close()
459+
self._listener = None
450460

451461
address = property(lambda self: self._listener._address)
452462
last_accepted = property(lambda self: self._listener._last_accepted)
453463

464+
def __enter__(self):
465+
return self
466+
467+
def __exit__(self, exc_type, exc_value, exc_tb):
468+
self.close()
469+
454470

455471
def Client(address, family=None, authkey=None):
456472
'''

Lib/multiprocessing/dummy/connection.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ def close(self):
5353

5454
address = property(lambda self: self._backlog_queue)
5555

56+
def __enter__(self):
57+
return self
58+
59+
def __exit__(self, exc_type, exc_value, exc_tb):
60+
self.close()
61+
5662

5763
def Client(address):
5864
_in, _out = Queue(), Queue()
@@ -85,3 +91,9 @@ def poll(self, timeout=0.0):
8591

8692
def close(self):
8793
pass
94+
95+
def __enter__(self):
96+
return self
97+
98+
def __exit__(self, exc_type, exc_value, exc_tb):
99+
self.close()

Lib/multiprocessing/pool.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,12 @@ def _terminate_pool(cls, taskqueue, inqueue, outqueue, pool,
522522
debug('cleaning up worker %d' % p.pid)
523523
p.join()
524524

525+
def __enter__(self):
526+
return self
527+
528+
def __exit__(self, exc_type, exc_val, exc_tb):
529+
self.terminate()
530+
525531
#
526532
# Class whose instances are returned by `Pool.apply_async()`
527533
#

Lib/test/test_multiprocessing.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,15 @@ def test_empty_iterable(self):
17191719
p.close()
17201720
p.join()
17211721

1722+
def test_context(self):
1723+
if self.TYPE == 'processes':
1724+
L = list(range(10))
1725+
expected = [sqr(i) for i in L]
1726+
with multiprocessing.Pool(2) as p:
1727+
r = p.map_async(sqr, L)
1728+
self.assertEqual(r.get(), expected)
1729+
self.assertRaises(AssertionError, p.map_async, sqr, L)
1730+
17221731
def raising():
17231732
raise KeyError("key")
17241733

@@ -2266,6 +2275,22 @@ def test_missing_fd_transfer(self):
22662275
self.assertRaises(RuntimeError, reduction.recv_handle, conn)
22672276
p.join()
22682277

2278+
def test_context(self):
2279+
a, b = self.Pipe()
2280+
2281+
with a, b:
2282+
a.send(1729)
2283+
self.assertEqual(b.recv(), 1729)
2284+
if self.TYPE == 'processes':
2285+
self.assertFalse(a.closed)
2286+
self.assertFalse(b.closed)
2287+
2288+
if self.TYPE == 'processes':
2289+
self.assertTrue(a.closed)
2290+
self.assertTrue(b.closed)
2291+
self.assertRaises(IOError, a.recv)
2292+
self.assertRaises(IOError, b.recv)
2293+
22692294
class _TestListener(BaseTestCase):
22702295

22712296
ALLOWED_TYPES = ('processes',)
@@ -2277,6 +2302,16 @@ def test_multiple_bind(self):
22772302
self.assertRaises(OSError, self.connection.Listener,
22782303
l.address, family)
22792304

2305+
def test_context(self):
2306+
with self.connection.Listener() as l:
2307+
with self.connection.Client(l.address) as c:
2308+
with l.accept() as d:
2309+
c.send(1729)
2310+
self.assertEqual(d.recv(), 1729)
2311+
2312+
if self.TYPE == 'processes':
2313+
self.assertRaises(IOError, l.accept)
2314+
22802315
class _TestListenerClient(BaseTestCase):
22812316

22822317
ALLOWED_TYPES = ('processes', 'threads')

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Core and Builtins
2929
Library
3030
-------
3131

32+
- Issue #15064: Implement context manager protocol for multiprocessing types
33+
3234
- Issue #15101: Make pool finalizer avoid joining current thread.
3335

3436
- Issue #14657: The frozen instance of importlib used for bootstrap is now

0 commit comments

Comments
 (0)