fastapi-extra 0.2.5__cp312-cp312-win_amd64.whl → 0.3.0__cp312-cp312-win_amd64.whl

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.
fastapi_extra/__init__.py CHANGED
@@ -1,13 +1,17 @@
1
- __version__ = "0.2.5"
1
+ __version__ = "0.3.0"
2
2
 
3
3
 
4
4
  from fastapi import FastAPI
5
+ from fastapi import routing as origin_routing
6
+ from fastapi.dependencies import utils as origin_utils
5
7
 
6
8
 
7
9
  def setup(app: FastAPI) -> None:
8
10
  try:
9
- from fastapi_extra import routing as native_routing # type: ignore
10
-
11
- native_routing.install(app)
11
+ from fastapi_extra import _patch
12
+
13
+ _patch.install_routes(app)
14
+ origin_routing.solve_dependencies.__globals__['is_sequence_field'] = _patch.is_sequence_field # type: ignore
15
+ origin_utils.QueryParams.__init__ = _patch.query_params_init # type: ignore
12
16
  except ImportError: # pragma: nocover
13
17
  pass
@@ -0,0 +1,33 @@
1
+ __author__ = "ziyan.yin"
2
+ __date__ = "2026-01-13"
3
+
4
+
5
+ from fastapi import FastAPI
6
+ from fastapi._compat import v2
7
+ from starlette import datastructures
8
+
9
+ from fastapi_extra import routing
10
+ from fastapi_extra.urlparse import parse_qsl
11
+
12
+
13
+ def install_routes(app: FastAPI) -> None:
14
+ routing.install(app)
15
+
16
+
17
+ def is_sequence_field(field: v2.ModelField) -> bool:
18
+ if not hasattr(field, "_is_sequence"):
19
+ setattr(field, "_is_sequence", v2.is_sequence_field(field))
20
+ return getattr(field, "_is_sequence")
21
+
22
+
23
+ def query_params_init(obj: datastructures.QueryParams, *args, **kwargs) -> None:
24
+ value = args[0] if args else []
25
+
26
+ if isinstance(value, bytes):
27
+ super(datastructures.QueryParams, obj).__init__(parse_qsl(value, keep_blank_values=True), **kwargs)
28
+ elif isinstance(value, str):
29
+ super(datastructures.QueryParams, obj).__init__(parse_qsl(value.encode("latin-1"), keep_blank_values=True), **kwargs)
30
+ else:
31
+ super(datastructures.QueryParams, obj).__init__(*args, **kwargs) # type: ignore[arg-type]
32
+ obj._list = [(str(k), str(v)) for k, v in obj._list]
33
+ obj._dict = {str(k): str(v) for k, v in obj._dict.items()}
Binary file
@@ -0,0 +1,50 @@
1
+ __author__ = "ziyan.yin"
2
+ __describe__ = ""
3
+
4
+
5
+ from libc.stdlib cimport strtol
6
+ from libc.string cimport memmove, strlen
7
+
8
+
9
+ cdef inline size_t _unquote(char* c_string, bint change_plus):
10
+ cdef:
11
+ int i = 0
12
+ char[2] quote
13
+ size_t n = strlen(c_string)
14
+
15
+ while i < n:
16
+ if c_string[i] == '+' and change_plus:
17
+ c_string[i] = ' '
18
+ elif c_string[i] == '%':
19
+ quote[0] = c_string[i + 1]
20
+ quote[1] = c_string[i + 2]
21
+ c_string[i] = strtol(quote, NULL, 16)
22
+ memmove(c_string + i + 1, c_string + i + 3, n - i - 2)
23
+ n -= 2
24
+ i += 1
25
+ return n
26
+
27
+
28
+ def unquote(val: bytes, encoding: str = "utf-8") -> str:
29
+ return val[:_unquote(val, 0)].decode(encoding)
30
+
31
+
32
+ def unquote_plus(val: bytes, encoding: str = "utf-8") -> str:
33
+ return val[:_unquote(val, 1)].decode(encoding)
34
+
35
+
36
+ def parse_qsl(qs: bytes, keep_blank_values: bool = False) -> list[tuple[str, str]]:
37
+ query_args = qs.split(b'&') if qs else []
38
+ r = []
39
+ for name_value in query_args:
40
+ if not name_value:
41
+ continue
42
+ nv = name_value.split(b'=')
43
+ if len(nv) < 2:
44
+ if not keep_blank_values:
45
+ continue
46
+ nv.append(b'')
47
+ name = unquote_plus(nv[0])
48
+ value = unquote_plus(nv[1])
49
+ r.append((name, value))
50
+ return r
fastapi_extra/response.py CHANGED
@@ -3,7 +3,7 @@ __date__ = "2024-12-24"
3
3
 
