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

Simplify the hello world example, explain request lifecycle #4329

Merged
merged 5 commits into from
Nov 12, 2019
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
1 change: 1 addition & 0 deletions CHANGES/4272.doc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Simplify README hello word example and add a documentation page for people comming from requests.
23 changes: 13 additions & 10 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@ I hope everybody knows how to work with git and github nowadays :)

Workflow is pretty straightforward:

1. Clone the GitHub_ repo using ``--recurse-submodules`` argument
1. Clone the GitHub_ repo using the ``--recurse-submodules`` argument

2. Make a change
2. Setup your machine with the required dev environment

3. Make sure all tests passed
3. Make a change

4. Add a file into ``CHANGES`` folder.
4. Make sure all tests passed

5. Commit changes to own aiohttp clone
5. Add a file into the ``CHANGES`` folder, named after the ticket or PR number

6. Make pull request from github page for your clone against master branch
6. Commit changes to your own aiohttp clone

7. Optionally make backport Pull Request(s) for landing a bug fix
into released aiohttp versions.
7. Make a pull request from the github page of your clone against the master branch

Please open https://docs.aiohttp.org/en/stable/contributing.html
documentation page for getting detailed information about all steps.
8. Optionally make backport Pull Request(s) for landing a bug fix into released aiohttp versions.

.. important::

Please open the "`contributing <https://docs.aiohttp.org/en/stable/contributing.html>`_"
documentation page to get detailed informations about all steps.

.. _GitHub: https://github.com/aio-libs/aiohttp
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ Justas Trimailovas
Justin Foo
Justin Turner Arthur
Kay Zheng
Kevin Samuel
Kimmo Parviainen-Jalanko
Kirill Klenov
Kirill Malovitsa
Expand Down
29 changes: 19 additions & 10 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ Async http client/server framework
:alt: Chat on Gitter



Key Features
============

Expand All @@ -58,19 +57,29 @@ To get something from the web:
import aiohttp
import asyncio

async def fetch(session, url):
async with session.get(url) as response:
return await response.text()

async def main():

async with aiohttp.ClientSession() as session:
html = await fetch(session, 'http://python.org')
print(html)
async with session.get('http://python.org') as response:

print("Status:", response.status)
print("Content-type:", response.headers['content-type'])

html = await response.text()
print("Body:", html[:15], "...")

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

This prints:

.. code-block::

if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Status: 200
Content-type: text/html; charset=utf-8
Body: <!doctype html> ...

Comming from `requests <https://requests.readthedocs.io/>`_ ? Read `why we need so many lines <http://aiohttp.readthedocs.io/en/latest/aiohttp-request-lifecycle>`_.

Server
------
Expand Down
1 change: 1 addition & 0 deletions docs/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ The page contains all information about aiohttp Client API:
Advanced Usage <client_advanced>
Reference <client_reference>
Tracing Reference <tracing_reference>
The aiohttp Request Lifecycle <http_request_lifecycle>
32 changes: 17 additions & 15 deletions docs/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,27 @@ Contributing
Instructions for contributors
-----------------------------


In order to make a clone of the GitHub_ repo: open the link and press the
"Fork" button on the upper-right menu of the web page.
In order to make a clone of the GitHub_ repo: open the link and press the "Fork" button on the upper-right menu of the web page.

I hope everybody knows how to work with git and github nowadays :)

Workflow is pretty straightforward:

1. Clone the GitHub_ repo using ``--recurse-submodules`` argument
1. Clone the GitHub_ repo using the ``--recurse-submodules`` argument

2. Setup your machine with the required dev environment

2. Make a change
3. Make a change

3. Make sure all tests passed
4. Make sure all tests passed

4. Add a file into ``CHANGES`` folder (`Changelog update`_).
5. Add a file into ``CHANGES`` folder (see `Changelog update`_ for how).

5. Commit changes to own aiohttp clone
6. Commit changes to your own aiohttp clone

6. Make pull request from github page for your clone against master branch
7. Make a pull request from the github page of your clone against the master branch

7. Optionally make backport Pull Request(s) for landing a bug fix
into released aiohttp versions.
8. Optionally make backport Pull Request(s) for landing a bug fix into released aiohttp versions.

.. note::

Expand Down Expand Up @@ -68,8 +67,7 @@ For *virtualenvwrapper*:
$ cd aiohttp
$ mkvirtualenv --python=`which python3` aiohttp

There are other tools like *pyvenv* but you know the rule of thumb
now: create a python3 virtual environment and activate it.
There are other tools like *pyvenv* but you know the rule of thumb now: create a python3 virtual environment and activate it.

After that please install libraries required for development:

Expand All @@ -79,13 +77,17 @@ After that please install libraries required for development:

.. note::

If you plan to use ``pdb`` or ``ipdb`` within the test suite, execute:
For now, the development tooling depends on ``make`` and assumes an Unix OS If you wish to contribute to aiohttp from a Windows machine, the easiest way is probably to `configure the WSL <https://docs.microsoft.com/en-us/windows/wsl/install-win10>`_ so you can use the same instructions. If it's not possible for you or if it doesn't work, please contact us so we can find a solution together.

.. warning::

If you plan to use temporary ``print()``, ``pdb`` or ``ipdb`` within the test suite, execute it with ``-s``:

.. code-block:: shell

$ py.test tests -s

command to run the tests with disabled output capturing.
in order to run the tests without output capturing.

Congratulations, you are ready to run the test suite!

