spakky-fastapi 1.6.2__tar.gz → 1.7.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spakky-fastapi
3
- Version: 1.6.2
3
+ Version: 1.7.0
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
@@ -9,10 +9,10 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.10
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
- Requires-Dist: fastapi (>=0.109.2,<0.110.0)
13
- Requires-Dist: orjson (>=3.9.15,<4.0.0)
14
- Requires-Dist: spakky-core (>=1.6,<2.0)
15
- Requires-Dist: websockets (>=12.0,<13.0)
12
+ Requires-Dist: fastapi (>=0.115.5,<0.116.0)
13
+ Requires-Dist: orjson (>=3.10.11,<4.0.0)
14
+ Requires-Dist: spakky-core (>=1.8,<2.0)
15
+ Requires-Dist: websockets (>=14.1,<15.0)
16
16
  Description-Content-Type: text/markdown
17
17
 
18
18
  <h1 align="center">Spakky FastAPI</h1>
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "spakky-fastapi"
3
- version = "1.6.2"
3
+ version = "1.7.0"
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"
@@ -11,26 +11,29 @@ build-backend = "poetry.core.masonry.api"
11
11
 
12
12
  [tool.poetry.dependencies]
13
13
  python = ">=3.10"
14
- fastapi = "^0.109.2"
15
- spakky-core = "^1.6"
16
- websockets = "^12.0"
17
- orjson = "^3.9.15"
14
+ fastapi = "^0.115.5"
15
+ spakky-core = "^1.8"
16
+ websockets = "^14.1"
17
+ orjson = "^3.10.11"
18
18
 
19
19
  [tool.poetry.group.dev.dependencies]
20
20
  pylint = "^3.0.2"
21
- black = "^23.11.0"
21
+ black = "^24.4.2"
22
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
- httpx = "^0.26.0"
27
-
26
+ pytest-xdist = "^3.6.1"
28
27
  toml = "^0.10.2"
28
+ httpx = "^0.27.2"
29
+
29
30
  [tool.poetry.group.ci.dependencies]
30
31
  pylint = "^3.0.2"
31
32
  pytest = "^7.4.3"
32
33
  pytest-cov = "^4.1.0"
33
34
  pytest-asyncio = "^0.21.1"
35
+ pytest-xdist = "^3.6.1"
36
+ httpx = "^0.27.2"
34
37
 
35
38
  [tool.black]
36
39
  line-length = 90
@@ -60,6 +63,7 @@ disable = """
60
63
  missing-module-docstring,
61
64
  missing-class-docstring,
62
65
  missing-function-docstring,
66
+ too-many-positional-arguments,
63
67
  too-many-instance-attributes,
64
68
  too-few-public-methods,
65
69
  unnecessary-ellipsis,
@@ -81,7 +85,9 @@ addopts = """
81
85
  --cov-report=xml
82
86
  --no-cov-on-fail
83
87
  --strict-markers
88
+ --dist=loadfile
84
89
  -p no:warnings
90
+ -n auto
85
91
  -vv
