Skip to content

Commit 7ce8a6e

Browse files
authored
set_loop for app check, allow app_factory() (#96)
* set_loop for app check, allow app_factory() * history and uprev * fix linting and tests
1 parent bde53ef commit 7ce8a6e

File tree

7 files changed

+64
-14
lines changed

7 files changed

+64
-14
lines changed

HISTORY.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
History
44
-------
55

6+
0.5.0 (2017-XX-XX)
7+
------------------
8+
* set loop before running app check #96
9+
* allow app factory with simpler signature ``app_factory()`` #96
610

711
0.4.1 (2017-06-03)
812
------------------

aiohttp_devtools/cli.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ def serve(path, livereload, port, verbose):
5151
precheck_help = ("Whether to start and stop the app before creating it in a subprocess to check it's working. "
5252
"env variable AIO_PRECHECK")
5353
app_factory_help = ('name of the app factory to create an aiohttp.web.Application with, if missing default app-factory '
54-
'names are tried. This can be either a function with signature "def create_app(loop): -> '
55-
'Application" or just an instance of aiohttp.Application. env variable AIO_APP_FACTORY')
54+
'names are tried. This can be either a function with signature '
55+
'"def create_app(loop): -> Application" or "def create_app(): -> Application" '
56+
'or just an instance of aiohttp.Application. env variable AIO_APP_FACTORY')
5657
port_help = 'Port to serve app from, default 8000. env variable: AIO_PORT'
5758
aux_port_help = 'Port to serve auxiliary app (reload and static) on, default port + 1. env variable: AIO_AUX_PORT'
5859

@@ -73,7 +74,7 @@ def serve(path, livereload, port, verbose):
7374
@click.option('-v', '--verbose', is_flag=True, help=verbose_help)
7475
def runserver(**config):
7576
"""
76-
Run a development server for aiohttp apps.
77+
Run a development server for an aiohttp apps.
7778
7879
Takes one argument "app-path" which should be a path to either a directory containing a recognized default file
7980
("app.py" or "main.py") or to a specific file. Defaults to the environment variable "AIO_APP_PATH" or ".".

aiohttp_devtools/runserver/config.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import inspect
12
import re
23
import sys
34
from importlib import import_module
@@ -175,19 +176,30 @@ def check(self, loop):
175176
raise AdevConfigError('app_factory "{.app_factory_name}" is not callable or an '
176177
'instance of aiohttp.web.Application'.format(self))
177178

179+
loop.run_until_complete(self._startup_cleanup(loop))
180+
181+
def load_app(self, loop):
178182
if isinstance(self.app_factory, Application):
179183
app = self.app_factory
180184
else:
181185
# app_factory should be a proper factory with signature (loop): -> Application
182-
app = self.app_factory(loop)
186+
signature = inspect.signature(self.app_factory)
187+
if 'loop' in signature.parameters:
188+
app = self.app_factory(loop=loop)
189+
else:
190+
# loop argument missing, assume no arguments
191+
app = self.app_factory()
192+
183193
if not isinstance(app, Application):
184194
raise AdevConfigError('app factory "{.app_factory_name}" returned "{.__class__.__name__}" not an '
185195
'aiohttp.web.Application'.format(self, app))
186196

187-
logger.debug('app "%s" successfully created', app)
188-
loop.run_until_complete(self._startup_cleanup(app))
197+
return app
189198

190-
async def _startup_cleanup(self, app):
199+
async def _startup_cleanup(self, loop):
200+
app = self.load_app(loop)
201+
app._set_loop(loop)
202+
logger.debug('app "%s" successfully created', app)
191203
logger.debug('running app startup...')
192204
await app.startup()
193205
logger.debug('running app cleanup...')

aiohttp_devtools/runserver/serve.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import aiohttp_debugtoolbar
77
from aiohttp import WSMsgType, web
88
from aiohttp.hdrs import LAST_MODIFIED
9-
from aiohttp.web import Application, FileResponse, Response
9+
from aiohttp.web import FileResponse, Response
1010
from aiohttp.web_exceptions import HTTPNotFound, HTTPNotModified
1111
from aiohttp.web_urldispatcher import StaticResource
1212
from yarl import unquote
@@ -95,10 +95,7 @@ def serve_main_app(config: Config, loop: asyncio.AbstractEventLoop=None):
9595

9696
loop = loop or asyncio.get_event_loop()
9797

98-
if isinstance(config.app_factory, Application):
99-
app = config.app_factory
100-
else:
101-
app = config.app_factory(loop=loop)
98+
app = config.load_app(loop)
10299

103100
modify_main_app(app, config)
104101

aiohttp_devtools/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
__all__ = ['VERSION']
44

5-
VERSION = StrictVersion('0.4.1')
5+
VERSION = StrictVersion('0.5.0')

tests/test_cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def test_cli_help():
1010
runner = CliRunner()
1111
result = runner.invoke(cli, ['--help'])
1212
assert result.exit_code == 0
13-
assert 'Run a development server for aiohttp apps.' in result.output
13+
assert 'Run a development server for an aiohttp apps.' in result.output
1414
assert 'Serve static files from a directory.' in result.output
1515
assert 'Create a new aiohttp app.' in result.output
1616

tests/test_runserver_main.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,42 @@ async def check_callback(session):
124124
code_event_handler._process.terminate()
125125

126126

127+
@if_boxed
128+
@slow
129+
def test_start_runserver_no_loop_argument(tmpworkdir, loop):
130+
mktree(tmpworkdir, {
131+
'app.py': """\
132+
from aiohttp import web
133+
134+
async def hello(request):
135+
return web.Response(text='<h1>hello world</h1>', content_type='text/html')
136+
137+
def app():
138+
a = web.Application()
139+
a.router.add_get('/', hello)
140+
return a
141+
"""
142+
})
143+
asyncio.set_event_loop(loop)
144+
aux_app, observer, aux_port, _ = runserver(app_path='app.py')
145+
assert len(observer._handlers) == 1
146+
event_handlers = list(observer._handlers.values())[0]
147+
code_event_handler = next(eh for eh in event_handlers if isinstance(eh, PyCodeEventHandler))
148+
149+
async def check_callback(session):
150+
async with session.get('http://localhost:8000/') as r:
151+
assert r.status == 200
152+
assert r.headers['content-type'].startswith('text/html')
153+
text = await r.text()
154+
assert '<h1>hello world</h1>' in text
155+
assert '<script src="http://localhost:8001/livereload.js"></script>' in text
156+
157+
try:
158+
loop.run_until_complete(check_server_running(loop, check_callback))
159+
finally:
160+
code_event_handler._process.terminate()
161+
162+
127163
def kill_parent_soon(pid):
128164
time.sleep(0.2)
129165
os.kill(pid, signal.SIGINT)

0 commit comments

Comments
 (0)