Skip to content

Commit 22bb71f

Browse files
authored
Merge branch 'master' into fix/accept-pathlike-subprocess-args
2 parents e455812 + 9e017e6 commit 22bb71f

File tree

6 files changed

+171
-13
lines changed

6 files changed

+171
-13
lines changed

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ recursive-include vendor/libuv *
66
recursive-exclude vendor/libuv/.git *
77
recursive-exclude vendor/libuv/docs *
88
recursive-exclude vendor/libuv/img *
9-
include LICENSE-MIT LICENSE-APACHE README.rst Makefile performance.png
9+
include LICENSE-MIT LICENSE-APACHE README.rst Makefile performance.png .flake8

tests/test_base.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,46 @@ async def coro():
541541
self.assertFalse(isinstance(task, MyTask))
542542
self.loop.run_until_complete(task)
543543

544+
def test_set_task_name(self):
545+
if self.implementation == 'asyncio' and sys.version_info < (3, 8, 0):
546+
raise unittest.SkipTest('unsupported task name')
547+
548+
self.loop._process_events = mock.Mock()
549+
550+
result = None
551+
552+
class MyTask(asyncio.Task):
553+
def set_name(self, name):
554+
nonlocal result
555+
result = name + "!"
556+
557+
def get_name(self):
558+
return result
559+
560+
async def coro():
561+
pass
562+
563+
factory = lambda loop, coro: MyTask(coro, loop=loop)
564+
565+
self.assertIsNone(self.loop.get_task_factory())
566+
task = self.loop.create_task(coro(), name="mytask")
567+
self.assertFalse(isinstance(task, MyTask))
568+
if sys.version_info >= (3, 8, 0):
569+
self.assertEqual(task.get_name(), "mytask")
570+
self.loop.run_until_complete(task)
571+
572+
self.loop.set_task_factory(factory)
573+
self.assertIs(self.loop.get_task_factory(), factory)
574+
575+
task = self.loop.create_task(coro(), name="mytask")
576+
self.assertTrue(isinstance(task, MyTask))
577+
self.assertEqual(result, "mytask!")
578+
self.assertEqual(task.get_name(), "mytask!")
579+
self.loop.run_until_complete(task)
580+
581+
self.loop.set_task_factory(None)
582+
self.assertIsNone(self.loop.get_task_factory())
583+
544584
def _compile_agen(self, src):
545585
try:
546586
g = {}

tests/test_udp.py

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ def datagram_received(self, data, addr):
5656

5757
s_transport, server = self.loop.run_until_complete(coro)
5858

59-
host, port, *_ = s_transport.get_extra_info('sockname')
59+
remote_addr = s_transport.get_extra_info('sockname')
60+
host, port, *_ = remote_addr
6061

6162
self.assertIsInstance(server, TestMyDatagramProto)
6263
self.assertEqual('INITIALIZED', server.state)
@@ -86,6 +87,36 @@ def datagram_received(self, data, addr):
8687
# received
8788
self.assertEqual(8, client.nbytes)
8889

90+
# https://github.com/MagicStack/uvloop/issues/319
91+
# uvloop should behave the same as asyncio when given remote_addr
92+
transport.sendto(b'xxx', remote_addr)
93+
tb.run_until(
94+
self.loop, lambda: server.nbytes > 3 or client.done.done())
95+
self.assertEqual(6, server.nbytes)
96+
tb.run_until(self.loop, lambda: client.nbytes > 8)
97+
98+
# received
99+
self.assertEqual(16, client.nbytes)
100+
101+
# reject sendto with a different port
102+
with self.assertRaisesRegex(
103+
ValueError, "Invalid address.*" + repr(remote_addr)
104+
):
105+
bad_addr = list(remote_addr)
106+
bad_addr[1] += 1
107+
bad_addr = tuple(bad_addr)
108+
transport.sendto(b"xxx", bad_addr)
109+
110+
# reject sento with unresolved hostname
111+
if remote_addr[0] != lc_addr[0]:
112+
with self.assertRaisesRegex(
113+
ValueError, "Invalid address.*" + repr(remote_addr)
114+
):
115+
bad_addr = list(remote_addr)
116+
bad_addr[0] = lc_addr[0]
117+
bad_addr = tuple(bad_addr)
118+
transport.sendto(b"xxx", bad_addr)
119+
89120
# extra info is available
90121
self.assertIsNotNone(transport.get_extra_info('sockname'))
91122