4
4
 
5
5
  from enum import Enum
6
- from typing import TYPE_CHECKING, Generic
6
+ from typing import TYPE_CHECKING, Generic, Mapping
7
7
 
8
8
  from fastapi.responses import JSONResponse
9
9
  from pydantic import BaseModel, Field
@@ -220,6 +220,20 @@ class APIResponse(JSONResponse):
220
220
  exclude_none=False,
221
221
  exclude_unset=False,
222
222
  )
223
+
224
+ def init_headers(self, headers: Mapping[str, str] | None = None) -> None:
225
+ self.raw_headers = [
226
+ (b"content-length", str(len(self.body)).encode("latin-1")),
227
+ (b"content-type", b"application/json; charset=utf-8"),
228
+ ]
229
+ if headers:
230
+ raw_headers = [
231
+ (k.lower().encode("latin-1"), v.encode("latin-1"))
232
+ for k, v in headers.items() if k not in ("content-length", "content-type")
233
+ ]
234
+ self.raw_headers.extend(raw_headers)
235
+
236
+
223
237
 
224
238
 
225
239
  class APIError(Exception):
Binary file
@@ -0,0 +1,11 @@
1
+ __author__ = "ziyan.yin"
2
+ __describe__ = ""
3
+
4
+
5
+ def unquote(val: bytes, encoding: str = "utf-8") -> str: ...
6
+
7
+
8
+ def unquote_plus(val: bytes, encoding: str = "utf-8") -> str: ...
9
+
10
+
11
+ def parse_qsl(qs: bytes, keep_blank_values: bool = False) -> list[tuple[str, str]]: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-extra
3
- Version: 0.2.5
3
+ Version: 0.3.0
4
4
  Summary: extra package for fastapi.
5
5
  Author-email: Ziyan Yin <408856732@qq.com>
6
6
  License: BSD-3-Clause
@@ -18,9 +18,9 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
18
  Requires-Python: >=3.12
19
19
  Description-Content-Type: text/x-rst
20
20
  License-File: LICENSE
21
- Requires-Dist: fastapi<0.116.0,>=0.115.0
21
+ Requires-Dist: fastapi<0.129.0,>=0.128.0
22
22
  Requires-Dist: httpx<0.29.0,>=0.28.0
23
- Requires-Dist: pydantic-settings>=2.7.0
23
+ Requires-Dist: pydantic-settings>=2.12.0
24
24
  Requires-Dist: sqlmodel>=0.0.22
25
25
  Provides-Extra: redis
26
26
  Requires-Dist: redis; extra == "redis"
@@ -1,14 +1,17 @@
1
- fastapi_extra/__init__.py,sha256=rJP0eaJqATS_5xgXXgckRpXjww5v96IXUmNxlbb3J8Y,278
2
- fastapi_extra/cursor.cp312-win_amd64.pyd,sha256=SsGGCZqd7fnrd8sp9r75yTdO5D8IRckLBndnegrgneA,53760
1
+ fastapi_extra/__init__.py,sha256=wgNT6dUITkXf69b2m5-YVabRIJRzirouJfBh3nLnQKg,556
2
+ fastapi_extra/_patch.py,sha256=WpuAsjBRKgU5fgmGPhAdm68jc_QAArsZyKuYzO1LTuI,1194
3
+ fastapi_extra/cursor.cp312-win_amd64.pyd,sha256=yb4XpcrI2yyiV0s8MjYO8wIZlTwxeLv8GynyTHg6fnA,52224
3
4
  fastapi_extra/cursor.pyi,sha256=9VRlz_g1lPEqn_03thmwJga7aELGp--jvqySN7f_4iM,158
4
5
  fastapi_extra/dependency.py,sha256=cnmZjoL9DVZq8qHAkLVhDX3RIvCWUMdxN0dB7obZruc,2106
5
6
  fastapi_extra/form.py,sha256=jwaMfHiILp_wUlP-BRA-Gp3zp42T5mrZjJp0CALU5So,1275
