spakky-fastapi 1.7.0__tar.gz → 2.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {spakky_fastapi-1.7.0 → spakky_fastapi-2.0.1}/PKG-INFO +2 -2
  2. {spakky_fastapi-1.7.0 → spakky_fastapi-2.0.1}/pyproject.toml +11 -44
  3. spakky_fastapi-2.0.1/spakky_fastapi/error.py +54 -0
  4. spakky_fastapi-2.0.1/spakky_fastapi/main.py +11 -0
  5. {spakky_fastapi-1.7.0 → spakky_fastapi-2.0.1}/spakky_fastapi/middlewares/error_handling.py +6 -12
  6. spakky_fastapi-2.0.1/spakky_fastapi/middlewares/manage_context.py +26 -0
  7. spakky_fastapi-2.0.1/spakky_fastapi/post_processors/add_builtin_middlewares.py +34 -0
  8. spakky_fastapi-2.0.1/spakky_fastapi/post_processors/register_routes.py +104 -0
  9. spakky_fastapi-2.0.1/spakky_fastapi/routes/__init__.py +21 -0
  10. spakky_fastapi-2.0.1/spakky_fastapi/routes/delete.py +62 -0
  11. spakky_fastapi-2.0.1/spakky_fastapi/routes/get.py +62 -0
  12. spakky_fastapi-2.0.1/spakky_fastapi/routes/head.py +62 -0
  13. spakky_fastapi-2.0.1/spakky_fastapi/routes/options.py +62 -0
  14. spakky_fastapi-2.0.1/spakky_fastapi/routes/patch.py +62 -0
  15. spakky_fastapi-2.0.1/spakky_fastapi/routes/post.py +62 -0
  16. spakky_fastapi-2.0.1/spakky_fastapi/routes/put.py +62 -0
  17. spakky_fastapi-2.0.1/spakky_fastapi/routes/route.py +112 -0
  18. spakky_fastapi-2.0.1/spakky_fastapi/routes/websocket.py +28 -0
  19. spakky_fastapi-2.0.1/spakky_fastapi/stereotypes/api_controller.py +10 -0
  20. spakky_fastapi-1.7.0/spakky_fastapi/aspects/authenticate.py +0 -107
  21. spakky_fastapi-1.7.0/spakky_fastapi/error.py +0 -65
  22. spakky_fastapi-1.7.0/spakky_fastapi/plugins/authenticate.py +0 -13
  23. spakky_fastapi-1.7.0/spakky_fastapi/plugins/fast_api.py +0 -24
  24. spakky_fastapi-1.7.0/spakky_fastapi/post_processor.py +0 -69
  25. spakky_fastapi-1.7.0/spakky_fastapi/stereotypes/__init__.py +0 -0
  26. spakky_fastapi-1.7.0/spakky_fastapi/stereotypes/api_controller.py +0 -508
  27. {spakky_fastapi-1.7.0 → spakky_fastapi-2.0.1}/README.md +0 -0
  28. {spakky_fastapi-1.7.0 → spakky_fastapi-2.0.1}/spakky_fastapi/__init__.py +0 -0
  29. {spakky_fastapi-1.7.0/spakky_fastapi/aspects → spakky_fastapi-2.0.1/spakky_fastapi/middlewares}/__init__.py +0 -0
  30. {spakky_fastapi-1.7.0/spakky_fastapi/middlewares → spakky_fastapi-2.0.1/spakky_fastapi/post_processors}/__init__.py +0 -0
  31. {spakky_fastapi-1.7.0 → spakky_fastapi-2.0.1}/spakky_fastapi/py.typed +0 -0
  32. {spakky_fastapi-1.7.0/spakky_fastapi/plugins → spakky_fastapi-2.0.1/spakky_fastapi/stereotypes}/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spakky-fastapi
3
- Version: 1.7.0
3
+ Version: 2.0.1
4
4
  Summary: Highly abstracted Framework core to use DDD & DI/IoC & AOP & Etc...
