-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement web.run_app utility function #734
Changes from 19 commits
636b075
0b8e831
918d820
7670eee
2e4e4bb
479e151
6f35b4d
c9b74c8
6b5ed62
51267fb
a2b39a0
df0cb02
1b7f94c
0f17dca
635599c
727393b
69d30c0
8460828
f418dca
0733de2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -287,3 +287,38 @@ def __call__(self): | |
|
||
def __repr__(self): | ||
return "<Application>" | ||
|
||
|
||
def run_app(app, *, host='0.0.0.0', port=None, loop=None, | ||
shutdown_timeout=60.0, ssl_context=None): | ||
"""Run an app locally""" | ||
if port is None: | ||
if not ssl_context: | ||
port = 8080 | ||
else: | ||
port = 8443 | ||
|
||
if loop is None: | ||
loop = asyncio.get_event_loop() | ||
|
||
handler = app.make_handler() | ||
srv = loop.run_until_complete(loop.create_server(handler, host, port, | ||
ssl=ssl_context)) | ||
|
||
scheme = 'https' if ssl_context else 'http' | ||
prompt = '127.0.0.1' if host == '0.0.0.0' else host | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How should someone bind to public interface? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry, read from end to top)) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I don't follow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I already said -- I read diff from bottom to top) thought that you replaced '0.0.0.0' with localhost and bind to it) |
||
print("======== Running on {scheme}://{prompt}:{port}/ ========\n" | ||
"(Press CTRL+C to quit)".format( | ||
scheme=scheme, prompt=prompt, port=port)) | ||
|
||
try: | ||
loop.run_forever() | ||
except KeyboardInterrupt: | ||
pass | ||
finally: | ||
srv.close() | ||
loop.run_until_complete(srv.wait_closed()) | ||
loop.run_until_complete(app.shutdown()) | ||
loop.run_until_complete(handler.finish_connections(shutdown_timeout)) | ||
loop.run_until_complete(app.finish()) | ||
loop.close() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import asyncio | ||
import pytest | ||
import ssl | ||
|
||
from unittest import mock | ||
from aiohttp import web | ||
|
||
|
||
def test_run_app_http(loop): | ||
loop = mock.Mock(spec=asyncio.AbstractEventLoop, wrap=loop) | ||
loop.call_later(0.01, loop.stop) | ||
app = mock.Mock(wrap=web.Application(loop=loop)) | ||
|
||
web.run_app(app, loop=loop) | ||
|
||
app.make_handler.assert_called_with() | ||
|
||
loop.close.assert_called_with() | ||
app.finish.assert_called_with() | ||
app.shutdown.assert_called_with() | ||
|
||
loop.create_server.assert_called_with(mock.ANY, '0.0.0.0', 8080, ssl=None) | ||
|
||
|
||
def test_run_app_https(loop): | ||
loop = mock.Mock(spec=asyncio.AbstractEventLoop, wrap=loop) | ||
loop.call_later(0.01, loop.stop) | ||
|
||
app = mock.Mock(wrap=web.Application(loop=loop)) | ||
|
||
ssl_context = ssl.create_default_context() | ||
|
||
web.run_app(app, ssl_context=ssl_context, loop=loop) | ||
|
||
app.make_handler.assert_called_with() | ||
|
||
loop.close.assert_called_with() | ||
app.finish.assert_called_with() | ||
app.shutdown.assert_called_with() | ||
|
||
loop.create_server.assert_called_with(mock.ANY, '0.0.0.0', 8443, | ||
ssl=ssl_context) | ||
|
||
|
||
def test_run_app_nondefault_host_port(loop, unused_port): | ||
port = unused_port() | ||
host = 'localhost' | ||
|
||
loop = mock.Mock(spec=asyncio.AbstractEventLoop, wrap=loop) | ||
loop.call_later(0.01, loop.stop) | ||
|
||
app = mock.Mock(wrap=web.Application(loop=loop)) | ||
|
||
web.run_app(app, host=host, port=port, loop=loop) | ||
|
||
loop.create_server.assert_called_with(mock.ANY, host, port, ssl=None) | ||
|
||
|
||
def test_run_app_default_eventloop(loop): | ||
# don't perform any assert checks just make sure the run_app was successful | ||
# mocking a default loop produces a failure on Python 3.4 | ||
# with PYTHONASYNCIODEBUG enabled | ||
|
||
asyncio.set_event_loop(loop) | ||
loop.call_later(0.01, loop.stop) | ||
|
||
web.run_app(web.Application()) | ||
|
||
|
||
def test_run_app_exit_with_exception(loop): | ||
loop = mock.Mock(spec=asyncio.AbstractEventLoop, wrap=loop) | ||
|
||
loop.run_forever.return_value = None # to disable wrapping | ||
loop.run_forever.side_effect = exc = RuntimeError() | ||
|
||
app = mock.Mock(wrap=web.Application(loop=loop)) | ||
|
||
with pytest.raises(RuntimeError) as ctx: | ||
web.run_app(app, loop=loop) | ||
|
||
assert ctx.value is exc | ||
|
||
assert not loop.close.called | ||
app.finish.assert_called_with() | ||
app.shutdown.assert_called_with() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is an explicit
loop
parameter necessary -- Wouldn'tloop = app.loop
suffice here?Is there a use case where the App would run on a different loop than the Server?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!