Skip to content

Commit 45dddb7

Browse files
committed
Merge branch 'master' of https://github.com/python/cpython into asyncio_mock
2 parents aec3153 + f7bda5c commit 45dddb7

11 files changed

Lines changed: 105 additions & 57 deletions

File tree

Doc/library/http.cookies.rst

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ in Cookie name (as :attr:`~Morsel.key`).
5555
.. class:: SimpleCookie([input])
5656

5757
This class derives from :class:`BaseCookie` and overrides :meth:`value_decode`
58-
and :meth:`value_encode` to be the identity and :func:`str` respectively.
59-
58+
and :meth:`value_encode`. SimpleCookie supports strings as cookie values.
59+
When setting the value, SimpleCookie calls the builtin :func:`str()` to convert
60+
the value to a string. Values received from HTTP are kept as strings.
6061

6162
.. seealso::
6263

@@ -76,15 +77,16 @@ Cookie Objects
7677

7778
.. method:: BaseCookie.value_decode(val)
7879

79-
Return a decoded value from a string representation. Return value can be any
80-
type. This method does nothing in :class:`BaseCookie` --- it exists so it can be
81-
overridden.
80+
Return a tuple ``(real_value, coded_value)`` from a string representation.
81+
``real_value`` can be any type. This method does no decoding in
82+
:class:`BaseCookie` --- it exists so it can be overridden.
8283

8384

8485
.. method:: BaseCookie.value_encode(val)
8586

86-
Return an encoded value. *val* can be any type, but return value must be a
87-
string. This method does nothing in :class:`BaseCookie` --- it exists so it can
87+
Return a tuple ``(real_value, coded_value)``. *val* can be any type, but
88+
``coded_value`` will always be converted to a string.
89+
This method does no encoding in :class:`BaseCookie` --- it exists so it can
8890
be overridden.
8991

9092
In general, it should be the case that :meth:`value_encode` and

Doc/library/signal.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ The :func:`signal.signal` function allows defining custom handlers to be
1616
executed when a signal is received. A small number of default handlers are
1717
installed: :const:`SIGPIPE` is ignored (so write errors on pipes and sockets
1818
can be reported as ordinary Python exceptions) and :const:`SIGINT` is
19-
translated into a :exc:`KeyboardInterrupt` exception.
19+
translated into a :exc:`KeyboardInterrupt` exception if the parent process
20+
has not changed it.
2021

