Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
7 changes: 4 additions & 3 deletions nest/common/route_resolver.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from fastapi import APIRouter, FastAPI
from nest.engine.proto import App, Router


class RoutesResolver:
def __init__(self, container, app_ref: FastAPI):
def __init__(self, container, app_ref: App):
self.container = container
self.app_ref = app_ref

Expand All @@ -12,5 +12,6 @@ def register_routes(self):
self.register_route(controller)

def register_route(self, controller):
router: APIRouter = controller.get_router()
router: Router = controller.get_router()
self.app_ref.include_router(router)

17 changes: 9 additions & 8 deletions nest/core/decorators/class_based_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,34 @@
List,
Type,
TypeVar,
Union,
get_origin,
get_type_hints,
)

from fastapi import APIRouter, Depends
from starlette.routing import Route, WebSocketRoute

from nest.engine.proto import Router, RouteProtocol, Route
from fastapi import Depends # TODO: remove in future release


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

CBV_CLASS_KEY = "__cbv_class__"


def class_based_view(router: APIRouter, cls: Type[T]) -> Type[T]:
def class_based_view(router: Router, cls: Type[T]) -> Type[T]:
"""
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`.
"""
_init_cbv(cls)
cbv_router = APIRouter()
cbv_router = router.__class__()
function_members = inspect.getmembers(cls, inspect.isfunction)
functions_set = set(func for _, func in function_members)
cbv_routes = [
route
for route in router.routes
if isinstance(route, (Route, WebSocketRoute))
if isinstance(route, RouteProtocol)
and route.endpoint in functions_set
]
for route in cbv_routes:
Expand Down Expand Up @@ -95,7 +96,7 @@ def new_init(self: Any, *args: Any, **kwargs: Any) -> None:


def _update_cbv_route_endpoint_signature(
cls: Type[Any], route: Union[Route, WebSocketRoute]
cls: Type[Any], route: Route
) -> None:
"""
Fixes the endpoint signature for a cbv route to ensure FastAPI performs dependency injection properly.
Expand All @@ -110,4 +111,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)
12 changes: 7 additions & 5 deletions nest/core/decorators/controller.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Optional, Type

from fastapi.routing import APIRouter

from nest.engine.proto import Router
from nest.engine.fastapi_engine import FastAPIRouter
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
Expand All @@ -24,7 +24,9 @@ def Controller(prefix: Optional[str] = None, tag: Optional[str] = None):
route_prefix = process_prefix(prefix, tag)

def wrapper(cls: Type) -> Type[ClassBasedView]:
router = APIRouter(tags=[tag] if tag else None)
tags = [tag] if tag else None
# TODO: replace with factory
router: Router = FastAPIRouter(tags=tags)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to think of a way to remove router intilization.
Could be done by creating abs for controller and make instace for fastapi/others.

instead of

from nest.core.decrator import Controller
from nest.decrator.fastapi import Controller


# Process class dependencies
process_dependencies(cls)
Expand Down Expand Up @@ -86,7 +88,7 @@ def ensure_init_method(cls: Type) -> None:
pass


def add_routes(cls: Type, router: APIRouter, route_prefix: str) -> None:
def add_routes(cls: Type, router: Router, route_prefix: str) -> None:
"""Add routes from class methods to the router."""
for method_name, method_function in cls.__dict__.items():
if callable(method_function) and hasattr(method_function, "__http_method__"):
Expand Down Expand Up @@ -127,7 +129,7 @@ def configure_method_route(method_function: callable, route_prefix: str) -> None
method_function.__route_path__ = method_function.__route_path__.rstrip("/")


def add_route_to_router(router: APIRouter, method_function: callable) -> None:
def add_route_to_router(router: Router, method_function: callable) -> None:
"""Add the configured route to the router."""
route_kwargs = {
"path": method_function.__route_path__,
Expand Down
14 changes: 6 additions & 8 deletions nest/core/pynest_application.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from typing import Any

from fastapi import FastAPI

from nest.common.route_resolver import RoutesResolver
from nest.core.pynest_app_context import PyNestApplicationContext
from nest.core.pynest_container import PyNestContainer
from nest.engine.proto import App


class PyNestApp(PyNestApplicationContext):
Expand All @@ -19,13 +17,13 @@ class PyNestApp(PyNestApplicationContext):
def is_listening(self) -> bool:
return self._is_listening

def __init__(self, container: PyNestContainer, http_server: FastAPI):
def __init__(self, container: PyNestContainer, http_server: App):
"""
Initialize the PyNestApp with the given container and HTTP server.