6
7
  fastapi_extra/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- fastapi_extra/response.py,sha256=L8Q6fQtOSmHPClPdc4d2_LcOb5oiKQCpaYQLR1Zxp8Q,10274
8
- fastapi_extra/routing.cp312-win_amd64.pyd,sha256=JKhBvG07s24uHvUxS0HrkNzHaF4uso-5qXQ05yuKbaM,91648
8
+ fastapi_extra/response.py,sha256=xLZs0oVf3fs1BF613UpDSx5XsrLmsCTfPibWujfqM9s,10827
9
+ fastapi_extra/routing.cp312-win_amd64.pyd,sha256=kjqn56w9U2xrJB2mEfcJ8PwlvVF3n8j2_RLR_3H0hH8,90112
9
10
  fastapi_extra/routing.pyi,sha256=0HdMRqaeAtT7kM9GFMkffOeNeNwKPK1YqtQ40SDMKew,223
10
11
  fastapi_extra/settings.py,sha256=PsjsdDvGR55spOFB6VUArzvwO7GsgZEwYKat-NXnN9s,1452
11
12
  fastapi_extra/types.py,sha256=8uG5vwWsYh5Wq7KORKmK31F4cw11ReCD9Nx6Tv2D_MU,825
13
+ fastapi_extra/urlparse.cp312-win_amd64.pyd,sha256=oowDmGfQpXJg2Rr9FpC6Tj0AURyb9aRgAcooX8Ip_d8,44544
14
+ fastapi_extra/urlparse.pyi,sha256=1FDp46XKqTrlJHlnbqK_NyzBkCYRpHEINERtNsMyvWI,275
12
15
  fastapi_extra/utils.py,sha256=tsPX3kpF_P5D9Bd3gnlG6rkVsLkv5gbxjml-s6ZL_6I,346
13
16
  fastapi_extra/cache/__init__.py,sha256=Kq5eCF53aGIFch14xDj_SCiK5IvY7A0jX28st-ergls,114
14
17
  fastapi_extra/cache/redis.py,sha256=Zcoyn98d7uyp6p07sssjjo_TcG-kyjPj-D-zr4C8liU,1624
@@ -18,8 +21,9 @@ fastapi_extra/database/service.py,sha256=uc_1VlExIapSoQMpLUhckA_gBLW1bGXLVwTy2uz
18
21
  fastapi_extra/database/session.py,sha256=fIC5Pk7C-kiF2alFJpW02_P5aoFZrAP3LtJwSelHj4Y,2011
19
22
  fastapi_extra/native/cursor.pyx,sha256=GchJIPY6e5scuyl1BbA-Et0CdRcHOI3xgsTvDem0B70,1141
20
23
  fastapi_extra/native/routing.pyx,sha256=NisJ9YaxzceDVWJnF3EeVXWT8rv_6WRDuIfLEK0eWbc,6233
21
- fastapi_extra-0.2.5.dist-info/licenses/LICENSE,sha256=0vTjHDa3VDsxTT-R-sH6SpYcA2F1hKtbX9ZFZQm-EcU,1516
22
- fastapi_extra-0.2.5.dist-info/METADATA,sha256=YXgH8WXpZGYrCqupM911xt_BWTTW_2PRKz1haV9a7PM,1371
23
- fastapi_extra-0.2.5.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
24
- fastapi_extra-0.2.5.dist-info/top_level.txt,sha256=B7D80bEftE2E-eSd1be2r9BWkLLMZN21dRTWpb4y4Ig,14
25
- fastapi_extra-0.2.5.dist-info/RECORD,,
24
+ fastapi_extra/native/urlparse.pyx,sha256=LJWPyZlajYkjBoin65NgeOjrdR9wKabrB5Lpr8edgIU,1406
25
+ fastapi_extra-0.3.0.dist-info/licenses/LICENSE,sha256=0vTjHDa3VDsxTT-R-sH6SpYcA2F1hKtbX9ZFZQm-EcU,1516
26
+ fastapi_extra-0.3.0.dist-info/METADATA,sha256=wAmRDRVtVIKTgpyr6Mbb0ulTn-brtEN4Ug7jSBj2_Ew,1372
27
+ fastapi_extra-0.3.0.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
28
+ fastapi_extra-0.3.0.dist-info/top_level.txt,sha256=B7D80bEftE2E-eSd1be2r9BWkLLMZN21dRTWpb4y4Ig,14
29
+ fastapi_extra-0.3.0.dist-info/RECORD,,