2122
A handler for a particular signal, once set, remains installed until it is
2223
explicitly reset (Python emulates the BSD style interface regardless of the

Lib/asyncio/base_events.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1306,7 +1306,8 @@ async def create_datagram_endpoint(self, protocol_factory,
13061306
if local_addr:
13071307
sock.bind(local_address)
13081308
if remote_addr:
1309-
await self.sock_connect(sock, remote_address)
1309+
if not allow_broadcast:
1310+
await self.sock_connect(sock, remote_address)
13101311
r_addr = remote_address
13111312
except OSError as exc:
13121313
if sock is not None:

Lib/asyncio/selector_events.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,10 @@ class _SelectorTransport(transports._FlowControlMixin,
587587
def __init__(self, loop, sock, protocol, extra=None, server=None):
588588
super().__init__(extra, loop)
589589
self._extra['socket'] = sock
590-
self._extra['sockname'] = sock.getsockname()
590+
try:
591+
self._extra['sockname'] = sock.getsockname()
592+
except OSError:
593+
self._extra['sockname'] = None
591594
if 'peername' not in self._extra:
592595
try:
593596
self._extra['peername'] = sock.getpeername()
@@ -976,9 +979,11 @@ def sendto(self, data, addr=None):
976979
if not data:
977980
return
978981

979-
if self._address and addr not in (None, self._address):
980-
raise ValueError(
981-
f'Invalid address: must be None or {self._address}')
982+
if self._address:
983+
if addr not in (None, self._address):
984+
raise ValueError(
985+
f'Invalid address: must be None or {self._address}')
986+
addr = self._address
982987

983988
if self._conn_lost and self._address:
984989
if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
@@ -989,7 +994,7 @@ def sendto(self, data, addr=None):
989994
if not self._buffer:
990995
# Attempt to send it right away first.
991996
try:
992-
if self._address:
997+
if self._extra['peername']:
993998
self._sock.send(data)
994999
else:
9951000
self._sock.sendto(data, addr)
@@ -1012,7 +1017,7 @@ def _sendto_ready(self):
10121017
while self._buffer:
10131018
data, addr = self._buffer.popleft()
10141019
try:
1015-
if self._address:
1020+
if self._extra['peername']:
10161021
self._sock.send(data)
10171022
else:
10181023
self._sock.sendto(data, addr)

Lib/concurrent/futures/process.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
import queue
5252
from queue import Full
5353
import multiprocessing as mp
54-
from multiprocessing.connection import wait
54+
import multiprocessing.connection
5555
from multiprocessing.queues import Queue
5656
import threading
5757
import weakref
@@ -352,7 +352,7 @@ def shutdown_worker():
352352
# submitted, from the executor being shutdown/gc-ed, or from the
353353
# shutdown of the python interpreter.
354354
worker_sentinels = [p.sentinel for p in processes.values()]
355-
ready = wait(readers + worker_sentinels)
355+
ready = mp.connection.wait(readers + worker_sentinels)
356356

357357
cause = None
358358
is_broken = True

Lib/logging/__init__.py

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -231,49 +231,38 @@ def _releaseLock():
231231
# Prevent a held logging lock from blocking a child from logging.
232232

233233
if not hasattr(os, 'register_at_fork'): # Windows and friends.
234-
def _register_at_fork_acquire_release(instance):
234+
def _register_at_fork_reinit_lock(instance):
235235
pass # no-op when os.register_at_fork does not exist.
236-
else: # The os.register_at_fork API exists
237-
os.register_at_fork(before=_acquireLock,
238-
after_in_child=_releaseLock,
239-
after_in_parent=_releaseLock)
240-
241-
# A collection of instances with acquire and release methods (logging.Handler)
242-
# to be called before and after fork. The weakref avoids us keeping discarded
243-
# Handler instances alive forever in case an odd program creates and destroys
244-
# many over its lifetime.
245-
_at_fork_acquire_release_weakset = weakref.WeakSet()
246-
247-
248-
def _register_at_fork_acquire_release(instance):
249-
# We put the instance itself in a single WeakSet as we MUST have only
250-
# one atomic weak ref. used by both before and after atfork calls to
251-
# guarantee matched pairs of acquire and release calls.
252-
_at_fork_acquire_release_weakset.add(instance)
253-
236+
else:
237+
# A collection of instances with a createLock method (logging.Handler)
238+
# to be called in the child after forking. The weakref avoids us keeping
239+
# discarded Handler instances alive. A set is used to avoid accumulating
240+
# duplicate registrations as createLock() is responsible for registering
241+
# a new Handler instance with this set in the first place.
242+
_at_fork_reinit_lock_weakset = weakref.WeakSet()
243+
244+
def _register_at_fork_reinit_lock(instance):
245+
_acquireLock()
246+
try:
247+
_at_fork_reinit_lock_weakset.add(instance)
248+
finally:
249+
_releaseLock()
254250

255-
def _at_fork_weak_calls(method_name):
256-
for instance in _at_fork_acquire_release_weakset:
257-
method = getattr(instance, method_name)
251+
def _after_at_fork_child_reinit_locks():
252+
# _acquireLock() was called in the parent before forking.
253+
for handler in _at_fork_reinit_lock_weakset:
258254
try:
259-
method()
255+
handler.createLock()
260256
except Exception as err:
261257
# Similar to what PyErr_WriteUnraisable does.
262258
print("Ignoring exception from logging atfork", instance,
263-
method_name, "method:", err, file=sys.stderr)
264-
265-
266-
def _before_at_fork_weak_calls():
267-
_at_fork_weak_calls('acquire')
259+
"._reinit_lock() method:", err, file=sys.stderr)
260+
_releaseLock() # Acquired by os.register_at_fork(before=.
268261

269262

270-
def _after_at_fork_weak_calls():
271-
_at_fork_weak_calls('release')
272-
273-
274-
os.register_at_fork(before=_before_at_fork_weak_calls,
275-
after_in_child=_after_at_fork_weak_calls,
276-
after_in_parent=_after_at_fork_weak_calls)
263+
os.register_at_fork(before=_acquireLock,
264+
after_in_child=_after_at_fork_child_reinit_locks,
265+
after_in_parent=_releaseLock)
277266

278267

279268
#---------------------------------------------------------------------------
@@ -900,7 +889,7 @@ def createLock(self):
900889
Acquire a thread lock for serializing access to the underlying I/O.
901890
"""
902891
self.lock = threading.RLock()
903-
_register_at_fork_acquire_release(self)
892+
_register_at_fork_reinit_lock(self)
904893

905894
def acquire(self):
906895
"""

Lib/test/test_asyncio/test_base_events.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,6 +1586,23 @@ def test_create_datagram_endpoint_connect_err(self):
15861586
self.assertRaises(
15871587
OSError, self.loop.run_until_complete, coro)
15881588

1589+
def test_create_datagram_endpoint_allow_broadcast(self):
1590+
protocol = MyDatagramProto(create_future=True, loop=self.loop)
1591+
self.loop.sock_connect = sock_connect = mock.Mock()
1592+
sock_connect.return_value = []
1593+
1594+
coro = self.loop.create_datagram_endpoint(
1595+
lambda: protocol,
1596+
remote_addr=('127.0.0.1', 0),
1597+
allow_broadcast=True)
1598+
1599+
transport, _ = self.loop.run_until_complete(coro)
1600+
self.assertFalse(sock_connect.called)
1601+
1602+
transport.close()
1603+
self.loop.run_until_complete(protocol.done)
1604+
self.assertEqual('CLOSED', protocol.state)
1605+
15891606
@patch_socket
15901607
def test_create_datagram_endpoint_socket_err(self, m_socket):
15911608
m_socket.getaddrinfo = socket.getaddrinfo

Lib/test/test_asyncio/test_selector_events.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,7 @@ def setUp(self):
10651065
self.sock.fileno.return_value = 7
10661066

10671067
def datagram_transport(self, address=None):
1068+
self.sock.getpeername.side_effect = None if address else OSError
10681069
transport = _SelectorDatagramTransport(self.loop, self.sock,
10691070
self.protocol,
10701071
address=address)

Lib/test/test_logging.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -668,10 +668,28 @@ def remove_loop(fname, tries):
668668
# register_at_fork mechanism is also present and used.
669669
@unittest.skipIf(not hasattr(os, 'fork'), 'Test requires os.fork().')
670670
def test_post_fork_child_no_deadlock(self):
671-
"""Ensure forked child logging locks are not held; bpo-6721."""
672-
refed_h = logging.Handler()
671+
"""Ensure child logging locks are not held; bpo-6721 & bpo-36533."""
672+
class _OurHandler(logging.Handler):
673+
def __init__(self):
674+
super().__init__()
675+
self.sub_handler = logging.StreamHandler(
676+
stream=open('/dev/null', 'wt'))
677+
678+
def emit(self, record):
679+
self.sub_handler.acquire()
680+
try:
681+
self.sub_handler.emit(record)
682+
finally:
683+
self.sub_handler.release()
684+
685+
self.assertEqual(len(logging._handlers), 0)
686+
refed_h = _OurHandler()
673687
refed_h.name = 'because we need at least one for this test'
674688
self.assertGreater(len(logging._handlers), 0)
689+
self.assertGreater(len(logging._at_fork_reinit_lock_weakset), 1)
690+
test_logger = logging.getLogger('test_post_fork_child_no_deadlock')
691+
test_logger.addHandler(refed_h)
692+
test_logger.setLevel(logging.DEBUG)
675693

676694
locks_held__ready_to_fork = threading.Event()
677695
fork_happened__release_locks_and_end_thread = threading.Event()
@@ -709,19 +727,24 @@ def lock_holder_thread_fn():
709727
locks_held__ready_to_fork.wait()
710728
pid = os.fork()
711729
if pid == 0: # Child.
712-
logging.error(r'Child process did not deadlock. \o/')
713-
os._exit(0)
730+
try:
731+
test_logger.info(r'Child process did not deadlock. \o/')
732+
finally:
733+
os._exit(0)
714734
else: # Parent.
735+
test_logger.info(r'Parent process returned from fork. \o/')
715736
fork_happened__release_locks_and_end_thread.set()
716737
lock_holder_thread.join()
717738
start_time = time.monotonic()
718739
while True:
740+
test_logger.debug('Waiting for child process.')
719741
waited_pid, status = os.waitpid(pid, os.WNOHANG)
720742
if waited_pid == pid:
721743
break # child process exited.
722744
if time.monotonic() - start_time > 7:
723745
break # so long? implies child deadlock.
724746
time.sleep(0.05)
747+
test_logger.debug('Done waiting.')
725748
if waited_pid != pid:
726749
os.kill(pid, signal.SIGKILL)
727750
waited_pid, status = os.waitpid(pid, 0)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:meth:`asyncio.AbstractEventLoop.create_datagram_endpoint`:
2+
Do not connect UDP socket when broadcast is allowed.
3+
This allows to receive replies after a UDP broadcast.

0 commit comments

Comments
 (0)