Skip to content

Add decorator type hint #101

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
9 changes: 4 additions & 5 deletions nest/core/decorators/class_based_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@
from fastapi import APIRouter, Depends
from starlette.routing import Route, WebSocketRoute

T = TypeVar("T")
K = TypeVar("K", bound=Callable[..., Any])
C = TypeVar("C", bound=object)

CBV_CLASS_KEY = "__cbv_class__"


def class_based_view(router: APIRouter, cls: Type[T]) -> Type[T]:
def class_based_view(router: APIRouter, cls: Type[C]) -> Type[C]:
"""
Replaces any methods of the provided class `cls` that are endpoints of routes in `router` with updated
function calls that will properly inject an instance of `cls`.
Expand All @@ -48,7 +47,7 @@ def class_based_view(router: APIRouter, cls: Type[T]) -> Type[T]:
return cls


def _init_cbv(cls: Type[Any]) -> None:
def _init_cbv(cls: Type[C]) -> None:
"""
Idempotently modifies the provided `cls`, performing the following modifications:
* The `__init__` function is updated to set any class-annotated dependencies as instance attributes
Expand Down Expand Up @@ -110,4 +109,4 @@ def _update_cbv_route_endpoint_signature(
for parameter in old_parameters[1:]
]
new_signature = old_signature.replace(parameters=new_parameters)
setattr(route.endpoint, "__signature__", new_signature)
setattr(route.endpoint, "__signature__", new_signature)
7 changes: 4 additions & 3 deletions nest/core/decorators/controller.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from typing import Optional, Type
from typing import Optional, Type, TypeVar, Callable

from fastapi.routing import APIRouter

from nest.core.decorators.class_based_view import class_based_view as ClassBasedView
from nest.core.decorators.http_method import HTTPMethod
from nest.core.decorators.utils import get_instance_variables, parse_dependencies

C = TypeVar('C', bound=object)

def Controller(prefix: Optional[str] = None, tag: Optional[str] = None):
def Controller(prefix: Optional[str] = None, tag: Optional[str] = None) -> Callable[[Type[C]], Type[C]]:
"""
Decorator that turns a class into a controller, allowing you to define
routes using FastAPI decorators.
Expand All @@ -23,7 +24,7 @@ def Controller(prefix: Optional[str] = None, tag: Optional[str] = None):
# Default route_prefix to tag_name if route_prefix is not provided
route_prefix = process_prefix(prefix, tag)

def wrapper(cls: Type) -> Type[ClassBasedView]:
def wrapper(cls: Type[C]) -> Type[C]:
router = APIRouter(tags=[tag] if tag else None)

# Process class dependencies
Expand Down
17 changes: 13 additions & 4 deletions nest/core/decorators/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@

from fastapi.exceptions import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from typing import TypeVar, Callable, Union

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

if sys.version_info >= (3, 10):
from typing import ParamSpec
else:
from typing_extensions import ParamSpec

def db_request_handler(func):
P = ParamSpec("P")
R = TypeVar("R")


def db_request_handler(func: Callable[[P], R]) -> Callable[[object, P], Union[R, HTTPException]]:
"""
Decorator that handles database requests, including error handling and session management.

Expand All @@ -19,7 +28,7 @@ def db_request_handler(func):
function: The decorated function.
"""

def wrapper(self, *args, **kwargs):
def wrapper(self, *args: P.args, **kwargs: P.kwargs) -> Union[R, HTTPException]:
try:
s = time.time()
result = func(self, *args, **kwargs)
Expand All @@ -40,7 +49,7 @@ def wrapper(self, *args, **kwargs):
return wrapper


def async_db_request_handler(func):
def async_db_request_handler(func: Callable[[P], R]) -> Callable[[P], R]:
"""
Asynchronous decorator that handles database requests, including error handling,
session management, and logging for async functions.
Expand All @@ -52,7 +61,7 @@ def async_db_request_handler(func):
function: The decorated async function.
"""

async def wrapper(*args, **kwargs):
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
try:
start_time = time.time()
result = await func(*args, **kwargs) # Awaiting the async function
Expand Down
13 changes: 12 additions & 1 deletion nest/core/decorators/http_code.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
from nest.common.constants import STATUS_CODE_TOKEN
from typing import TypeVar, Callable
import sys

if sys.version_info >= (3, 10):
from typing import ParamSpec, TypeAlias
else:
from typing_extensions import ParamSpec, TypeAlias

P = ParamSpec("P")
R = TypeVar("R")
Func: TypeAlias = Callable[[P], R]


def HttpCode(status_code: int):
Expand All @@ -9,7 +20,7 @@ def HttpCode(status_code: int):
status_code (int): The HTTP status code for the response.
"""

def decorator(func):
def decorator(func: Func) -> Func:
if not hasattr(func, STATUS_CODE_TOKEN):
setattr(func, STATUS_CODE_TOKEN, status_code)
return func
Expand Down
36 changes: 24 additions & 12 deletions nest/core/decorators/http_method.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
from typing import Callable, List, Union, TypeVar
from enum import Enum
from typing import Any, Callable, List, Union
import sys

