Skip to content

Commit

Permalink
Add an httpx backend (#86)
Browse files Browse the repository at this point in the history
  • Loading branch information
yeraydiazdiaz authored and brettcannon committed Oct 25, 2019
1 parent 24feb6c commit a3e6937
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 9 deletions.
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Changelog

- Fix mypy warnings about the ``Dict`` and ``Mapping`` generic types lacking
type parameters.
- Add `httpx <https://www.encode.io/httpx>`_ backend.

3.1.0
''''''
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = []


# -- Options for HTMLHelp output ------------------------------------------
Expand Down
19 changes: 19 additions & 0 deletions docs/httpx.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
:mod:`gidgethub.httpx` --- httpx support
=============================================

.. module:: gidgethub.httpx

.. class:: GitHubAPI(client, requester, *, oauth_token=None, cache=None)

An implementation of :class:`gidgethub.abc.GitHubAPI` using
`httpx <https://www.encode.io/httpx>`_. Typical usage will be::

import httpx
import gidgethub.httpx


async with httpx.AsyncClient() as client:
gh = gidgethub.httpx.GitHubAPI(client, requester,
oauth_token=oauth_token)
# Make your requests, e.g. ...
data = await gh.getitem("/rate_limit")
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ Contents
aiohttp
treq
tornado
httpx


About the title
Expand Down
24 changes: 24 additions & 0 deletions gidgethub/httpx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import asyncio
from typing import Mapping, Tuple, Any

import httpx

from . import abc as gh_abc


class GitHubAPI(gh_abc.GitHubAPI):
def __init__(self, client: httpx.AsyncClient, *args: Any,
**kwargs: Any) -> None:
self._client = client
super().__init__(*args, **kwargs)

async def _request(self, method: str, url: str, headers: Mapping[str, str],
body: bytes = b'') -> Tuple[int, Mapping[str, str], bytes]:
"""Make an HTTP request."""
response = await self._client.request(
method, url, headers=dict(headers), data=body)
return response.status_code, response.headers, await response.read()

async def sleep(self, seconds: float) -> None:
"""Sleep for the specified number of seconds."""
await asyncio.sleep(seconds)
39 changes: 39 additions & 0 deletions gidgethub/test/test_httpx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import datetime

import httpx
import pytest

from .. import httpx as gh_httpx
from .. import sansio


@pytest.mark.asyncio
async def test_sleep():
delay = 1
start = datetime.datetime.now()
async with httpx.AsyncClient() as client:
gh = gh_httpx.GitHubAPI(client, "gidgethub")
await gh.sleep(delay)
stop = datetime.datetime.now()
assert (stop - start) > datetime.timedelta(seconds=delay)


@pytest.mark.asyncio
async def test__request():
"""Make sure that that abstract method is implemented properly."""
request_headers = sansio.create_headers("gidgethub")
async with httpx.AsyncClient() as client:
gh = gh_httpx.GitHubAPI(client, "gidgethub")
aio_call = await gh._request("GET", "https://api.github.com/rate_limit",
request_headers)
data, rate_limit, _ = sansio.decipher_response(*aio_call)
assert "rate" in data


@pytest.mark.asyncio
async def test_get():
"""Integration test."""
async with httpx.AsyncClient() as client:
gh = gh_httpx.GitHubAPI(client, "gidgethub")
data = await gh.getitem("/rate_limit")
assert "rate" in data
15 changes: 9 additions & 6 deletions gidgethub/tornado.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Mapping, Optional, Tuple
from typing import Mapping, Tuple, Union, List, Dict, Any

from tornado import gen
from tornado import httpclient
Expand All @@ -11,11 +11,14 @@ class GitHubAPI(gh_abc.GitHubAPI):
async def _request(self, method: str, url: str, headers: Mapping[str, str],
body: bytes = b'') -> Tuple[int, Mapping[str, str], bytes]:
"""Make an HTTP request."""
if method == "GET" and not body:
real_body = None
else:
real_body = body
request = httpclient.HTTPRequest(url, method, headers, real_body)
# Setting 'body' to None fails type checking, so only add a 'body' argument if necessary.
args: List[Union[str, Dict[Any, Any], bytes]] = [url, method, dict(headers)]
if method != "GET" and body:
args.append(body)
# The below line is skipped from mypy because Tornado's HTTPRequest signature
# requires many types of arguments some of which are internal to it
# adding all of them to the `args` would be impractical.
request = httpclient.HTTPRequest(*args) # type: ignore
# Since Tornado has designed AsyncHTTPClient to be a singleton, there's
# no reason not to simply instantiate it every time.
client = httpclient.AsyncHTTPClient()
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ classifiers = ["Intended Audience :: Developers",
]

[tool.flit.metadata.requires-extra]
test = ["pytest>=3.0.0", "pytest-asyncio"]
test = ["pytest>=3.0.0", "pytest-asyncio", "pytest-tornasync"]
doc = ["sphinx"]
# The 'dev' target should contain all dependencies listed in any extras coming
# after it to make sure that the test suite will run successfully.
dev = ["aiohttp", "mypy", "pytest-cov", "treq", "twisted[tls]", "tornado"]
dev = ["aiohttp", "mypy", "pytest-cov", "treq", "twisted[tls]", "tornado", "httpx"]
aiohttp = ["aiohttp"]
treq = ["treq", "twisted[tls]"]
tornado = ["tornado"]
httpx = ["httpx"]

[tool.flit.metadata.urls]
Documentation = "https://gidgethub.readthedocs.io"

0 comments on commit a3e6937

Please sign in to comment.