@@ -100,8 +131,11 @@ def test_create_datagram_endpoint_addrs_ipv4(self):
100131
self._test_create_datagram_endpoint_addrs(
101132
socket.AF_INET, ('127.0.0.1', 0))
102133

103-
@unittest.skipUnless(tb.has_IPv6, 'no IPv6')
104-
def test_create_datagram_endpoint_addrs_ipv6(self):
134+
def test_create_datagram_endpoint_addrs_ipv4_nameaddr(self):
135+
self._test_create_datagram_endpoint_addrs(
136+
socket.AF_INET, ('localhost', 0))
137+
138+
def _test_create_datagram_endpoint_addrs_ipv6(self):
105139
self._test_create_datagram_endpoint_addrs(
106140
socket.AF_INET6, ('::1', 0))
107141

@@ -250,6 +284,50 @@ async def run():
250284

251285
self.loop.run_until_complete(run())
252286

287+
def test_socketpair(self):
288+
peername = asyncio.Future(loop=self.loop)
289+
290+
class Proto(MyDatagramProto):
291+
def datagram_received(self, data, addr):
292+
super().datagram_received(data, addr)
293+
peername.set_result(addr)
294+
295+
s1, s2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
296+
297+
with s1, s2:
298+
try:
299+
f = self.loop.create_datagram_endpoint(
300+
lambda: Proto(loop=self.loop), sock=s1)
301+
except TypeError as ex:
302+
# asyncio in 3.5.0 doesn't have the 'sock' argument
303+
if 'got an unexpected keyword argument' not in ex.args[0]:
304+
raise
305+
else:
306+
tr, pr = self.loop.run_until_complete(f)
307+
self.assertIsInstance(pr, Proto)
308+
309+
s2.send(b'hello, socketpair')
310+
addr = self.loop.run_until_complete(
311+
asyncio.wait_for(peername, 1, loop=self.loop))
312+
if sys.platform.startswith('linux'):
313+
self.assertEqual(addr, None)
314+
else:
315+
self.assertEqual(addr, '')
316+
self.assertEqual(pr.nbytes, 17)
317+
318+
if not self.is_asyncio_loop():
319+
# asyncio doesn't support sendto(xx) on UDP sockets
320+
# https://git.io/Jfqbw
321+
data = b'from uvloop'
322+
tr.sendto(data)
323+
result = self.loop.run_until_complete(asyncio.wait_for(
324+
self.loop.run_in_executor(None, s2.recv, 1024),
325+
1, loop=self.loop))
326+
self.assertEqual(data, result)
327+
328+
tr.close()
329+
self.loop.run_until_complete(pr.done)
330+
253331

254332
class Test_UV_UDP(_TestUDP, tb.UVTestCase):
255333

@@ -291,6 +369,16 @@ def test_send_after_close(self):
291369
self.loop.run_until_complete(asyncio.sleep(0.01))
292370
s_transport.sendto(b'aaaa', ('127.0.0.1', 80))
293371

372+
@unittest.skipUnless(tb.has_IPv6, 'no IPv6')
373+
def test_create_datagram_endpoint_addrs_ipv6(self):
374+
self._test_create_datagram_endpoint_addrs_ipv6()
375+
294376

295377
class Test_AIO_UDP(_TestUDP, tb.AIOTestCase):
296-
pass
378+
@unittest.skipUnless(tb.has_IPv6, 'no IPv6')
379+
@unittest.skipIf(
380+
sys.version_info[:3] < (3, 6, 7) or sys.version_info[:3] == (3, 7, 0),
381+
'bpo-27500: bug fixed in Python 3.6.7, 3.7.1 and above.',
382+
)
383+
def test_create_datagram_endpoint_addrs_ipv6(self):
384+
self._test_create_datagram_endpoint_addrs_ipv6()

uvloop/handles/udp.pxd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ cdef class UDPTransport(UVBaseTransport):
22
cdef:
33
bint __receiving
44
int _family
5+
object _address
56

67
cdef _init(self, Loop loop, unsigned int family)
8+
cdef _set_address(self, system.addrinfo *addr)
79

810
cdef _connect(self, system.sockaddr* addr, size_t addr_len)
911