5
5
  Author: Spakky
6
6
  Author-email: sejong418@icloud.com
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
12
  Requires-Dist: fastapi (>=0.115.5,<0.116.0)
13
13
  Requires-Dist: orjson (>=3.10.11,<4.0.0)
14
- Requires-Dist: spakky-core (>=1.8,<2.0)
14
+ Requires-Dist: spakky-core (>=2.10)
15
15
  Requires-Dist: websockets (>=14.1,<15.0)
16
16
  Description-Content-Type: text/markdown
17
17
 
@@ -1,10 +1,13 @@
1
1
  [tool.poetry]
2
2
  name = "spakky-fastapi"
3
- version = "1.7.0"
3
+ version = "2.0.1"
4
4
  description = "Highly abstracted Framework core to use DDD & DI/IoC & AOP & Etc..."
5
5
  authors = ["Spakky <sejong418@icloud.com>"]
6
6
  readme = "README.md"
7
7
 
8
+ [tool.poetry.plugins."spakky.plugins"]
9
+ fastapi = "spakky_fastapi.main:initialize"
10
+
8
11
  [build-system]
9
12
  requires = ["poetry-core"]
10
13
  build-backend = "poetry.core.masonry.api"
@@ -12,67 +15,31 @@ build-backend = "poetry.core.masonry.api"
12
15
  [tool.poetry.dependencies]
13
16
  python = ">=3.10"
14
17
  fastapi = "^0.115.5"
15
- spakky-core = "^1.8"
18
+ spakky-core = ">=2.10"
16
19
  websockets = "^14.1"
17
20
  orjson = "^3.10.11"
18
21
 
19
22
  [tool.poetry.group.dev.dependencies]
20
- pylint = "^3.0.2"
21
- black = "^24.4.2"
22
- isort = "^5.12.0"
23
23
  pytest = "^7.4.3"
24
24
  pytest-cov = "^4.1.0"
25
25
  pytest-asyncio = "^0.21.1"
26
26
  pytest-xdist = "^3.6.1"
27
27
  toml = "^0.10.2"
28
28
  httpx = "^0.27.2"
29
+ ruff = "^0.9.7"
30
+ pre-commit = "^4.1.0"
29
31
 
30
32
  [tool.poetry.group.ci.dependencies]
31
- pylint = "^3.0.2"
32
33
  pytest = "^7.4.3"
33
34
  pytest-cov = "^4.1.0"
34
35
  pytest-asyncio = "^0.21.1"
35
36
  pytest-xdist = "^3.6.1"
36
37
  httpx = "^0.27.2"
38
+ ruff = "^0.9.7"
37
39
 
38
- [tool.black]
39
- line-length = 90
40
- target-version = ["py310"]
41
- include = '\.pyi?$'
42
-
43
- [tool.isort]
44
- py_version = 310
45
- src_paths = ["spakky_fastapi", "tests"]
46
- skip = [".venv", ".gitignore", ".dockerignore"]
47
- extend_skip = [".md", ".json", ".txt"]
48
- profile = "black"
49
- combine_as_imports = true
50
- remove_redundant_aliases = true
51
- length_sort_sections = ["future", "stdlib"]
52
-
53
- [tool.pylint]
54
- ignore-paths = [".venv", "tests"]
55
-
56
- [tool.pylint.'MESSAGES CONTROL']
57
- max-line-length = 90
58
- disable = """
59
- no-member,
60
- arguments-differ,
61
- invalid-field-call,
62
- too-many-arguments,
63
- missing-module-docstring,
64
- missing-class-docstring,
65
- missing-function-docstring,
66
- too-many-positional-arguments,
67
- too-many-instance-attributes,
68
- too-few-public-methods,
69
- unnecessary-ellipsis,
70
- redefined-builtin,
71
- too-many-locals,
72
- multiple-statements,
73
- consider-using-f-string,
74
- duplicate-code,
75
- """
40
+ [tool.ruff]
41
+ builtins = ["_"]
42
+ cache-dir = "~/.cache/ruff"
76
43
 
