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

Add aiohttp.web command-line interface to serve apps #740

Merged
merged 7 commits into from
Jan 19, 2016
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 4 additions & 1 deletion aiohttp/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from .web_urldispatcher import * # noqa
from .web_ws import * # noqa


__all__ = (web_reqrep.__all__ +
web_exceptions.__all__ +
web_urldispatcher.__all__ +
Expand Down Expand Up @@ -322,3 +321,7 @@ def run_app(app, *, host='0.0.0.0', port=None,
loop.run_until_complete(handler.finish_connections(shutdown_timeout))
loop.run_until_complete(app.cleanup())
loop.close()

if __name__ == "__main__":
from .web_main import main
main()
43 changes: 43 additions & 0 deletions aiohttp/web_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from .web import run_app
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the file content to the end of web.py.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay...I wasn't sure about the layout.
I was trying to emulate some resemblance to a package level __main__.py.
Speaking of which could web be turned into a proper sub-package, or would that break backwards compatibility?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aiohttp will keep flat structure without sub-packages.

from argparse import ArgumentParser
from importlib import import_module


def main():
arg_parser = ArgumentParser(
description="aiohttp.web TCP/IP Application server",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'aiohttp.web HTTP server' sounds better I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTTP is redundant; aiohttp.web already implies it's HTTP.
I'll make it aiohttp.web Application server in case the TCP/IP part is too clunky.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

prog="aiohttp.web"
)
arg_parser.add_argument(
"entry_func",
help=("Callable returning the `aiohttp.web.Application` instance to "
"run. Should be specified in the Python import syntax, "
"e.g. 'package.module.function')"),
metavar="entry-func"
)
arg_parser.add_argument(
"-n", "--hostname",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use "-h" instead of "-n".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, as an option -- "-H" and "-P" to don't clash with "-h/--help"

help="TCP/IP hostname to serve on (default: %(default)r)",
default="localhost"
)
arg_parser.add_argument(
"-p", "--port",
help="TCP/IP port to serve on (default: %(default)r)",
type=int,
default="8080"
)
args, extra_args = arg_parser.parse_known_args()

# Import logic
mod_str, _, func_str = args.entry_func.rpartition(".")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use ':' as module / name separator like setuptools entrypoint for example: package.module:function.

try:
module = import_module(mod_str)
func = getattr(module, func_str)
except (ImportError, ValueError) as e:
arg_parser.error(e)
except AttributeError as e:
arg_parser.error(e)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it may be merged with previous exception handler.


app = func(extra_args)
run_app(app, host=args.hostname, port=args.port)
arg_parser.exit(message="Stopped\n")
31 changes: 25 additions & 6 deletions docs/web.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,34 @@ particular *HTTP method* and *path*::

After that, run the application by :func:`run_app` call::

run_app(app)
web.run_app(app)

That's it. Now, head over to ``http://localhost:8080/`` to see the results.

.. seealso:: :ref:`aiohttp-web-graceful-shutdown` section
explains what :func:`run_app` does and how implement
complex server initialization/finalization from scratch.
.. seealso::

:ref:`aiohttp-web-graceful-shutdown` section explains what :func:`run_app`
does and how to implement complex server initialization/finalization
from scratch.


.. _aiohttp-web-cli:

Command Line Interface (CLI)
----------------------------
:mod:`aiohttp.web` implements a basic CLI for quickly serving an
:class:`Application` in *development* over TCP/IP::

$ python -m aiohttp.web -n localhost -p 8080 package.module.init_func

``package.module.init_func`` should be an importable :term:`callable` that
accepts a list of any non-parsed command-line arguments and returns an
:class:`Application` instance after setting it up::

def init_function(args):
app = web.Application()
app.router.add_route("GET", "/", index_handler)
return app


.. _aiohttp-web-handler:
Expand Down Expand Up @@ -858,8 +879,6 @@ finalizing. It's pretty close to :func:`run_app` utility function::
loop.close()




CORS support
------------

Expand Down
20 changes: 20 additions & 0 deletions examples/web_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
Example for running Application using the `aiohttp.web` CLI.

Run this app using::

$ python -m aiohttp.web web_app.init
"""

from aiohttp.web import Application, Response


def hello_world(req):
return Response(text="Hello World")


def init(args):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example should have a code for demonstrating how to parse args again via argparse.
I mean parse.parse_args(args) -- not every user know about the ability.
I want to encourage argparse usage, not ugly code like if args[0] == '--param' and args[1] == 'value'.

app = Application()
app.router.add_route('GET', '/', hello_world)

return app