if sys.version_info >= (3, 10):
from typing import ParamSpec, TypeAlias
else:
from typing_extensions import ParamSpec, TypeAlias


P = ParamSpec("P")
R = TypeVar("R")
Func: TypeAlias = Callable[[P], R]

class HTTPMethod(Enum):
GET = "GET"
Expand All @@ -12,20 +22,22 @@ class HTTPMethod(Enum):
OPTIONS = "OPTIONS"


def route(http_method: HTTPMethod, route_path: Union[str, List[str]] = "/", **kwargs):
def route(http_method: HTTPMethod, route_path: Union[str, List[str]] = "/", **kwargs) -> Callable[[Func], Func]:
"""
Decorator that defines a route for the controller.

Args:
http_method (HTTPMethod): The HTTP method for the route (GET, POST, DELETE, PUT, PATCH).
route_path (Union[str, List[str]]): The route path for the route. example: "/users"
route_path (Union[str, List[str]]): The route path for the route.
**kwargs: Additional keyword arguments to configure the route.

Returns:
function: The decorated function.
function: The decorated function, preserving the signature.
"""

def decorator(func):
def decorator(func: Func) -> Func:

# Add custom route metadata
func.__http_method__ = http_method
func.__route_path__ = route_path
func.__kwargs__ = kwargs
Expand All @@ -35,29 +47,29 @@ def decorator(func):
return decorator


def Get(route_path: Union[str, List[str]] = "/", **kwargs) -> Callable[..., Any]:
def Get(route_path: Union[str, List[str]] = "/", **kwargs):
return route(HTTPMethod.GET, route_path, **kwargs)


def Post(route_path: Union[str, List[str]] = "/", **kwargs) -> Callable[..., Any]:
def Post(route_path: Union[str, List[str]] = "/", **kwargs):
return route(HTTPMethod.POST, route_path, **kwargs)


def Delete(route_path: Union[str, List[str]] = "/", **kwargs) -> Callable[..., Any]:
def Delete(route_path: Union[str, List[str]] = "/", **kwargs):
return route(HTTPMethod.DELETE, route_path, **kwargs)


def Put(route_path: Union[str, List[str]] = "/", **kwargs) -> Callable[..., Any]:
def Put(route_path: Union[str, List[str]] = "/", **kwargs):
return route(HTTPMethod.PUT, route_path, **kwargs)


def Patch(route_path: Union[str, List[str]] = "/", **kwargs) -> Callable[..., Any]:
def Patch(route_path: Union[str, List[str]] = "/", **kwargs):
return route(HTTPMethod.PATCH, route_path, **kwargs)


def Head(route_path: Union[str, List[str]] = "/", **kwargs) -> Callable[..., Any]:
def Head(route_path: Union[str, List[str]] = "/", **kwargs):
return route(HTTPMethod.HEAD, route_path, **kwargs)


def Options(route_path: Union[str, List[str]] = "/", **kwargs) -> Callable[..., Any]:
def Options(route_path: Union[str, List[str]] = "/", **kwargs):
return route(HTTPMethod.OPTIONS, route_path, **kwargs)
8 changes: 5 additions & 3 deletions nest/core/decorators/injectable.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from typing import Callable, Optional, Type
from typing import Callable, Optional, Type, TypeVar

from injector import inject

from nest.common.constants import DEPENDENCIES, INJECTABLE_NAME, INJECTABLE_TOKEN
from nest.core.decorators.utils import parse_dependencies

C = TypeVar('C', bound=object)

def Injectable(target_class: Optional[Type] = None, *args, **kwargs) -> Callable:

def Injectable(target_class: Optional[Type[C]] = None, *args, **kwargs) -> Callable:
"""
Decorator to mark a class as injectable and handle its dependencies.

Expand All @@ -17,7 +19,7 @@ def Injectable(target_class: Optional[Type] = None, *args, **kwargs) -> Callable
Callable: The decorator function.
"""

def decorator(decorated_class: Type) -> Type:
def decorator(decorated_class: Type[C]) -> Type[C]:
"""
Inner decorator function to process the class.

Expand Down
4 changes: 3 additions & 1 deletion nest/core/decorators/module.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from typing import Type, TypeVar
from nest.common.constants import ModuleMetadata

C = TypeVar('C', bound=object)

class Module:
def __init__(
Expand All @@ -16,7 +18,7 @@ def __init__(
self.exports = exports
self.is_global = is_global

def __call__(self, cls):
def __call__(self, cls: Type[C]) -> Type[C]:
setattr(cls, ModuleMetadata.CONTROLLERS, self.controllers)
setattr(cls, ModuleMetadata.PROVIDERS, self.providers)
setattr(cls, ModuleMetadata.IMPORTS, self.imports)
Expand Down
Loading