Args:
container (PyNestContainer): The PyNestContainer container instance.
http_server (FastAPI): The FastAPI server instance.
http_server (App): The App server instance.
"""
self.container = container
self.http_server = http_server
Expand All @@ -48,12 +46,12 @@ def use(self, middleware: type, **options: Any) -> "PyNestApp":
self.http_server.add_middleware(middleware, **options)
return self

def get_server(self) -> FastAPI:
def get_server(self) -> App:
"""
Get the FastAPI server instance.
Get the App server instance.

Returns:
FastAPI: The FastAPI server instance.
App: The App server instance.
"""
return self.http_server

Expand Down
9 changes: 5 additions & 4 deletions nest/core/pynest_factory.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from abc import ABC, abstractmethod
from typing import Type, TypeVar

from fastapi import FastAPI

from nest.core.pynest_application import PyNestApp
from nest.core.pynest_container import PyNestContainer
from nest.engine.proto import App
from nest.engine.fastapi_engine import FastAPIApp

ModuleType = TypeVar("ModuleType")


# TODO: move default fastapi to here and add future implementation
class AbstractPyNestFactory(ABC):
@abstractmethod
def create(self, main_module: Type[ModuleType], **kwargs):
Expand Down Expand Up @@ -36,7 +37,7 @@ def create(main_module: Type[ModuleType], **kwargs) -> PyNestApp:
return PyNestApp(container, http_server)

@staticmethod
def _create_server(**kwargs) -> FastAPI:
def _create_server(app_cls: Type[App] = FastAPIApp, **kwargs) -> App:
"""
Create a FastAPI server.

Expand All @@ -46,4 +47,4 @@ def _create_server(**kwargs) -> FastAPI:
Returns:
FastAPI: The created FastAPI server.
"""
return FastAPI(**kwargs)
return app_cls(**kwargs)
2 changes: 2 additions & 0 deletions nest/engine/fastapi_engine/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from fastapi import APIRouter as FastAPIRouter
from fastapi import FastAPI as FastAPIApp
3 changes: 3 additions & 0 deletions nest/engine/proto/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .app import App
from .router import Router
from .route import Route, RouteProtocol
20 changes: 20 additions & 0 deletions nest/engine/proto/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import Protocol, TypeVar, List, Optional, Iterable

from nest.engine.proto.router import Router
from nest.engine.proto.route import Route
from nest.engine.types import Endpoint

Middleware = TypeVar('Middleware')


class AppProtocol(Protocol):

def __init__(self, **kwargs) -> None: ...
def add_middleware(self, middleware: Middleware, **options) -> None: ...
def include_router(self, router: Router, *, prefix: str = "", tags: Optional[List[str]] = None, **options) -> None: ...
def add_api_route(self, *, path: str, endpoint: Endpoint, methods: Optional[Iterable[str]]) -> None: ...
@property
def routes(self) -> List[Route]: ...


App = TypeVar('App', bound=AppProtocol)
12 changes: 12 additions & 0 deletions nest/engine/proto/route.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import Protocol, TypeVar, runtime_checkable

from nest.engine.types import Endpoint


@runtime_checkable
class RouteProtocol(Protocol):
endpoint: Endpoint
"""Function representing the route endpoint logic."""


Route = TypeVar('Route', bound=RouteProtocol)
14 changes: 14 additions & 0 deletions nest/engine/proto/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from typing import Protocol, List, Optional, TypeVar, Iterable, runtime_checkable
from .route import Route
from ..types import Endpoint


@runtime_checkable
class RouterProtocol(Protocol):
routes: List[Route]
def __init__(self, tags: Optional[List[str]] = None, **kwargs) -> None: ...
def include_router(self, router: 'RouterProtocol', **kwargs) -> None: ...
def add_api_route(self, *, path: str, endpoint: Endpoint, methods: Optional[Iterable[str]]) -> None: ...


Router = TypeVar('Router', bound=RouterProtocol)
3 changes: 3 additions & 0 deletions nest/engine/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from typing import Callable, TypeAlias, Any
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Failed check: CLI Test / test (MySQLSync)
I’ve attached the relevant part of the log for your convenience:
ImportError: cannot import name 'TypeAlias' from 'typing' module in Python 3.9


Finding type: Log Error

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this comment has been addressed. The commit 417821f has modified the import statement in nest/engine/types.py to resolve the ImportError. The TypeAlias import has been moved from 'typing' to 'typing_extensions', which should fix the issue with Python 3.9 compatibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed using import from typing_extensions instead of typing.
Maybe should check version and decide according to the version


Endpoint: TypeAlias = Callable[..., Any]
Loading