uvloop/handles/udp.pyx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ cdef class UDPTransport(UVBaseTransport):
5555
def __cinit__(self):
5656
self._family = uv.AF_UNSPEC
5757
self.__receiving = 0
58+
self._address = None
5859

5960
cdef _init(self, Loop loop, unsigned int family):
6061
cdef int err
@@ -78,6 +79,9 @@ cdef class UDPTransport(UVBaseTransport):
7879

7980
self._finish_init()
8081

82+
cdef _set_address(self, system.addrinfo *addr):
83+
self._address = __convert_sockaddr_to_pyaddr(addr.ai_addr)
84+
8185
cdef _connect(self, system.sockaddr* addr, size_t addr_len):
8286
cdef int err
8387
err = uv.uv_udp_connect(<uv.uv_udp_t*>self._handle, addr)
@@ -279,6 +283,16 @@ cdef class UDPTransport(UVBaseTransport):
279283
# Replicating asyncio logic here.
280284
return
281285

286+
if self._address:
287+
if addr not in (None, self._address):
288+
# Replicating asyncio logic here.
289+
raise ValueError(
290+
'Invalid address: must be None or %s' % (self._address,))
291+
292+
# Instead of setting addr to self._address below like what asyncio
293+
# does, we depend on previous uv_udp_connect() to set the address
294+
addr = None
295+
282296
if self._conn_lost:
283297
# Replicating asyncio logic here.
284298
if self._conn_lost >= LOG_THRESHOLD_FOR_CONNLOST_WRITES:
@@ -330,6 +344,12 @@ cdef void __uv_udp_on_receive(uv.uv_udp_t* handle,
330344

331345
if addr is NULL:
332346
pyaddr = None
347+
elif addr.sa_family == uv.AF_UNSPEC:
348+
# https://github.com/MagicStack/uvloop/issues/304
349+
IF UNAME_SYSNAME == "Linux":
350+
pyaddr = None
351+
ELSE:
352+
pyaddr = ''
333353
else:
334354
try:
335355
pyaddr = __convert_sockaddr_to_pyaddr(addr)
@@ -342,13 +362,6 @@ cdef void __uv_udp_on_receive(uv.uv_udp_t* handle,
342362
udp._on_receive(None, exc, pyaddr)
343363
return
344364

345-
if pyaddr is None:
346-
udp._fatal_error(
347-
RuntimeError(
348-
'uv_udp.receive callback: addr is NULL and nread >= 0'),
349-
False)
350-
return
351-
352365
if nread == 0:
353366
data = b''
354367
else:

uvloop/loop.pyx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1386,16 +1386,29 @@ cdef class Loop:
13861386
"""Create a Future object attached to the loop."""
13871387
return self._new_future()
13881388

1389-
def create_task(self, coro):
1389+
def create_task(self, coro, *, name=None):
13901390
"""Schedule a coroutine object.
13911391
13921392
Return a task object.
1393+
1394+
If name is not None, task.set_name(name) will be called if the task
1395+
object has the set_name attribute, true for default Task in Python 3.8.
13931396
"""
13941397
self._check_closed()
13951398
if self._task_factory is None:
13961399
task = aio_Task(coro, loop=self)
13971400
else:
13981401
task = self._task_factory(self, coro)
1402+
1403+
# copied from asyncio.tasks._set_task_name (bpo-34270)
1404+
if name is not None:
1405+
try:
1406+
set_name = task.set_name
1407+
except AttributeError:
1408+
pass
1409+
else:
1410+
set_name(name)
1411+
13991412
return task
14001413

14011414
def set_task_factory(self, factory):
@@ -3004,6 +3017,7 @@ cdef class Loop:
30043017
rai = (<AddrInfo>rads).data
30053018
udp._init(self, rai.ai_family)
30063019
udp._connect(rai.ai_addr, rai.ai_addrlen)
3020+
udp._set_address(rai)
30073021
else:
30083022
if family not in (uv.AF_INET, uv.AF_INET6):
30093023
raise ValueError('unexpected address family')
@@ -3047,6 +3061,7 @@ cdef class Loop:
30473061
rai = rai.ai_next
30483062
continue
30493063
udp._connect(rai.ai_addr, rai.ai_addrlen)
3064+
udp._set_address(rai)
30503065
break
30513066
else:
30523067
raise OSError(

0 commit comments

Comments
 (0)