Welcome to aiosonic

Really Fast Python asyncio HTTP 1.1 client, Support for http 2.0 is planned.

Current version is 0.7.2.

Repo is hosted at Github.

Features

  • Keepalive and Smart Pool of Connections

  • Multipart File Uploads

  • Chunked responses handling

  • Chunked requests

  • Fully type annotated.

  • Connection Timeouts

  • Automatic Decompression

  • Follow Redirects

  • 100% test coverage.

Requirements

  • Python>=3.6

Install

$ pip install aiosonic

Getting Started

import asyncio
import aiosonic
import json


async def run():
    client = aiosonic.HttpClient()

    # ##################
    # Sample get request
    # ##################
    response = await client.get('https://www.google.com/')
    assert response.status_code == 200
    assert 'Google' in (await response.text())

    # ##################
    # Post data as multipart form
    # ##################
    url = "https://postman-echo.com/post"
    posted_data = {'foo': 'bar'}
    response = await client.post(url, data=posted_data)

    assert response.status_code == 200
    data = json.loads(await response.content())
    assert data['form'] == posted_data

    # ##################
    # Posted as json
    # ##################
    response = await client.post(url, json=posted_data)

    assert response.status_code == 200
    data = json.loads(await response.content())
    assert data['json'] == posted_data

    # ##################
    # Sample request + timeout
    # ##################
    from aiosonic.timeout import Timeouts
    timeouts = Timeouts(
        sock_read=10,
        sock_connect=3
    )
    response = await client.get('https://www.google.com/', timeouts=timeouts)
    assert response.status_code == 200
    assert 'Google' in (await response.text())
    await client.shutdown()

    print('success')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())

Benchmarks

Some benchmarking

$ python ./tests/performance.py
doing tests...
{
 "aiosonic": "1000 requests in 110.56 ms",
 "aiosonic cyclic": "1000 requests in 207.75 ms",
 "aiohttp": "1000 requests in 357.19 ms",
 "requests": "1000 requests in 4274.21 ms",
 "httpx": "1000 requests in 800.98 ms"
}
aiosonic is 223.05% faster than aiohttp
aiosonic is 3765.79% faster than requests
aiosonic is 87.90% faster than aiosonic cyclic
aiosonic is 624.45% faster than httpx

Contributing

  1. Fork

  2. create a branch feature/your_feature

  3. commit - push - pull request

Thanks :)

Indices and tables

Examples

TODO: More examples

Download file

import asyncio
import aiosonic
import json


async def run():
    url = 'https://images.dog.ceo/breeds/leonberg/n02111129_2301.jpg'
    async with aiosonic.HTTPClient() as client:

       res = await client.get(url)
       assert res.status_code == 200

    if res.chunked:
        # write in chunks
        with open('dog_image.jpg', 'wb') as _file:
            async for chunk in res.read_chunks():
                _file.write(chunk)
    else:
        # or write all bytes, for chunked this also works
        with open('dog_image.jpg', 'wb') as _file:
            _file.write(await res.content())


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())

Concurrent Requests

import aiosonic
import asyncio


async def main():
    urls = [
        'https://www.facebook.com/',
        'https://www.google.com/',
        'https://twitch.tv/',
        'https://linkedin.com/',
    ]
    async with aiosonic.HTTPClient() as client:
        # asyncio.gather is the key for concurrent requests.
        responses = await asyncio.gather(*[client.get(url) for url in urls])

        # stream/chunked responses doesn't release the connection acquired
        # from the pool until the response has been read, so better to read
        # it.
        for response in responses:
            if response.chunked:
                await response.text()

        assert all([res.status_code in [200, 301] for res in responses])

asyncio.run(main())

Reference

TODO: get better this page

class aiosonic.HTTPClient(connector: aiosonic.connectors.TCPConnector = None)

aiosonic.HTTPClient class.

This class holds the client creation that will be used for requests.

async aiosonic.HTTPClient.request(self, url: str, method: str = 'GET', headers: Union[Dict[str, str], List[Tuple[str, str]], aiosonic.HttpHeaders] = None, params: Union[Dict[str, str], Sequence[Tuple[str, str]]] = None, data: Union[str, bytes, dict, tuple, AsyncIterator[bytes], Iterator[bytes]] = None, multipart: bool = False, verify: bool = True, ssl: ssl.SSLContext = None, timeouts: aiosonic.timeout.Timeouts = None, follow: bool = False, http2: bool = False)aiosonic.HttpResponse

Do http request.

Params:
  • url: url of request

  • method: Http method of request

  • headers: headers to add in request

  • params: query params to add in request if not manually added

  • data: Data to be sent, this param is ignored for get requests.

  • multipart: Tell aiosonic if request is multipart

  • verify: parameter to indicate whether to verify ssl

  • ssl: this parameter allows to specify a custom ssl context

  • timeouts: parameter to indicate timeouts for request

  • follow: parameter to indicate whether to follow redirects

  • http2: flag to indicate whether to use http2 (experimental)

