Skip to content

Commit

Permalink
clean up types and remove asyncio
Browse files Browse the repository at this point in the history
  • Loading branch information
rhymiz committed Aug 23, 2023
1 parent 4349ee2 commit 09edc80
Show file tree
Hide file tree
Showing 11 changed files with 41 additions and 43 deletions.
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ check_untyped_defs = True
disallow_incomplete_defs = True
disallow_untyped_defs = True
disallow_untyped_calls = True
strict_equality = True
strict_equality = True
2 changes: 1 addition & 1 deletion deez/conf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def configure(self) -> None:

settings_configured.send(self)

def __getattr__(self, item) -> Any:
def __getattr__(self, item: str) -> Any:
if not self._configured:
raise DeezError(
"Settings have not yet been configured. "
Expand Down
31 changes: 15 additions & 16 deletions deez/core/router.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import asyncio
from dataclasses import dataclass
from functools import lru_cache
from typing import Any, Dict, List, Match, Tuple, Type, Union
from typing import Any, Callable, Dict, List, Match, Tuple, Type, Union, Pattern

from deez.core.api_gateway import api_gateway_response
from deez.exceptions import NoResponseError, NotFound
from deez.logger import get_logger
from deez.middleware import Middleware
from deez.request import Request
from deez.resource import Resource
from deez.response import BaseResponse
from deez.response import JsonResponse
import typing

if typing.TYPE_CHECKING:
from deez.conf import Setting


@dataclass
Expand All @@ -35,12 +38,12 @@ class Router:
def __init__(
self,
*,
routes,
route_patterns,
settings,
middleware,
middleware_reversed,
exception_handler,
routes: Dict[str, Type[Resource]],
route_patterns: List[Pattern[str]],
settings: "Setting",
middleware: List[Middleware],
middleware_reversed: List[Middleware],
exception_handler: Callable[..., Dict[str, Any]],
) -> None:
self._routes = routes
self._settings = settings
Expand Down Expand Up @@ -81,8 +84,8 @@ def _prepare_response(
self,
middleware: List[Middleware],
request: Request,
response: BaseResponse,
) -> BaseResponse:
response: JsonResponse,
) -> JsonResponse:
for mw in middleware:
if hasattr(mw, "before_response") and mw.run(request.path):
mw.before_response(response=response)
Expand Down Expand Up @@ -122,11 +125,7 @@ def execute(
# middleware that needs to run before calling the resource
request = self._prepare_request(self._middleware, request)

# TODO: experimental asyncio support: should be looked at more closely?
if asyncio.iscoroutinefunction(resource_instance.dispatch):
response = asyncio.run(resource_instance.dispatch(request=request, **kwargs))
else:
response = resource_instance.dispatch(request=request, **kwargs)
response = resource_instance.dispatch(request=request, **kwargs)
if not response:
raise NoResponseError(f"{resource_instance} did not return a response")

Expand Down
2 changes: 1 addition & 1 deletion deez/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import Any


def method_proxy(cls, attr: str) -> Any:
def method_proxy(cls: Any, attr: str) -> Any:
return object.__getattribute__(cls, attr)
4 changes: 2 additions & 2 deletions deez/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def __init__(self, data: Dict[str, str]) -> None:
super().__init__(data)
self._lowercase_keys(self._data)

def _lowercase_keys(self, data) -> None:
def _lowercase_keys(self, data: Dict[str, str]) -> None:
"""
Lowercase all keys so that headers are case-insensitive.
"""
Expand Down Expand Up @@ -121,5 +121,5 @@ def _build(self) -> None:
def __str__(self) -> str:
return "%s %s" % (self.method, self.path)

def __getattr__(self, item) -> Any:
def __getattr__(self, item: str) -> Any:
return method_proxy(self, item)
20 changes: 7 additions & 13 deletions deez/resource.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import asyncio
import sys
from typing import Callable
from typing import Any, Callable, Set, Dict

from deez.conf import settings
from deez.exceptions import MethodNotAllowed
from deez.request import Request
from deez.response import JsonResponse

_HTTP_METHODS = {"get", "post", "put", "patch", "delete", "head", "options"}
_HTTP_METHODS: Set[str] = {"get", "post", "put", "patch", "delete", "head", "options"}


class Resource:
Expand All @@ -22,29 +21,26 @@ def _get_allowed_methods(self) -> str:
methods.append(m.upper())
return ", ".join(methods)

async def head(self, request, **kwargs) -> JsonResponse:
def head(self, request: Request, **kwargs: Dict[str, Any]) -> JsonResponse:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD
func: Callable = getattr(self, "get")
func: Callable[..., JsonResponse] = getattr(self, "get")
if func is None:
return JsonResponse(data={}, status_code=405)

if asyncio.iscoroutinefunction(func):
response = asyncio.run(func(request, **kwargs))
else:
response = func(request, **kwargs)
response = func(request, **kwargs)
response.data = {}
response.headers["Content-Length"] = sys.getsizeof(response.data)
return response

async def options(self, request, **kwargs) -> JsonResponse:
def options(self, request: Request, **kwargs: Dict[str, Any]) -> JsonResponse:
headers = {
"Access-Control-Max-Age": settings.ACCESS_CONTROL_MAX_AGE,
"Access-Control-Allow-Origin": settings.ACCESS_CONTROL_ALLOW_ORIGIN,
"Access-Control-Allow-Methods": self._get_allowed_methods(),
}
return JsonResponse(data={}, status_code=204, headers=headers)

async def dispatch(self, request: Request, **kwargs) -> JsonResponse:
def dispatch(self, request: Request, **kwargs: Dict[str, Any]) -> JsonResponse:
"""
Tries to call the underlying user-implemented method that is responsible for
serving the HTTP Method.
Expand All @@ -54,8 +50,6 @@ async def dispatch(self, request: Request, **kwargs) -> JsonResponse:
raise MethodNotAllowed("method not allowed!")

func: Callable[..., JsonResponse] = getattr(self, method)
if asyncio.iscoroutinefunction(func):
return await func(request=request, **kwargs)
return func(request=request, **kwargs)

def __str__(self) -> str:
Expand Down
8 changes: 4 additions & 4 deletions deez/response.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import abstractmethod
from typing import Any, Dict, Union
from typing import Any, Dict, Union, Tuple

from deez.contrib.serialization import json_dumps

Expand All @@ -20,15 +20,15 @@ def __init__(
self.content_type = content_type

@abstractmethod
def render(self, *args, **kwargs) -> Union[bytes, None]:
def render(self, *args: Tuple[Any], **kwargs: Dict[str, Any]) -> Union[bytes, None]:
pass


class NoContentResponse(BaseResponse):
def __init__(self, headers: Union[Dict[str, Any], None] = None) -> None:
super().__init__(headers=headers, status_code=204)

def render(self, *args, **kwargs) -> None:
def render(self, *args: Tuple[Any], **kwargs: Dict[str, Any]) -> None:
return None


Expand All @@ -45,5 +45,5 @@ def __init__(
headers=headers,
)

def render(self, *args, **kwargs) -> bytes:
def render(self, *args: Tuple[Any], **kwargs: Dict[str, Any]) -> bytes:
return json_dumps(self.data)
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ mypy = "^1.5.1"
[tool.black]
line-length = 120

[tool.mypy]
python_version = "3.9"
disallow_untyped_defs = true
exclude = ["build", "dist", ".venv", "tests", "example"]

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
2 changes: 1 addition & 1 deletion tests/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

def _make_resource() -> Type[Resource]:
class DynamicResource(Resource):
async def get(self, request, *args, **kwargs) -> JsonResponse:
def get(self, request, *args, **kwargs) -> JsonResponse:
return JsonResponse({"foo": "bar"})

return DynamicResource
Expand Down
4 changes: 2 additions & 2 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from deez.conf import settings


def test_can_access_existing_attributes():
def test_can_access_existing_attributes() -> None:
assert settings.DEBUG == True


def test_raises_attribute_error_when_attribute_nonexistent():
def test_raises_attribute_error_when_attribute_nonexistent() -> None:
with pytest.raises(AttributeError) as e:
assert settings.HAMZA

Expand Down
4 changes: 2 additions & 2 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
from deez.utils import middleware_resolver


def test_resolve_default_middleware():
def test_resolve_default_middleware() -> None:
refs = ["tests.middleware.TestMiddleware"]
resolved_refs = middleware_resolver(refs)
assert len(resolved_refs) == 1


def test_resolve_scoped_middleware():
def test_resolve_scoped_middleware() -> None:
refs = [
{
"middleware": "tests.middleware.TestMiddleware",
Expand Down

0 comments on commit 09edc80

Please sign in to comment.