Skip to content

Commit

Permalink
Fully annotate bravado_asyncio, add mypy type checking to test run
Browse files Browse the repository at this point in the history
  • Loading branch information
sjaensch committed Jan 27, 2018
1 parent 3297189 commit 264df7c
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 21 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/.cache
/.coverage
/.idea
/.mypy_cache
/.tox
/coverage-html
/dist
Expand Down
28 changes: 19 additions & 9 deletions bravado_asyncio/http_client.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import asyncio
import logging
from collections import Mapping
from typing import Optional # noqa
from typing import Any
from typing import Callable
from typing import Dict
from typing import Optional
from typing import Union

import aiohttp
from aiohttp.formdata import FormData
from bravado import http_future # noqa
from bravado.http_client import HttpClient
from bravado.http_future import HttpFuture
from bravado_core.operation import Operation
from bravado_core.response import IncomingResponse
from bravado_core.schema import is_list_like
from multidict import MultiDict

Expand Down Expand Up @@ -47,7 +54,7 @@ class AsyncioClient(HttpClient):
async / await.
"""

def __init__(self, run_mode: RunMode=RunMode.THREAD, loop: Optional[asyncio.AbstractEventLoop]=None):
def __init__(self, run_mode: RunMode=RunMode.THREAD, loop: Optional[asyncio.AbstractEventLoop]=None) -> None:
"""Instantiate a client using the given run_mode. If you do not pass in an event loop, then
either a shared loop in a separate thread (THREAD mode) or the default asyncio
event loop (FULL_ASYNCIO mode) will be used.
Expand All @@ -57,10 +64,10 @@ def __init__(self, run_mode: RunMode=RunMode.THREAD, loop: Optional[asyncio.Abst
self.run_mode = run_mode
if self.run_mode == RunMode.THREAD:
self.loop = loop or get_thread_loop()
self.run_coroutine_func = asyncio.run_coroutine_threadsafe
self.run_coroutine_func = asyncio.run_coroutine_threadsafe # type: Callable
self.response_adapter = AioHTTPResponseAdapter
self.bravado_future_class = HttpFuture
self.future_adapter = FutureAdapter
self.future_adapter = FutureAdapter # type: http_future.FutureAdapter
elif run_mode == RunMode.FULL_ASYNCIO:
from aiobravado.http_future import HttpFuture as AsyncioHttpFuture

Expand All @@ -78,16 +85,19 @@ def __init__(self, run_mode: RunMode=RunMode.THREAD, loop: Optional[asyncio.Abst
else:
self.client_session = get_client_session(self.loop)

def request(self, request_params, operation=None, response_callbacks=None,
also_return_response=False):
def request(
self,
request_params: Dict[str, Any],
operation: Optional[Operation]=None,
response_callbacks: Optional[Callable[[IncomingResponse, Operation], None]]=None,
also_return_response: bool=False,
) -> HttpFuture:
"""Sets up the request params for aiohttp and executes the request in the background.
:param request_params: request parameters for the http request.
:type request_params: dict
:param operation: operation that this http request is for. Defaults
to None - in which case, we're obviously just retrieving a Swagger
Spec.
:type operation: :class:`bravado_core.operation.Operation`
:param response_callbacks: List of callables to post-process the
incoming response. Expects args incoming_response and operation.
:param also_return_response: Consult the constructor documentation for
Expand Down Expand Up @@ -145,7 +155,7 @@ def request(self, request_params, operation=None, response_callbacks=None,
also_return_response,
)

def prepare_params(self, params):
def prepare_params(self, params: Optional[Dict[str, Any]]) -> MultiDict[str, Union[list, str]]:
if not params:
return params

Expand Down
20 changes: 11 additions & 9 deletions bravado_asyncio/response_adapter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import asyncio
from typing import Any
from typing import Dict
from typing import TypeVar

from bravado_core.response import IncomingResponse
Expand All @@ -22,28 +24,28 @@ def __call__(self: T, response: AsyncioResponse) -> T:
return self

@property
def status_code(self):
def status_code(self) -> int:
return self._delegate.status

@property
def text(self):
def text(self) -> str:
future = asyncio.run_coroutine_threadsafe(self._delegate.text(), self._loop)
return future.result(self._remaining_timeout)

@property
def raw_bytes(self):
def raw_bytes(self) -> bytes:
future = asyncio.run_coroutine_threadsafe(self._delegate.read(), self._loop)
return future.result(self._remaining_timeout)

@property
def reason(self):
def reason(self) -> str:
return self._delegate.reason

@property
def headers(self):
def headers(self) -> Dict[str, str]:
return self._delegate.headers

def json(self, **_):
def json(self, **_: Any) -> Dict[str, Any]:
future = asyncio.run_coroutine_threadsafe(self._delegate.json(), self._loop)
return future.result(self._remaining_timeout)

Expand All @@ -53,12 +55,12 @@ class AsyncioHTTPResponseAdapter(AioHTTPResponseAdapter):
Methods are coroutines if they call coroutines themselves and need to be awaited."""

@property
async def text(self):
async def text(self) -> str: # type: ignore
return await asyncio.wait_for(self._delegate.text(), timeout=self._remaining_timeout, loop=self._loop)

@property
async def raw_bytes(self):
async def raw_bytes(self) -> bytes: # type: ignore
return await asyncio.wait_for(self._delegate.read(), timeout=self._remaining_timeout, loop=self._loop)

async def json(self, **_):
async def json(self, **_: Any) -> Dict[str, Any]: # type: ignore
return await asyncio.wait_for(self._delegate.json(), timeout=self._remaining_timeout, loop=self._loop)
10 changes: 10 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[mypy]
ignore_missing_imports = True
strict_optional = True
warn_redundant_casts = True
warn_unused_ignores = True

[mypy-bravado_asyncio.*]
disallow_any_decorated = True
disallow_untyped_calls = True
disallow_untyped_defs = True
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ bravado>=9.2.0
coverage
ephemeral_port_reserve
mock
mypy
pytest
tox
u-msgpack-python
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ def wait_unit_service_starts(url, timeout=10):
while time.time() < start + timeout:
try:
urllib.request.urlopen(url, timeout=2)
except urllib.error.HTTPError:
except urllib.error.HTTPError: # pragma: no cover
return
except urllib.error.URLError:
except urllib.error.URLError: # pragma: no cover
time.sleep(0.1)


Expand Down
5 changes: 4 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ envlist = py35, py36, pre-commit
deps =
-rrequirements-dev.txt
commands =
coverage run --source=bravado_asyncio/ --omit=bravado_asyncio/__init__.py -m pytest --capture=no --strict -vvv {posargs:tests/}
coverage run --source=bravado_asyncio/,tests/ --omit=bravado_asyncio/__init__.py -m pytest --capture=no --strict -vvv {posargs:tests/}
coverage report --omit=.tox/*,tests/* -m
# abort if coverage is below 100% for test files; there might be tests that don't get executed
coverage report --fail-under=100 --include tests/* -m
mypy bravado_asyncio testing

[flake8]
exclude = .git,__pycache__,.tox,docs,venv
Expand Down

0 comments on commit 264df7c

Please sign in to comment.