async aiosonic.HTTPClient.get(self, url: str, headers: Union[Dict[str, str], List[Tuple[str, str]], aiosonic.HttpHeaders] = None, params: Union[Dict[str, str], Sequence[Tuple[str, str]]] = None, verify: bool = True, ssl: ssl.SSLContext = None, timeouts: aiosonic.timeout.Timeouts = None, follow: bool = False, http2: bool = False)aiosonic.HttpResponse

Do get http request.

async aiosonic.HTTPClient.post(self, url: str, data: Union[str, bytes, dict, tuple, AsyncIterator[bytes], Iterator[bytes]] = None, headers: Union[Dict[str, str], List[Tuple[str, str]], aiosonic.HttpHeaders] = None, json: dict = None, params: Union[Dict[str, str], Sequence[Tuple[str, str]]] = None, json_serializer=<function dumps>, multipart: bool = False, verify: bool = True, ssl: ssl.SSLContext = None, timeouts: aiosonic.timeout.Timeouts = None, follow: bool = False, http2: bool = False)aiosonic.HttpResponse

Do post http request.

async aiosonic.HTTPClient.put(self, url: str, data: Union[str, bytes, dict, tuple, AsyncIterator[bytes], Iterator[bytes]] = None, headers: Union[Dict[str, str], List[Tuple[str, str]], aiosonic.HttpHeaders] = None, json: dict = None, params: Union[Dict[str, str], Sequence[Tuple[str, str]]] = None, json_serializer=<function dumps>, multipart: bool = False, verify: bool = True, ssl: ssl.SSLContext = None, timeouts: aiosonic.timeout.Timeouts = None, follow: bool = False, http2: bool = False)aiosonic.HttpResponse

Do put http request.

async aiosonic.HTTPClient.patch(self, url: str, data: Union[str, bytes, dict, tuple, AsyncIterator[bytes], Iterator[bytes]] = None, headers: Union[Dict[str, str], List[Tuple[str, str]], aiosonic.HttpHeaders] = None, json: dict = None, params: Union[Dict[str, str], Sequence[Tuple[str, str]]] = None, json_serializer=<function dumps>, multipart: bool = False, verify: bool = True, ssl: ssl.SSLContext = None, timeouts: aiosonic.timeout.Timeouts = None, follow: bool = False, http2: bool = False)aiosonic.HttpResponse

Do patch http request.

async aiosonic.HTTPClient.delete(self, url: str, data: Union[str, bytes, dict, tuple, AsyncIterator[bytes], Iterator[bytes]] = b'', headers: Union[Dict[str, str], List[Tuple[str, str]], aiosonic.HttpHeaders] = None, json: dict = None, params: Union[Dict[str, str], Sequence[Tuple[str, str]]] = None, json_serializer=<function dumps>, multipart: bool = False, verify: bool = True, ssl: ssl.SSLContext = None, timeouts: aiosonic.timeout.Timeouts = None, follow: bool = False, http2: bool = False)aiosonic.HttpResponse

Do delete http request.

async aiosonic.HTTPClient.wait_requests(self, timeout: int = 30)

Wait until all pending requests are done.

If timeout, returns false.

This is useful when doing safe shutdown of a process.

Classes

class aiosonic.HttpHeaders(data=None, **kwargs)

Http headers dict.

class aiosonic.HttpResponse

Custom HttpResponse class for handling responses.

Properties:
  • status_code (int): response status code

  • headers (HttpHeaders): headers in case insensitive dict

  • raw_headers (List[Tuple[bytes, bytes]]): headers as raw format

async content() → bytes

Read response body.

async json(json_decoder=<function loads>) → dict

Read response body.

read_chunks() → AsyncIterator[bytes]

Read chunks from chunked response.

property status_code

Get status code.

async text() → str

Read response body.

class aiosonic.timeout.Timeouts(sock_connect: Optional[float] = 5, sock_read: Optional[float] = 30, pool_acquire: Optional[float] = None, request_timeout: Optional[float] = 60)

Timeouts class wrapper.

Types

aiosonic.DataType = typing.Union[str, bytes, dict, tuple, typing.AsyncIterator[bytes], typing.Iterator[bytes]]

The central part of internal API.

This represents a generic version of type ‘origin’ with type arguments ‘params’. There are two kind of these aliases: user defined and special. The special ones are wrappers around builtin collections and ABCs in collections.abc. These must have ‘name’ always set. If ‘inst’ is False, then the alias can’t be instantiated, this is used by e.g. typing.List and typing.Dict.

aiosonic.HeadersType = typing.Union[typing.Dict[str, str], typing.List[typing.Tuple[str, str]], aiosonic.HttpHeaders]

Headers