Commit e1f4ef92 authored by Ondřej Nový's avatar Ondřej Nový

New upstream version 0.11.2+ds1

parent 8db2b06b
Metadata-Version: 1.1
Name: uvloop
Version: 0.10.1
Version: 0.11.2
Summary: Fast implementation of asyncio event loop on top of libuv
Home-page: http://github.com/MagicStack/uvloop
Author: Yury Selivanov
......@@ -9,9 +9,6 @@ License: MIT
Description: .. image:: https://travis-ci.org/MagicStack/uvloop.svg?branch=master
:target: https://travis-ci.org/MagicStack/uvloop
.. image:: https://ci.appveyor.com/api/projects/status/4apd79e5jqrwwe40/branch/master?svg=true
:target: https://ci.appveyor.com/project/MagicStack/uvloop
.. image:: https://img.shields.io/pypi/v/uvloop.svg
:target: https://pypi.python.org/pypi/uvloop
......
.. image:: https://travis-ci.org/MagicStack/uvloop.svg?branch=master
:target: https://travis-ci.org/MagicStack/uvloop
.. image:: https://ci.appveyor.com/api/projects/status/4apd79e5jqrwwe40/branch/master?svg=true
:target: https://ci.appveyor.com/project/MagicStack/uvloop
.. image:: https://img.shields.io/pypi/v/uvloop.svg
:target: https://pypi.python.org/pypi/uvloop
......
......@@ -31,7 +31,7 @@ def _libuv_build_env():
env = os.environ.copy()
cur_cflags = env.get('CFLAGS', '')
if not re.search('-O\d', cur_cflags):
if not re.search(r'-O\d', cur_cflags):
cur_cflags += ' -O2'
env['CFLAGS'] = (cur_cflags + ' -fPIC ' + env.get('ARCHFLAGS', ''))
......@@ -40,9 +40,17 @@ def _libuv_build_env():
def _libuv_autogen(env):
if not os.path.exists(os.path.join(LIBUV_DIR, 'configure')):
subprocess.run(
['/bin/sh', 'autogen.sh'], cwd=LIBUV_DIR, env=env, check=True)
if os.path.exists(os.path.join(LIBUV_DIR, 'configure')):
# No need to use autogen, the configure script is there.
return
if not os.path.exists(os.path.join(LIBUV_DIR, 'autogen.sh')):
raise RuntimeError(
'the libuv submodule has not been checked out; '
'try running "git submodule init; git submodule update"')
subprocess.run(
['/bin/sh', 'autogen.sh'], cwd=LIBUV_DIR, env=env, check=True)
class uvloop_sdist(sdist):
......
......@@ -8,6 +8,7 @@ else:
import asyncio
import unittest
import weakref
from uvloop import _testbase as tb
......@@ -25,12 +26,11 @@ class _TestAioHTTP:
app = aiohttp.web.Application()
app.router.add_get('/', on_request)
f = self.loop.create_server(
app.make_handler(),
'0.0.0.0', '0')
srv = self.loop.run_until_complete(f)
port = srv.sockets[0].getsockname()[1]
runner = aiohttp.web.AppRunner(app)
self.loop.run_until_complete(runner.setup())
site = aiohttp.web.TCPSite(runner, '0.0.0.0', '0')
self.loop.run_until_complete(site.start())
port = site._server.sockets[0].getsockname()[1]
async def test():
# Make sure we're using the correct event loop.
......@@ -45,11 +45,61 @@ class _TestAioHTTP:
self.assertEqual(result, PAYLOAD)
self.loop.run_until_complete(test())
self.loop.run_until_complete(app.shutdown())
self.loop.run_until_complete(app.cleanup())
self.loop.run_until_complete(runner.cleanup())
def test_aiohttp_graceful_shutdown(self):
async def websocket_handler(request):
ws = aiohttp.web.WebSocketResponse()
await ws.prepare(request)
request.app['websockets'].add(ws)
try:
async for msg in ws:
await ws.send_str(msg.data)
finally:
request.app['websockets'].discard(ws)
return ws
async def on_shutdown(app):
for ws in set(app['websockets']):
await ws.close(
code=aiohttp.WSCloseCode.GOING_AWAY,
message='Server shutdown')
asyncio.set_event_loop(self.loop)
app = aiohttp.web.Application()
app.router.add_get('/', websocket_handler)
app.on_shutdown.append(on_shutdown)
app['websockets'] = weakref.WeakSet()
runner = aiohttp.web.AppRunner(app)
self.loop.run_until_complete(runner.setup())
site = aiohttp.web.TCPSite(runner, '0.0.0.0', '0')
self.loop.run_until_complete(site.start())
port = site._server.sockets[0].getsockname()[1]
async def client():
async with aiohttp.ClientSession() as client:
async with client.ws_connect(
'http://127.0.0.1:{}'.format(port)) as ws:
await ws.send_str("hello")
async for msg in ws:
assert msg.data == "hello"
client_task = asyncio.ensure_future(client())
async def stop():
await asyncio.sleep(0.1)
try:
await asyncio.wait_for(runner.cleanup(), timeout=0.1)
finally:
try:
client_task.cancel()
await client_task
except asyncio.CancelledError:
pass
self.loop.run_until_complete(stop())
srv.close()
self.loop.run_until_complete(srv.wait_closed())
@unittest.skipIf(skip_tests, "no aiohttp module")
......
......@@ -3,6 +3,7 @@ import decimal
import random
import sys
import unittest
import weakref
from uvloop import _testbase as tb
......@@ -117,6 +118,30 @@ class _ContextBaseTests:
self.assertEqual(cvar.get(), -1)
@unittest.skipUnless(PY37, 'requires Python 3.7')
def test_task_context_4(self):
import contextvars
cvar = contextvars.ContextVar('cvar', default='nope')
class TrackMe:
pass
tracked = TrackMe()
ref = weakref.ref(tracked)
async def sub():
cvar.set(tracked) # NoQA
self.loop.call_soon(lambda: None)
async def main():
await self.loop.create_task(sub())
await asyncio.sleep(0.01, loop=self.loop)
task = self.loop.create_task(main())
self.loop.run_until_complete(task)
del tracked
self.assertIsNone(ref())
class Test_UV_Context(_ContextBaseTests, tb.UVTestCase):
......
......@@ -48,7 +48,7 @@ if __name__ == '__main__':
cmd = sys.executable
proc = await asyncio.create_subprocess_exec(
cmd, b'-c', prog,
cmd, b'-W', b'ignore', b'-c', prog,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
loop=self.loop)
......
......@@ -10,10 +10,15 @@ import tempfile
import time
import unittest
import psutil
from uvloop import _testbase as tb
class _TestProcess:
def get_num_fds(self):
return psutil.Process(os.getpid()).num_fds()
def test_process_env_1(self):
async def test():
cmd = 'echo $FOO$BAR'
......@@ -79,7 +84,7 @@ class _TestProcess:
async def test():
cmd = sys.executable
proc = await asyncio.create_subprocess_exec(
cmd, '-c',
cmd, b'-W', b'ignore', '-c',
'import os,sys;sys.stdout.write(os.getenv("FRUIT"))',
stdout=subprocess.PIPE,
preexec_fn=lambda: os.putenv("FRUIT", "apple"),
......@@ -100,7 +105,7 @@ class _TestProcess:
async def test():
cmd = sys.executable
proc = await asyncio.create_subprocess_exec(
cmd, '-c', 'import time; time.sleep(10)',
cmd, b'-W', b'ignore', '-c', 'import time; time.sleep(10)',
preexec_fn=raise_it,
loop=self.loop)
......@@ -126,7 +131,7 @@ class _TestProcess:
def test_process_executable_1(self):
async def test():
proc = await asyncio.create_subprocess_exec(
b'doesnotexist', b'-c', b'print("spam")',
b'doesnotexist', b'-W', b'ignore', b'-c', b'print("spam")',
executable=sys.executable,
stdout=subprocess.PIPE,
loop=self.loop)
......@@ -145,7 +150,7 @@ print(os.getpid())
cmd = sys.executable
proc = await asyncio.create_subprocess_exec(
cmd, b'-c', prog,
cmd, b'-W', b'ignore', b'-c', prog,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
loop=self.loop)
......@@ -177,7 +182,7 @@ exit(11)
cmd = sys.executable
proc = await asyncio.create_subprocess_exec(
cmd, b'-c', prog,
cmd, b'-W', b'ignore', b'-c', prog,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
......@@ -216,7 +221,7 @@ while True:
cmd = sys.executable
proc = await asyncio.create_subprocess_exec(
cmd, b'-c', prog,
cmd, b'-W', b'ignore', b'-c', prog,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
......@@ -260,7 +265,7 @@ print('err', file=sys.stderr, flush=True)
'''
proc = await asyncio.create_subprocess_exec(
sys.executable, '-c', prog,
sys.executable, b'-W', b'ignore', b'-c', prog,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
loop=self.loop)
......@@ -280,7 +285,7 @@ print('err', file=sys.stderr, flush=True)
'''
proc = await asyncio.create_subprocess_exec(
sys.executable, '-c', prog,
sys.executable, b'-W', b'ignore', b'-c', prog,
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
......@@ -316,7 +321,7 @@ print("OK")
tempfile.TemporaryFile() as non_inherited:
proc = await asyncio.create_subprocess_exec(
sys.executable, '-c', prog, '--',
sys.executable, b'-W', b'ignore', b'-c', prog, '--',
str(inherited.fileno()),
str(non_inherited.fileno()),
stdout=subprocess.PIPE,
......@@ -330,21 +335,62 @@ print("OK")
self.loop.run_until_complete(test())
def test_subprocess_fd_leak_1(self):
async def main(n):
for i in range(n):
try:
await asyncio.create_subprocess_exec(
'nonexistant',
loop=self.loop,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except FileNotFoundError:
pass
await asyncio.sleep(0, loop=self.loop)
self.loop.run_until_complete(main(10))
num_fd_1 = self.get_num_fds()
self.loop.run_until_complete(main(10))
num_fd_2 = self.get_num_fds()
self.assertEqual(num_fd_1, num_fd_2)
def test_subprocess_fd_leak_2(self):
async def main(n):
for i in range(n):
try:
p = await asyncio.create_subprocess_exec(
'ls',
loop=self.loop,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
finally:
await p.wait()
await asyncio.sleep(0, loop=self.loop)
self.loop.run_until_complete(main(10))
num_fd_1 = self.get_num_fds()
self.loop.run_until_complete(main(10))
num_fd_2 = self.get_num_fds()
self.assertEqual(num_fd_1, num_fd_2)
class _AsyncioTests:
# Program blocking
PROGRAM_BLOCKED = [sys.executable, '-c', 'import time; time.sleep(3600)']
PROGRAM_BLOCKED = [sys.executable, b'-W', b'ignore',
b'-c', b'import time; time.sleep(3600)']
# Program copying input to output
PROGRAM_CAT = [
sys.executable, '-c',
';'.join(('import sys',
'data = sys.stdin.buffer.read()',
'sys.stdout.buffer.write(data)'))]
sys.executable, b'-c',
b';'.join((b'import sys',
b'data = sys.stdin.buffer.read()',
b'sys.stdout.buffer.write(data)'))]
PROGRAM_ERROR = [
sys.executable, '-c', '1/0'
sys.executable, b'-W', b'ignore', b'-c', b'1/0'
]
def test_stdin_not_inheritable(self):
......@@ -354,7 +400,7 @@ class _AsyncioTests:
def len_message(message):
code = 'import sys; data = sys.stdin.read(); print(len(data))'
proc = yield from asyncio.create_subprocess_exec(
sys.executable, '-c', code,
sys.executable, b'-W', b'ignore', b'-c', code,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
......@@ -499,7 +545,7 @@ class _AsyncioTests:
def test_send_signal(self):
code = 'import time; print("sleeping", flush=True); time.sleep(3600)'
args = [sys.executable, '-c', code]
args = [sys.executable, b'-W', b'ignore', b'-c', code]
create = asyncio.create_subprocess_exec(*args,
stdout=subprocess.PIPE,
loop=self.loop)
......@@ -629,7 +675,7 @@ print('err', file=sys.stderr, flush=True)
'''
proc = await asyncio.create_subprocess_exec(
sys.executable, '-c', prog,
sys.executable, b'-W', b'ignore', b'-c', prog,
loop=self.loop)
out, err = await proc.communicate()
......
......@@ -40,7 +40,7 @@ run()
"""
proc = await asyncio.create_subprocess_exec(
sys.executable, b'-c', PROG,
sys.executable, b'-W', b'ignore', b'-c', PROG,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
loop=self.loop)
......@@ -86,7 +86,7 @@ run()
"""
proc = await asyncio.create_subprocess_exec(
sys.executable, b'-c', PROG,
sys.executable, b'-W', b'ignore', b'-c', PROG,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
loop=self.loop)
......@@ -127,7 +127,7 @@ finally:
"""
proc = await asyncio.create_subprocess_exec(
sys.executable, b'-c', PROG,
sys.executable, b'-W', b'ignore', b'-c', PROG,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
loop=self.loop)
......@@ -177,7 +177,7 @@ finally:
"""
proc = await asyncio.create_subprocess_exec(
sys.executable, b'-c', PROG,
sys.executable, b'-W', b'ignore', b'-c', PROG,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
loop=self.loop)
......@@ -236,7 +236,7 @@ finally:
"""
proc = await asyncio.create_subprocess_exec(
sys.executable, b'-c', PROG,
sys.executable, b'-W', b'ignore', b'-c', PROG,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
loop=self.loop)
......
......@@ -189,6 +189,91 @@ class _TestSockets:
self.assertEqual(sock.fileno(), -1)
self.loop.run_until_complete(asyncio.sleep(0.01, loop=self.loop))
def test_sock_cancel_add_reader_race(self):
srv_sock_conn = None
async def server():
nonlocal srv_sock_conn
sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_server.setblocking(False)
with sock_server:
sock_server.bind(('127.0.0.1', 0))
sock_server.listen()
fut = asyncio.ensure_future(
client(sock_server.getsockname()), loop=self.loop)
srv_sock_conn, _ = await self.loop.sock_accept(sock_server)
srv_sock_conn.setsockopt(
socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
with srv_sock_conn:
await fut
async def client(addr):
sock_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_client.setblocking(False)
with sock_client:
await self.loop.sock_connect(sock_client, addr)
_, pending_read_futs = await asyncio.wait(
[self.loop.sock_recv(sock_client, 1)],
timeout=1, loop=self.loop)
async def send_server_data():
# Wait a little bit to let reader future cancel and
# schedule the removal of the reader callback. Right after
# "rfut.cancel()" we will call "loop.sock_recv()", which
# will add a reader. This will make a race between
# remove- and add-reader.
await asyncio.sleep(0.1, loop=self.loop)
await self.loop.sock_sendall(srv_sock_conn, b'1')
self.loop.create_task(send_server_data())
for rfut in pending_read_futs:
rfut.cancel()
data = await self.loop.sock_recv(sock_client, 1)
self.assertEqual(data, b'1')
self.loop.run_until_complete(server())
def test_sock_send_before_cancel(self):
srv_sock_conn = None
async def server():
nonlocal srv_sock_conn
sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_server.setblocking(False)
with sock_server:
sock_server.bind(('127.0.0.1', 0))
sock_server.listen()
fut = asyncio.ensure_future(
client(sock_server.getsockname()), loop=self.loop)
srv_sock_conn, _ = await self.loop.sock_accept(sock_server)
with srv_sock_conn:
await fut
async def client(addr):
await asyncio.sleep(0.01, loop=self.loop)
sock_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_client.setblocking(False)
with sock_client:
await self.loop.sock_connect(sock_client, addr)
_, pending_read_futs = await asyncio.wait(
[self.loop.sock_recv(sock_client, 1)],
timeout=1, loop=self.loop)
# server can send the data in a random time, even before
# the previous result future has cancelled.
await self.loop.sock_sendall(srv_sock_conn, b'1')
for rfut in pending_read_futs:
rfut.cancel()
data = await self.loop.sock_recv(sock_client, 1)
self.assertEqual(data, b'1')
self.loop.run_until_complete(server())
class TestUVSockets(_TestSockets, tb.UVTestCase):
......
This diff is collapsed.
......@@ -471,6 +471,57 @@ class Test_UV_Unix(_TestUnix, tb.UVTestCase):
finally:
os.unlink(fn)
@unittest.skipUnless(sys.platform.startswith('linux'), 'requires epoll')
def test_epollhup(self):
SIZE = 50
eof = False
done = False
recvd = b''
class Proto(asyncio.BaseProtocol):
def connection_made(self, tr):
tr.write(b'hello')
self.data = bytearray(SIZE)
self.buf = memoryview(self.data)
def get_buffer(self, sizehint):
return self.buf
def buffer_updated(self, nbytes):
nonlocal recvd
recvd += self.buf[:nbytes]
def eof_received(self):
nonlocal eof
eof = True
def connection_lost(self, exc):
nonlocal done
done = exc
async def test():
with tempfile.TemporaryDirectory() as td:
sock_name = os.path.join(td, 'sock')
srv = await self.loop.create_unix_server(Proto, sock_name)
s = socket.socket(socket.AF_UNIX)
with s:
s.setblocking(False)
await self.loop.sock_connect(s, sock_name)
d = await self.loop.sock_recv(s, 100)
self.assertEqual(d, b'hello')
# IMPORTANT: overflow recv buffer and close immediately
await self.loop.sock_sendall(s, b'a' * (SIZE + 1))
srv.close()
await srv.wait_closed()
self.loop.run_until_complete(test())
self.assertTrue(eof)
self.assertIsNone(done)
self.assertEqual(recvd, b'a' * (SIZE + 1))
class Test_AIO_Unix(_TestUnix, tb.AIOTestCase):
pass
......
Metadata-Version: 1.1
Name: uvloop
Version: 0.10.1
Version: 0.11.2
Summary: Fast implementation of asyncio event loop on top of libuv
Home-page: http://github.com/MagicStack/uvloop
Author: Yury Selivanov
......@@ -9,9 +9,6 @@ License: MIT
Description: .. image:: https://travis-ci.org/MagicStack/uvloop.svg?branch=master
:target: https://travis-ci.org/MagicStack/uvloop
.. image:: https://ci.appveyor.com/api/projects/status/4apd79e5jqrwwe40/branch/master?svg=true
:target: https://ci.appveyor.com/project/MagicStack/uvloop
.. image:: https://img.shields.io/pypi/v/uvloop.svg
:target: https://pypi.python.org/pypi/uvloop
......
......@@ -7,7 +7,7 @@ from . import _patch # NOQA
from .loop import Loop as __BaseLoop # NOQA
__version__ = '0.10.1'
__version__ = '0.11.2'
__all__ = ('new_event_loop', 'EventLoopPolicy')
......
......@@ -15,9 +15,11 @@ cdef class Handle:
self._source_traceback = extract_stack()
cdef inline _set_context(self, object context):
cdef PyContext* current_context
if PY37:
if context is None:
context = <object>PyContext_CopyCurrent()
context = copy_current_context()
self.context = context
else:
if context is not None:
......@@ -179,7 +181,7 @@ cdef class TimerHandle:
if PY37:
if context is None:
context = <object>PyContext_CopyCurrent()
context = copy_current_context()
self.context = context
else:
if context is not None:
......@@ -400,3 +402,15 @@ cdef extract_stack():
stack.reverse()
return stack
cdef copy_current_context():
cdef PyContext* current_context
if PY37:
current_context = PyContext_CopyCurrent()
py_context = <object>current_context
Py_XDECREF(<PyObject*>current_context)
return py_context
raise NotImplementedError('"contextvars" support requires Python 3.7+')
......@@ -38,7 +38,9 @@ cdef class UVBaseTransport(UVSocketHandle):
cdef inline _set_server(self, Server server)
cdef inline _set_waiter(self, object waiter)
cdef inline _set_protocol(self, object protocol)
cdef _set_protocol(self, object protocol)
cdef _clear_protocol(self)
cdef inline _init_protocol(self)
cdef inline _add_extra_info(self, str name, object obj)
......
@cython.no_gc_clear
cdef class UVBaseTransport(UVSocketHandle):
def __cinit__(self):
......@@ -126,7 +125,6 @@ cdef class UVBaseTransport(UVSocketHandle):
self._waiter = None
cdef _call_connection_made(self):
cdef Py_ssize_t _loop_ready_len
if self._protocol is None:
raise RuntimeError(
'protocol is not set, cannot call connection_made()')
......@@ -142,8 +140,6 @@ cdef class UVBaseTransport(UVSocketHandle):
self._wakeup_waiter()
return
_loop_ready_len = self._loop._ready_len
# Set _protocol_connected to 1 before calling "connection_made":
# if transport is aborted or closed, "connection_lost" will
# still be scheduled.
......@@ -161,24 +157,7 @@ cdef class UVBaseTransport(UVSocketHandle):
self._wakeup_waiter()
return
if _loop_ready_len == self._loop._ready_len:
# No new calls were scheduled by 'protocol.connection_made',
# so it's safe to start reading right now.
self._start_reading()
else:
# In asyncio we'd just call start_reading() right after we
# call protocol.connection_made(). However, that breaks
# SSLProtocol in uvloop, which does some initialization
# with loop.call_soon in its connection_made. It appears,
# that uvloop can call protocol.data_received() *before* it
# calls the handlers that connection_made set up.
# That's why we're using another call_soon here.
self._loop._call_soon_handle(
new_MethodHandle(self._loop,
"UVTransport._start_reading",
<method_t>self._start_reading,
self))
self._start_reading()
self._wakeup_waiter()
cdef _call_connection_lost(self, exc):
......@@ -201,8 +180,7 @@ cdef class UVBaseTransport(UVSocketHandle):
if self._protocol_connected:
self._protocol.connection_lost(exc)
finally:
self._protocol = None
self._protocol_data_received = None
self._clear_protocol()
self._close()
......@@ -223,7 +201,7 @@ cdef class UVBaseTransport(UVSocketHandle):
self._waiter = waiter
cdef inline _set_protocol(self, object protocol):
cdef _set_protocol(self, object protocol):
self._protocol = protocol
# Store a reference to the bound method directly
try:
......@@ -231,6 +209,10 @@ cdef class UVBaseTransport(UVSocketHandle):
except AttributeError:
pass
cdef _clear_protocol(self):
self._protocol = None