86
92
  """
87
93
 
@@ -0,0 +1,65 @@
1
+ from typing import ClassVar
2
+
3
+ from fastapi import status
4
+ from fastapi.responses import ORJSONResponse
5
+ from spakky.core.error import SpakkyCoreError
6
+
7
+
8
+ class SpakkyFastAPIError(SpakkyCoreError):
9
+ status_code: ClassVar[int]
10
+
11
+ def __init__(self, error: SpakkyCoreError) -> None:
12
+ self.message = error.message
13
+ self.args = error.args
14
+
15
+ def to_response(self) -> ORJSONResponse:
16
+ return ORJSONResponse(
17
+ content={
18
+ "message": self.message,
19
+ "args": [str(x) for x in self.args],
20
+ },
21
+ status_code=self.status_code,
22
+ )
23
+
24
+
25
+ class SpakkyUnknownError(SpakkyCoreError):
26
+ message = "알 수 없는 오류가 발생했습니다."
27
+
28
+
29
+ class BadRequest(SpakkyFastAPIError):
30
+ status_code: ClassVar[int] = status.HTTP_400_BAD_REQUEST
31
+
32
+
33
+ class Unauthorized(SpakkyFastAPIError):
34
+ status_code: ClassVar[int] = status.HTTP_401_UNAUTHORIZED
35
+
36
+
37
+ class Forbidden(SpakkyFastAPIError):
38
+ status_code: ClassVar[int] = status.HTTP_403_FORBIDDEN
39
+
40
+
41
+ class NotFound(SpakkyFastAPIError):
42
+ status_code: ClassVar[int] = status.HTTP_404_NOT_FOUND
43
+
44
+
45
+ class Conflict(SpakkyFastAPIError):
46
+ status_code: ClassVar[int] = status.HTTP_409_CONFLICT
47
+
48
+
49
+ class InternalServerError(SpakkyFastAPIError):
50
+ status_code: ClassVar[int] = status.HTTP_500_INTERNAL_SERVER_ERROR
51
+ stacktrace: str | None
52
+
53
+ def __init__(self, error: Exception, stacktrace: str | None) -> None:
54
+ super().__init__(SpakkyUnknownError(error.args))
55
+ self.stacktrace = stacktrace
56
+
57
+ def to_response(self) -> ORJSONResponse:
58
+ return ORJSONResponse(
59
+ content={
60
+ "message": self.message,
61
+ "args": [str(x) for x in self.args],
62
+ "stacktrace": self.stacktrace,
63
+ },
64
+ status_code=self.status_code,
65
+ )
@@ -2,8 +2,6 @@ import traceback
2
2
  from typing import Callable, Awaitable, TypeAlias
3
3
 
4
4
  from fastapi import Request
5
- from fastapi.responses import ORJSONResponse
6
- from pydantic import BaseModel
7
5
  from starlette.middleware.base import BaseHTTPMiddleware, DispatchFunction
8
6
  from starlette.responses import Response
9
7
  from starlette.types import ASGIApp
@@ -13,12 +11,6 @@ from spakky_fastapi.error import InternalServerError, SpakkyFastAPIError
13
11
  Next: TypeAlias = Callable[[Request], Awaitable[Response]]
14
12
 
15
13
 
16
- class ErrorResponse(BaseModel):
17
- message: str
18
- args: list[str]
19
- traceback: str = ""
20
-
21
-
22
14
  class ErrorHandlingMiddleware(BaseHTTPMiddleware):
23
15
  __debug: bool
24
16
 
@@ -35,23 +27,11 @@ class ErrorHandlingMiddleware(BaseHTTPMiddleware):
35
27
  try:
36
28
  return await call_next(request)
37
29
  except SpakkyFastAPIError as e:
38
- return ORJSONResponse(
39
- content=ErrorResponse(
40
- message=e.message,
41
- args=[str(x) for x in e.args],
42
- ).model_dump(),
43
- status_code=e.status_code,
44
- )
45
- # pylint: disable=broad-exception-caught
46
- except Exception as e:
30
+ return e.to_response()
31
+ except Exception as e: # pylint: disable=broad-exception-caught
47
32
  if self.__debug:
48
33
  traceback.print_exc() # pragma: no cover
49
- error = InternalServerError(e)
50
- return ORJSONResponse(
51
- content=ErrorResponse(
52
- message=error.message,
53
- args=[str(x) for x in error.args],
54
- traceback=error.traceback if self.__debug else "",
55
- ).model_dump(),
56
- status_code=error.status_code,
57
- )
34
+ return InternalServerError(
35
+ error=e,
36
+ stacktrace=traceback.format_exc() if self.__debug else None,
37
+ ).to_response()
@@ -4,7 +4,7 @@ from dataclasses import asdict
4
4
 
5
5
  from fastapi import APIRouter, FastAPI
6
6
  from fastapi.exceptions import FastAPIError
7
- from fastapi.utils import create_response_field # type: ignore
7
+ from fastapi.utils import create_model_field # type: ignore
8
8
  from spakky.application.interfaces.container import IPodContainer
9
9
  from spakky.application.interfaces.post_processor import IPodPostProcessor
10
10
  from spakky.pod.order import Order
@@ -49,7 +49,7 @@ class FastAPIPostProcessor(IPodPostProcessor):
49
49
  return_annotation: type | None = signature(method).return_annotation
50
50
  if return_annotation is not None:
51
51
  try:
52
- create_response_field("", return_annotation)
52
+ create_model_field("", return_annotation)
53
53
  except FastAPIError:
54
54
  pass
55
55
  else:
@@ -503,6 +503,6 @@ def websocket(
503
503
  )
504
504
 
505
505
 
506
- @dataclass
506
+ @dataclass(eq=False)
507
507
  class ApiController(Controller):
508
508
  tags: list[str | Enum] | None = None
@@ -1,45 +0,0 @@
1
- import traceback
2
-
3
- from fastapi import status
4
- from spakky.core.error import SpakkyCoreError
5
-
6
-
7
- class SpakkyFastAPIError(SpakkyCoreError):
8
- status_code: int
9
-
10
- def __init__(self, error: SpakkyCoreError) -> None:
11
- self.message = error.message
12
- self.args = error.args
13
-
14
-
15
- class SpakkyUnknownError(SpakkyCoreError):
16
- message = "알 수 없는 오류가 발생했습니다."
17
-
18
-
19
- class BadRequest(SpakkyFastAPIError):
20
- status_code: int = status.HTTP_400_BAD_REQUEST
21
-
22
-
23
- class Unauthorized(SpakkyFastAPIError):
24
- status_code: int = status.HTTP_401_UNAUTHORIZED
25
-
26
-
27
- class Forbidden(SpakkyFastAPIError):
28
- status_code: int = status.HTTP_403_FORBIDDEN
29
-
30
-
31
- class NotFound(SpakkyFastAPIError):
32
- status_code: int = status.HTTP_404_NOT_FOUND
33
-
34
-
35
- class Conflict(SpakkyFastAPIError):
36
- status_code: int = status.HTTP_409_CONFLICT
37
-
38
-
39
- class InternalServerError(SpakkyFastAPIError):
40
- status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR
41
- traceback: str = ""
42
-
43
- def __init__(self, error: Exception) -> None:
44
- super().__init__(SpakkyUnknownError(error.args))
45
- self.traceback = traceback.format_exc()
File without changes