Tutorials
A tutorial:
- is learning-oriented
- allows the newcomer to get started
- is a lesson
Analogy: teaching a small child how to cook
Your first Roll application
Make sure you installed Roll first.
The tinyest application you can make is this one:
from roll import Roll
from roll.extensions import simple_server
app = Roll()
@app.route('/hello/{parameter}')
async def hello(request, response, parameter):
response.body = f'Hello {parameter}'
if __name__ == '__main__':
simple_server(app)
Roll provides an asyncio protocol dealing with routes, requests and responses. Everything else is done via extensions. Default routing is done by autoroutes.
Note: if you are not familiar with that f''
thing, it is Python 3.6
shortcut for .format()
.
To launch that application, run it with python yourfile.py
. You should
be able to perform HTTP requests against it:
$ curl localhost:3579/hello/world
Hello world
Note: HTTPie is definitely a nicer replacement
for curl so we will use it from now on. You can pip install
it too.
$ http :3579/hello/world
HTTP/1.1 200 OK
Content-Length: 11
Hello world
That’s it! Celebrate that first step and… wait! We need to test that view before :-).
Your first Roll test
First install pytest
and pytest-asyncio
.
Then create a tests.py
file and copy-paste:
from http import HTTPStatus
import pytest
from yourfile import app as app_
pytestmark = pytest.mark.asyncio
@pytest.fixture(scope='function')
def app():
return app_
async def test_hello_view(client, app):
resp = await client.get('/hello/world')
assert resp.status == HTTPStatus.OK
assert resp.body == b'Hello world'
You will have to adapt the import of your app
given the filename
you gave during the previous part of the tutorial.
Once it’s done, you can launch py.test tests.py
.
According to pytest-asyncio documentation, if pip
has installed pytest-asyncio >= 0.17
you will have warnings in command line results. You should read the doc to configure pytest
correctly. To keep this tutorial simple and to avoid warnings, you can launch py.test tests.py --asyncio-mode=auto
.
Note: in case the client
fixture is not found, you probably did not
install Roll
correctly.
Your first Roll form
Imagine a basic login view which is waiting for a username and password:
from roll import Roll
from roll.extensions import simple_server
app = Roll()
@app.route('/login', methods=['POST'])
async def login(request, response):
username = request.form.get('username')
password = request.form.get('password')
response.body = f'Username: `{username}` password: `{password}`.'
if __name__ == '__main__':
simple_server(app)
Now if we post our username/password information using HTTPie:
$ http --form POST :3579/login username=David password=123456
HTTP/1.1 200 OK
Content-Length: 37
Username: `David` password: `123456`.
Obviously we do not want to return that kind of information but you get
the point! You also have access to optional .files
, check out the
dedicated reference section to learn more.
Websockets
Websockets can bring real-time dialog between a client, usually the browser, and your application.
In a browser, using a websocket requires javascript.
Server-side, your websocket endpoint is declared as a route. The main difference is that the route handler takes the websocket instead of the response as an argument. This websocket object can send and receive.
For our example, we'll implement an echo endpoint that will simply parrot what it gets through the websocket:
from roll import Roll
from roll.extensions import simple_server
app = Roll()
@app.route('/ws', protocol="websocket")
async def echo_websocket(request, ws, **params):
async for message in ws:
await ws.send(message)
The websocket will exit and close the communication with the client as soon as the endpoint execution is done. In this example, we use an endless loop that will asynchronously await for a message on the socket and asynchronously send it back.
Using extensions
There are a couple of extensions available to “enrich” your application.
These extensions have to be applied to your Roll app, for instance:
from roll import Roll
from roll.extensions import logger, simple_server
app = Roll()
logger(app) # <- This is the only change we made! (+ import)
@app.route('/hello/{parameter}')
async def hello(request, response, parameter):
response.body = f'Hello {parameter}'
if __name__ == '__main__':
simple_server(app)
Once you had that logger
extension, each and every request will be
logged on your server-side. Try it by yourself!
Relaunch the server $ python yourfile.py
and perform a new request with
httpie: $ http :3579/hello/world
. On your server terminal you should
have something like that:
python yourfile.py
Rolling on http://127.0.0.1:3579
GET /hello/world
Notice the GET
line, if you perform another HTTP request, a new line
will appear. Quite handy for debugging!
Another extension is very useful for debugging: traceback
. Try to add
it by yourself and raise any error within your view to see it in
application (do not forget to restart your server!).
See the reference documentation for all built-in extensions.
Using events
Last but not least, you can directly use registered events to alter the behaviour of Roll at runtime.
Note: this is how extensions are working internally.
Let’s say you want to display a custom message when you launch your server:
from roll import Roll
from roll.extensions import simple_server
app = Roll()
@app.route('/hello/{parameter}')
async def hello(request, response, parameter):
response.body = f'Hello {parameter}'
@app.listen('startup') # <- This is the part we added (3 lines)
async def on_startup():
print('Example message')
if __name__ == '__main__':
simple_server(app)
Now restart your server and you should see the message printed. Wonderful.
See the reference documentation for all available events.