77
44
  [tool.pytest.ini_options]
78
45
  pythonpath = "spakky"
@@ -0,0 +1,54 @@
1
+ import traceback
2
+ from abc import ABC
3
+ from typing import ClassVar
4
+
5
+ from fastapi import status
6
+ from fastapi.responses import JSONResponse, ORJSONResponse
7
+ from spakky.core.error import AbstractSpakkyCoreError
8
+
9
+
10
+ class AbstractSpakkyFastAPIError(AbstractSpakkyCoreError, ABC):
11
+ status_code: ClassVar[int]
12
+
13
+ def __init__(self, *args: object) -> None:
14
+ super().__init__(*args)
15
+
16
+ def to_response(self, show_traceback: bool) -> JSONResponse:
17
+ return ORJSONResponse(
18
+ content={
19
+ "message": self.message,
20
+ "args": [str(x) for x in self.args],
21
+ "traceback": traceback.format_exc() if show_traceback else None,
22
+ },
23
+ status_code=self.status_code,
24
+ )
25
+
26
+
27
+ class BadRequest(AbstractSpakkyFastAPIError):
28
+ message: ClassVar[str] = "Bad Request"
29
+ status_code: ClassVar[int] = status.HTTP_400_BAD_REQUEST
30
+
31
+
32
+ class Unauthorized(AbstractSpakkyFastAPIError):
33
+ message: ClassVar[str] = "Unauthorized"
34
+ status_code: ClassVar[int] = status.HTTP_401_UNAUTHORIZED
35
+
36
+
37
+ class Forbidden(AbstractSpakkyFastAPIError):
38
+ message: ClassVar[str] = "Forbidden"
39
+ status_code: ClassVar[int] = status.HTTP_403_FORBIDDEN
40
+
41
+
42
+ class NotFound(AbstractSpakkyFastAPIError):
43
+ message: ClassVar[str] = "Not Found"
44
+ status_code: ClassVar[int] = status.HTTP_404_NOT_FOUND
45
+
46
+
47
+ class Conflict(AbstractSpakkyFastAPIError):
48
+ message: ClassVar[str] = "Conflict"
49
+ status_code: ClassVar[int] = status.HTTP_409_CONFLICT
50
+
51
+
52
+ class InternalServerError(AbstractSpakkyFastAPIError):
53
+ message: ClassVar[str] = "Internal Server Error"
54
+ status_code: ClassVar[int] = status.HTTP_500_INTERNAL_SERVER_ERROR
@@ -0,0 +1,11 @@
1
+ from spakky.application.application import SpakkyApplication
2
+
3
+ from spakky_fastapi.post_processors.add_builtin_middlewares import (
4
+ AddBuiltInMiddlewaresPostProcessor,
5
+ )
6
+ from spakky_fastapi.post_processors.register_routes import RegisterRoutesPostProcessor
7
+
8
+
9
+ def initialize(app: SpakkyApplication) -> None:
10
+ app.add(AddBuiltInMiddlewaresPostProcessor)
11
+ app.add(RegisterRoutesPostProcessor)
@@ -1,12 +1,11 @@
1
- import traceback
2
- from typing import Callable, Awaitable, TypeAlias
1
+ from typing import Awaitable, Callable, TypeAlias
3
2
 
4
3
  from fastapi import Request
5
4
  from starlette.middleware.base import BaseHTTPMiddleware, DispatchFunction
6
5
  from starlette.responses import Response
7
6
  from starlette.types import ASGIApp
8
7
 
9
- from spakky_fastapi.error import InternalServerError, SpakkyFastAPIError
8
+ from spakky_fastapi.error import AbstractSpakkyFastAPIError, InternalServerError
10
9
 