Expand Down
7 changes: 7 additions & 0 deletions docs/glossary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@
A mechanism for encoding information in a Uniform Resource
Locator (URL) if URL parts don't fit in safe characters space.

requests

Currently the most popular synchronous library to make
HTTP requests in Python.

https://requests.readthedocs.io

requoting

Applying :term:`percent-encoding` to non-safe symbols and decode
Expand Down
110 changes: 110 additions & 0 deletions docs/http_request_lifecycle.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@


.. _aiohttp-request-lifecycle:


The aiohttp Request Lifecycle
=============================


Why is aiohttp client API that way?
--------------------------------------


The first time you use aiohttp, you'll notice that a simple HTTP request is performed not with one, but with up to three steps:


.. code-block:: python


async with aiohttp.ClientSession() as session:
async with session.get('http://python.org') as response:
print(await response.text())


It's especially unexpected when coming from other libraries such as the very popular :term:`requests`, where the "hello world" looks like this:


.. code-block:: python


response = requests.get('http://python.org')
print(response.text())


So why is the aiohttp snippet so verbose?


Because aiohttp is asynchronous, its API is designed to make the most out of non-blocking network operations. In a code like this, requests will block three times, and does it transparently, while aiohttp gives the event loop three opportunities to switch context:


- When doing the ``.get()``, both libraries send a GET request to the remote server. For aiohttp, this means asynchronous I/O, which is here marked with an ``async with`` that gives you the guaranty that not only it doesn't block, but that it's cleanly finalized.
- When doing ``response.text`` in requests, you just read an attribute. The call to ``.get()`` already preloaded and decoded the entire response payload, in a blocking manner. aiohttp loads only the headers when ``.get()`` is executed, letting you decide to pay the cost of loading the body afterward, in a second asynchronous operation. Hence the ``await response.text()``.
- ``async with aiohttp.ClientSession()`` does not perform I/O when entering the block, but at the end of it, it will ensure all remaining resources are closed correctly. Again, this is done asynchronously and must be marked as such. The session is also a performance tool, as it manages a pool of connections for you, allowing you to reuse them instead of opening and closing a new one at each request. You can even `manage the pool size by passing a connector object <client_advanced.html#limiting-connection-pool-size>`_.

Using a session as a best practice
-----------------------------------

The requests library does in fact also provides a session system. Indeed, it lets you do:

.. code-block:: python

with requests.session() as session:
response = session.get('http://python.org')
print(response.text)

It just not the default behavior, nor is it advertised early in the documentation. Because of this, most users take a hit in performances, but can quickly start hacking. And for requests, it's an understandable trade-off, since its goal is to be "HTTP for humans" and simplicity has always been more important than performance in this context.

However, if one uses aiohttp, one chooses asynchronous programming, a paradigm that makes the opposite trade-off: more verbosity for better performances. And so the library default behavior reflects this, encouraging you to use performant best practices from the start.

How to use the ClientSession ?
-------------------------------

By default the :class:`aiohttp.ClientSession` object will hold a connector with a maximum of 100 connections, putting the rest in a queue. This is quite a big number, this means you must be connected to a hundred different servers (not pages!) concurrently before even having to consider if your task needs resource adjustment.

In fact, you can picture the session object as a user starting and closing a browser: it wouldn't make sense to do that every time you want to load a new tab.

So you are expected to reuse a session object and make many requests from it. For most scripts and average-sized softwares, this means you can create a single session, and reuse it for the entire execution of the program. You can even pass the session around as a parameter in functions. E.G, the typical "hello world":

.. code-block:: python

import aiohttp
import asyncio

async def main():
async with aiohttp.ClientSession() as session:
async with session.get('http://python.org') as response:
html = await response.text()
print(html)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())


Can become this:


.. code-block:: python

import aiohttp
import asyncio

async def fetch(session, url):
async with session.get(url) as response:
return await response.text()

async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, 'http://python.org')
print(html)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

On more complex code bases, you can even create a central registry to hold the session object from anywhere in the code, or a higher level ``Client`` class that holds a reference to it.

When to create more than one session object then? It arises when you want more granularity with your resources management:

- you want to group connections by a common configuration. E.G: sessions can set cookies, headers, timeout values, etc. that are shared for all connections they holds.
- you need several threads and want to avoid sharing a mutable object between them.
- you want several connection pools to benefit from different queues and assign priorities. E.G: one session never uses the queue and is for high priority requests, the other one has a small concurrency limit and a very long queue, for non important requests.
46 changes: 31 additions & 15 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,42 @@ separate commands anymore!
Getting Started
===============

Client example::
Client example
--------------

import aiohttp
import asyncio
.. code-block:: python

async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
import aiohttp
import asyncio

async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, 'http://python.org')
print(html)
async def main():

if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
async with aiohttp.ClientSession() as session:
async with session.get('http://python.org') as response:

print("Status:", response.status)
print("Content-type:", response.headers['content-type'])

html = await response.text()
print("Body:", html[:15], "...")

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

This prints:

Server example::
.. code-block:: text

Status: 200
Content-type: text/html; charset=utf-8
Body: <!doctype html> ...

Comming from :term:`requests` ? Read :ref:`why we need so many lines <aiohttp-request-lifecycle>`.

Server example:
----------------

.. code-block:: python

from aiohttp import web

Expand All @@ -100,7 +117,6 @@ Server example::
For more information please visit :ref:`aiohttp-client` and
:ref:`aiohttp-web` pages.


What's new in aiohttp 3?
========================

Expand Down