Skip to content
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

set_loop for app check, allow app_factory() #96

Merged
merged 3 commits into from
Jul 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
History
-------

0.5.0 (2017-XX-XX)
------------------
* set loop before running app check #96
* allow app factory with simpler signature ``app_factory()`` #96

0.4.1 (2017-06-03)
------------------
Expand Down
7 changes: 4 additions & 3 deletions aiohttp_devtools/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ def serve(path, livereload, port, verbose):
precheck_help = ("Whether to start and stop the app before creating it in a subprocess to check it's working. "
"env variable AIO_PRECHECK")
app_factory_help = ('name of the app factory to create an aiohttp.web.Application with, if missing default app-factory '
'names are tried. This can be either a function with signature "def create_app(loop): -> '
'Application" or just an instance of aiohttp.Application. env variable AIO_APP_FACTORY')
'names are tried. This can be either a function with signature '
'"def create_app(loop): -> Application" or "def create_app(): -> Application" '
'or just an instance of aiohttp.Application. env variable AIO_APP_FACTORY')
port_help = 'Port to serve app from, default 8000. env variable: AIO_PORT'
aux_port_help = 'Port to serve auxiliary app (reload and static) on, default port + 1. env variable: AIO_AUX_PORT'

Expand All @@ -73,7 +74,7 @@ def serve(path, livereload, port, verbose):
@click.option('-v', '--verbose', is_flag=True, help=verbose_help)
def runserver(**config):
"""
Run a development server for aiohttp apps.
Run a development server for an aiohttp apps.

Takes one argument "app-path" which should be a path to either a directory containing a recognized default file
("app.py" or "main.py") or to a specific file. Defaults to the environment variable "AIO_APP_PATH" or ".".
Expand Down
20 changes: 16 additions & 4 deletions aiohttp_devtools/runserver/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import inspect
import re
import sys
from importlib import import_module
Expand Down Expand Up @@ -175,19 +176,30 @@ def check(self, loop):
raise AdevConfigError('app_factory "{.app_factory_name}" is not callable or an '
'instance of aiohttp.web.Application'.format(self))

loop.run_until_complete(self._startup_cleanup(loop))

def load_app(self, loop):
if isinstance(self.app_factory, Application):
app = self.app_factory
else:
# app_factory should be a proper factory with signature (loop): -> Application
app = self.app_factory(loop)
signature = inspect.signature(self.app_factory)
if 'loop' in signature.parameters:
app = self.app_factory(loop=loop)
else:
# loop argument missing, assume no arguments
app = self.app_factory()

if not isinstance(app, Application):
raise AdevConfigError('app factory "{.app_factory_name}" returned "{.__class__.__name__}" not an '
'aiohttp.web.Application'.format(self, app))

logger.debug('app "%s" successfully created', app)
loop.run_until_complete(self._startup_cleanup(app))
return app

async def _startup_cleanup(self, app):
async def _startup_cleanup(self, loop):
app = self.load_app(loop)
app._set_loop(loop)
logger.debug('app "%s" successfully created', app)
logger.debug('running app startup...')
await app.startup()
logger.debug('running app cleanup...')
Expand Down
7 changes: 2 additions & 5 deletions aiohttp_devtools/runserver/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import aiohttp_debugtoolbar
from aiohttp import WSMsgType, web
from aiohttp.hdrs import LAST_MODIFIED
from aiohttp.web import Application, FileResponse, Response
from aiohttp.web import FileResponse, Response
from aiohttp.web_exceptions import HTTPNotFound, HTTPNotModified
from aiohttp.web_urldispatcher import StaticResource
from yarl import unquote
Expand Down Expand Up @@ -95,10 +95,7 @@ def serve_main_app(config: Config, loop: asyncio.AbstractEventLoop=None):

loop = loop or asyncio.get_event_loop()

if isinstance(config.app_factory, Application):
app = config.app_factory
else:
app = config.app_factory(loop=loop)
app = config.load_app(loop)

modify_main_app(app, config)

Expand Down
2 changes: 1 addition & 1 deletion aiohttp_devtools/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

__all__ = ['VERSION']

VERSION = StrictVersion('0.4.1')
VERSION = StrictVersion('0.5.0')
2 changes: 1 addition & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_cli_help():
runner = CliRunner()
result = runner.invoke(cli, ['--help'])
assert result.exit_code == 0
assert 'Run a development server for aiohttp apps.' in result.output
assert 'Run a development server for an aiohttp apps.' in result.output
assert 'Serve static files from a directory.' in result.output
assert 'Create a new aiohttp app.' in result.output

Expand Down
36 changes: 36 additions & 0 deletions tests/test_runserver_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,42 @@ async def check_callback(session):
code_event_handler._process.terminate()


@if_boxed
@slow
def test_start_runserver_no_loop_argument(tmpworkdir, loop):
mktree(tmpworkdir, {
'app.py': """\
from aiohttp import web

async def hello(request):
return web.Response(text='<h1>hello world</h1>', content_type='text/html')

def app():
a = web.Application()
a.router.add_get('/', hello)
return a
"""
})
asyncio.set_event_loop(loop)
aux_app, observer, aux_port, _ = runserver(app_path='app.py')
assert len(observer._handlers) == 1
event_handlers = list(observer._handlers.values())[0]
code_event_handler = next(eh for eh in event_handlers if isinstance(eh, PyCodeEventHandler))

async def check_callback(session):
async with session.get('http://localhost:8000/') as r:
assert r.status == 200
assert r.headers['content-type'].startswith('text/html')
text = await r.text()
assert '<h1>hello world</h1>' in text
assert '<script src="http://localhost:8001/livereload.js"></script>' in text

try:
loop.run_until_complete(check_server_running(loop, check_callback))
finally:
code_event_handler._process.terminate()


def kill_parent_soon(pid):
time.sleep(0.2)
os.kill(pid, signal.SIGINT)
Expand Down