11
10
  Next: TypeAlias = Callable[[Request], Awaitable[Response]]
12
11
 
@@ -26,12 +25,7 @@ class ErrorHandlingMiddleware(BaseHTTPMiddleware):
26
25
  async def dispatch(self, request: Request, call_next: Next) -> Response:
27
26
  try:
28
27
  return await call_next(request)
29
- except SpakkyFastAPIError as e:
30
- return e.to_response()
31
- except Exception as e: # pylint: disable=broad-exception-caught
32
- if self.__debug:
33
- traceback.print_exc() # pragma: no cover
34
- return InternalServerError(
35
- error=e,
36
- stacktrace=traceback.format_exc() if self.__debug else None,
37
- ).to_response()
28
+ except AbstractSpakkyFastAPIError as e:
29
+ return e.to_response(self.__debug)
30
+ except Exception:
31
+ return InternalServerError().to_response(self.__debug)
@@ -0,0 +1,26 @@
1
+ from typing import Awaitable, Callable, TypeAlias
2
+
3
+ from fastapi import Request
4
+ from spakky.pod.interfaces.application_context import IApplicationContext
5
+ from starlette.middleware.base import BaseHTTPMiddleware, DispatchFunction
6
+ from starlette.responses import Response
7
+ from starlette.types import ASGIApp
8
+
9
+ Next: TypeAlias = Callable[[Request], Awaitable[Response]]
10
+
11
+
12
+ class ManageContextMiddleware(BaseHTTPMiddleware):
13
+ __application_context: IApplicationContext
14
+
15
+ def __init__(
16
+ self,
17
+ app: ASGIApp,
18
+ application_context: IApplicationContext,
19
+ dispatch: DispatchFunction | None = None,
20
+ ) -> None:
21
+ super().__init__(app, dispatch)
22
+ self.__application_context = application_context
23
+
24
+ async def dispatch(self, request: Request, call_next: Next) -> Response:
25
+ self.__application_context.clear_context()
26
+ return await call_next(request)
@@ -0,0 +1,34 @@
1
+ from fastapi import FastAPI
2
+ from spakky.pod.annotations.order import Order
3
+ from spakky.pod.annotations.pod import Pod
4
+ from spakky.pod.interfaces.application_context import IApplicationContext
5
+ from spakky.pod.interfaces.aware.application_context_aware import (
6
+ IApplicationContextAware,
7
+ )
8
+ from spakky.pod.interfaces.post_processor import IPostProcessor
9
+
10
+ from spakky_fastapi.middlewares.error_handling import ErrorHandlingMiddleware
11
+ from spakky_fastapi.middlewares.manage_context import ManageContextMiddleware
12
+
13
+
14
+ @Order(0)
15
+ @Pod()
16
+ class AddBuiltInMiddlewaresPostProcessor(IPostProcessor, IApplicationContextAware):
17
+ __application_context: IApplicationContext
18
+
19
+ def set_application_context(self, application_context: IApplicationContext) -> None:
20
+ self.__application_context = application_context
21
+
22
+ def post_process(self, pod: object) -> object:
23
+ if not isinstance(pod, FastAPI):
24
+ return pod
25
+
26
+ pod.add_middleware(
27
+ ErrorHandlingMiddleware,
28
+ debug=pod.debug,
29
+ )
30
+ pod.add_middleware(
31
+ ManageContextMiddleware,
32
+ application_context=self.__application_context,
33
+ )
34
+ return pod
@@ -0,0 +1,104 @@
1
+ from dataclasses import asdict
2
+ from functools import wraps
3
+ from inspect import getmembers, signature
4
+ from logging import Logger
5
+ from typing import Any
6
+
7
+ from fastapi import APIRouter, FastAPI
8
+ from fastapi.exceptions import FastAPIError
9
+ from fastapi.utils import create_model_field # type: ignore
10
+ from spakky.pod.annotations.order import Order
11
+ from spakky.pod.annotations.pod import Pod
12
+ from spakky.pod.interfaces.aware.container_aware import IContainerAware
13
+ from spakky.pod.interfaces.aware.logger_aware import ILoggerAware
14
+ from spakky.pod.interfaces.container import IContainer
15
+ from spakky.pod.interfaces.post_processor import IPostProcessor
16
+
17
+ from spakky_fastapi.routes.route import Route
18
+ from spakky_fastapi.routes.websocket import WebSocketRoute
19
+ from spakky_fastapi.stereotypes.api_controller import ApiController
20
+
21
+
22
+ @Order(0)
23
+ @Pod()
24
+ class RegisterRoutesPostProcessor(IPostProcessor, ILoggerAware, IContainerAware):
25
+ __logger: Logger
26
+ __container: IContainer
27
+
28
+ def set_logger(self, logger: Logger) -> None:
29
+ self.__logger = logger
30
+
31
+ def set_container(self, container: IContainer) -> None:
32
+ self.__container = container
33
+
34
+ def post_process(self, pod: object) -> object:
35
+ if not ApiController.exists(pod):
36
+ return pod
37
+
38
+ fast_api = self.__container.get(FastAPI)
39
+ controller = ApiController.get(pod)
40
+ router: APIRouter = APIRouter(prefix=controller.prefix, tags=controller.tags)
41
+ for name, method in getmembers(pod, callable):
42
+ route: Route | None = Route.get_or_none(method)
43
+ websocket_route: WebSocketRoute | None = WebSocketRoute.get_or_none(method)
44
+ if route is None and websocket_route is None:
45
+ continue
46
+
47
+ if route is not None:
48
+ # pylint: disable=line-too-long
49
+ self.__logger.debug(
50
+ f"[{type(self).__name__}] {route.methods!r} {controller.prefix}{route.path} -> {method.__qualname__}"
51
+ )
52
+ if route.name is None:
53
+ route.name = " ".join([x.capitalize() for x in name.split("_")])
54
+ if route.description is None:
55
+ route.description = method.__doc__
56
+ if route.response_model is None:
57
+ return_annotation: type | None = signature(method).return_annotation
58
+ if return_annotation is not None:
59
+ try:
60
+ create_model_field("", return_annotation)
61
+ route.response_model = return_annotation
62
+ except FastAPIError:
63
+ pass
64
+
65
+ @wraps(method)
66
+ async def endpoint(
67
+ *args: Any,
68
+ method_name: str = name,
69
+ controller_type: type[object] = controller.type_,
70
+ context: IContainer = self.__container,
71
+ **kwargs: Any,
72
+ ) -> Any:
73
+ controller_instance = context.get(controller_type)
74
+ method_to_call = getattr(controller_instance, method_name)
75
+ return await method_to_call(*args, **kwargs)
76
+
77
+ router.add_api_route(endpoint=endpoint, **asdict(route))
78
+ if websocket_route is not None:
79
+ # pylint: disable=line-too-long
80
+ self.__logger.debug(
81
+ f"[{type(self).__name__}] [WebSocket] {controller.prefix}{websocket_route.path} -> {method.__qualname__}"
82
+ )
83
+ if websocket_route.name is None:
84
+ websocket_route.name = " ".join(
85
+ [x.capitalize() for x in name.split("_")]
86
+ )
87
+
88
+ @wraps(method)
89
+ async def websocket_endpoint(
90
+ *args: Any,
91
+ method_name: str = name,
92
+ controller_type: type[object] = controller.type_,
93
+ context: IContainer = self.__container,
94
+ **kwargs: Any,
95
+ ) -> Any:
96
+ controller_instance = context.get(controller_type)
97
+ method_to_call = getattr(controller_instance, method_name)
98
+ return await method_to_call(*args, **kwargs)
99
+
100
+ router.add_api_websocket_route(
101
+ endpoint=websocket_endpoint, **asdict(websocket_route)
102
+ )
103
+ fast_api.include_router(router)
104
+ return pod
@@ -0,0 +1,21 @@
1
+ from .delete import delete
2
+ from .get import get
3
+ from .head import head
4
+ from .options import options
5
+ from .patch import patch
6
+ from .post import post
7
+ from .put import put
8
+ from .route import route
9
+ from .websocket import websocket
10
+
11
+ __all__ = [
12
+ "delete",
13
+ "get",
14
+ "head",
15
+ "options",
16
+ "patch",
17
+ "post",
18
+ "put",
19
+ "route",
20
+ "websocket",
21
+ ]
@@ -0,0 +1,62 @@
1
+ from typing import Any, Callable, Sequence
2
+
3
+ from fastapi import Response, params
4
+ from fastapi.responses import JSONResponse
5
+ from fastapi.routing import APIRoute
6
+ from spakky.core.types import FuncT
7
+ from starlette.routing import Route as StarletteRoute
8
+
9
+ from spakky_fastapi.routes.route import DictIntStrAny, HTTPMethod, SetIntStr, route
10
+
11
+
12
+ def delete(
13
+ path: str,
14
+ response_model: type[Any] | None = None,
15
+ status_code: int | None = None,
16
+ tags: list[str] | None = None,
17
+ dependencies: Sequence[params.Depends] | None = None,
18
+ summary: str | None = None,
19
+ description: str | None = None,
20
+ response_description: str = "Successful Response",
21
+ responses: dict[int | str, dict[str, Any]] | None = None,
22
+ deprecated: bool | None = None,
23
+ operation_id: str | None = None,
24
+ response_model_include: SetIntStr | DictIntStrAny | None = None,
25
+ response_model_exclude: SetIntStr | DictIntStrAny | None = None,
26
+ response_model_by_alias: bool = True,
27
+ response_model_exclude_unset: bool = False,
28
+ response_model_exclude_defaults: bool = False,
29
+ response_model_exclude_none: bool = False,
30
+ include_in_schema: bool = True,
31
+ response_class: type[Response] = JSONResponse,
32
+ name: str | None = None,
33
+ route_class_override: type[APIRoute] | None = None,
34
+ callbacks: list[StarletteRoute] | None = None,
35
+ openapi_extra: dict[str, Any] | None = None,
36
+ ) -> Callable[[FuncT], FuncT]:
37
+ return route(
38
+ path=path,
39
+ methods=[HTTPMethod.DELETE],
40
+ response_model=response_model,
41
+ status_code=status_code,
42
+ tags=tags,
43
+ dependencies=dependencies,
44
+ summary=summary,
45
+ description=description,
46
+ response_description=response_description,
47
+ responses=responses,
48
+ deprecated=deprecated,
49
+ operation_id=operation_id,
50
+ response_model_include=response_model_include,
51
+ response_model_exclude=response_model_exclude,
52
+ response_model_by_alias=response_model_by_alias,
53
+ response_model_exclude_unset=response_model_exclude_unset,
54
+ response_model_exclude_defaults=response_model_exclude_defaults,
55
+ response_model_exclude_none=response_model_exclude_none,
56
+ include_in_schema=include_in_schema,
57
+ response_class=response_class,
58
+ name=name,
59
+ route_class_override=route_class_override,
60
+ callbacks=callbacks,
61
+ openapi_extra=openapi_extra,
62
+ )
@@ -0,0 +1,62 @@
1
+ from typing import Any, Callable, Sequence
2
+
3
+ from fastapi import Response, params
4
+ from fastapi.responses import JSONResponse
5
+ from fastapi.routing import APIRoute
6
+ from spakky.core.types import FuncT
7
+ from starlette.routing import Route as StarletteRoute
8
+
9
+ from spakky_fastapi.routes.route import DictIntStrAny, HTTPMethod, SetIntStr, route
10
+
11
+
12
+ def get(
13
+ path: str,
14
+ response_model: type[Any] | None = None,
15
+ status_code: int | None = None,
16
+ tags: list[str] | None = None,
17
+ dependencies: Sequence[params.Depends] | None = None,
18
+ summary: str | None = None,
19
+ description: str | None = None,
20
+ response_description: str = "Successful Response",
21
+ responses: dict[int | str, dict[str, Any]] | None = None,
22
+ deprecated: bool | None = None,
23
+ operation_id: str | None = None,
24
+ response_model_include: SetIntStr | DictIntStrAny | None = None,
25
+ response_model_exclude: SetIntStr | DictIntStrAny | None = None,
26
+ response_model_by_alias: bool = True,
27
+ response_model_exclude_unset: bool = False,
28
+ response_model_exclude_defaults: bool = False,
29
+ response_model_exclude_none: bool = False,
30
+ include_in_schema: bool = True,
31
+ response_class: type[Response] = JSONResponse,
32
+ name: str | None = None,
33
+ route_class_override: type[APIRoute] | None = None,
34
+ callbacks: list[StarletteRoute] | None = None,
35
+ openapi_extra: dict[str, Any] | None = None,
36
+ ) -> Callable[[FuncT], FuncT]:
37
+ return route(
38
+ path=path,
39
+ methods=[HTTPMethod.GET],
40
+ response_model=response_model,
41
+ status_code=status_code,
42
+ tags=tags,
43
+ dependencies=dependencies,
44
+ summary=summary,
45
+ description=description,
46
+ response_description=response_description,
47
+ responses=responses,
48
+ deprecated=deprecated,
49
+ operation_id=operation_id,
50
+ response_model_include=response_model_include,
51
+ response_model_exclude=response_model_exclude,
52
+ response_model_by_alias=response_model_by_alias,
53
+ response_model_exclude_unset=response_model_exclude_unset,
54
+ response_model_exclude_defaults=response_model_exclude_defaults,
55
+ response_model_exclude_none=response_model_exclude_none,
56
+ include_in_schema=include_in_schema,
57
+ response_class=response_class,
58
+ name=name,
59
+ route_class_override=route_class_override,
60
+ callbacks=callbacks,
61
+ openapi_extra=openapi_extra,
62
+ )
@@ -0,0 +1,62 @@
1
+ from typing import Any, Callable, Sequence
2
+
3
+ from fastapi import Response, params
4
+ from fastapi.responses import JSONResponse
5
+ from fastapi.routing import APIRoute
6
+ from spakky.core.types import FuncT
7
+ from starlette.routing import Route as StarletteRoute
8
+
9
+ from spakky_fastapi.routes.route import DictIntStrAny, HTTPMethod, SetIntStr, route
10
+
11
+
12
+ def head(
13
+ path: str,
14
+ response_model: type[Any] | None = None,
15
+ status_code: int | None = None,
16
+ tags: list[str] | None = None,
17
+ dependencies: Sequence[params.Depends] | None = None,
18
+ summary: str | None = None,
19
+ description: str | None = None,
20
+ response_description: str = "Successful Response",
21
+ responses: dict[int | str, dict[str, Any]] | None = None,
22
+ deprecated: bool | None = None,
23
+ operation_id: str | None = None,
24
+ response_model_include: SetIntStr | DictIntStrAny | None = None,
25
+ response_model_exclude: SetIntStr | DictIntStrAny | None = None,
26
+ response_model_by_alias: bool = True,
27
+ response_model_exclude_unset: bool = False,
28
+ response_model_exclude_defaults: bool = False,
29
+ response_model_exclude_none: bool = False,
30
+ include_in_schema: bool = True,
31
+ response_class: type[Response] = JSONResponse,
32
+ name: str | None = None,
33
+ route_class_override: type[APIRoute] | None = None,
34
+ callbacks: list[StarletteRoute] | None = None,
35
+ openapi_extra: dict[str, Any] | None = None,
36
+ ) -> Callable[[FuncT], FuncT]:
37
+ return route(
38
+ path=path,
39
+ methods=[HTTPMethod.HEAD],
40
+ response_model=response_model,
41
+ status_code=status_code,
42
+ tags=tags,
43
+ dependencies=dependencies,
44
+ summary=summary,
45
+ description=description,
46
+ response_description=response_description,
47
+ responses=responses,
48
+ deprecated=deprecated,
49
+ operation_id=operation_id,
50
+ response_model_include=response_model_include,
51
+ response_model_exclude=response_model_exclude,
52
+ response_model_by_alias=response_model_by_alias,
53
+ response_model_exclude_unset=response_model_exclude_unset,
54
+ response_model_exclude_defaults=response_model_exclude_defaults,
55
+ response_model_exclude_none=response_model_exclude_none,
56
+ include_in_schema=include_in_schema,
57
+ response_class=response_class,
58
+ name=name,
59
+ route_class_override=route_class_override,
60
+ callbacks=callbacks,
61
+ openapi_extra=openapi_extra,
62
+ )
@@ -0,0 +1,62 @@
1
+ from typing import Any, Callable, Sequence
2
+
3
+ from fastapi import Response, params
4
+ from fastapi.responses import JSONResponse
5
+ from fastapi.routing import APIRoute
6
+ from spakky.core.types import FuncT
7
+ from starlette.routing import Route as StarletteRoute
8
+
9
+ from spakky_fastapi.routes.route import DictIntStrAny, HTTPMethod, SetIntStr, route
10
+
11
+
12
+ def options(
13
+ path: str,
14
+ response_model: type[Any] | None = None,
15
+ status_code: int | None = None,
16
+ tags: list[str] | None = None,
17
+ dependencies: Sequence[params.Depends] | None = None,
18
+ summary: str | None = None,
19
+ description: str | None = None,
20
+ response_description: str = "Successful Response",
21
+ responses: dict[int | str, dict[str, Any]] | None = None,
22
+ deprecated: bool | None = None,
23
+ operation_id: str | None = None,
24
+ response_model_include: SetIntStr | DictIntStrAny | None = None,
25
+ response_model_exclude: SetIntStr | DictIntStrAny | None = None,
26
+ response_model_by_alias: bool = True,
27
+ response_model_exclude_unset: bool = False,
28
+ response_model_exclude_defaults: bool = False,
29
+ response_model_exclude_none: bool = False,
30
+ include_in_schema: bool = True,
31
+ response_class: type[Response] = JSONResponse,
32
+ name: str | None = None,
33
+ route_class_override: type[APIRoute] | None = None,
34
+ callbacks: list[StarletteRoute] | None = None,
35
+ openapi_extra: dict[str, Any] | None = None,
36
+ ) -> Callable[[FuncT], FuncT]:
37
+ return route(
38
+ path=path,
39
+ methods=[HTTPMethod.OPTIONS],
40
+ response_model=response_model,
41
+ status_code=status_code,
42
+ tags=tags,
43
+ dependencies=dependencies,
44
+ summary=summary,
45
+ description=description,
46
+ response_description=response_description,
47
+ responses=responses,
48
+ deprecated=deprecated,
49
+ operation_id=operation_id,
50
+ response_model_include=response_model_include,
51
+ response_model_exclude=response_model_exclude,
52
+ response_model_by_alias=response_model_by_alias,
53
+ response_model_exclude_unset=response_model_exclude_unset,
54
+ response_model_exclude_defaults=response_model_exclude_defaults,
55
+ response_model_exclude_none=response_model_exclude_none,
56
+ include_in_schema=include_in_schema,
57
+ response_class=response_class,
58
+ name=name,
59
+ route_class_override=route_class_override,
60
+ callbacks=callbacks,
61
+ openapi_extra=openapi_extra,
62
+ )