fastapi-extra 0.3.2__tar.gz → 0.3.4__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. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/PKG-INFO +1 -1
  2. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/__init__.py +2 -1
  3. fastapi_extra-0.3.4/fastapi_extra/_patch.py +121 -0
  4. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra.egg-info/PKG-INFO +1 -1
  5. fastapi_extra-0.3.2/fastapi_extra/_patch.py +0 -33
  6. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/LICENSE +0 -0
  7. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/README.rst +0 -0
  8. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/cache/__init__.py +0 -0
  9. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/cache/redis.py +0 -0
  10. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/cursor.pyi +0 -0
  11. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/database/__init__.py +0 -0
  12. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/database/model.py +0 -0
  13. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/database/service.py +0 -0
  14. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/database/session.py +0 -0
  15. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/dependency.py +0 -0
  16. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/form.py +0 -0
  17. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/native/cursor.pyx +0 -0
  18. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/native/routing.pyx +0 -0
  19. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/native/urlparse.pyx +0 -0
  20. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/py.typed +0 -0
  21. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/response.py +0 -0
  22. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/routing.pyi +0 -0
  23. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/settings.py +0 -0
  24. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/types.py +0 -0
  25. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/urlparse.pyi +0 -0
  26. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra/utils.py +0 -0
  27. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra.egg-info/SOURCES.txt +0 -0
  28. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra.egg-info/dependency_links.txt +0 -0
  29. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra.egg-info/requires.txt +0 -0
  30. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/fastapi_extra.egg-info/top_level.txt +0 -0
  31. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/pyproject.toml +0 -0
  32. {fastapi_extra-0.3.2 → fastapi_extra-0.3.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-extra
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: extra package for fastapi.
5
5
  Author-email: Ziyan Yin <408856732@qq.com>
6
6
  License: BSD-3-Clause
@@ -1,4 +1,4 @@
1
- __version__ = "0.3.2"
1
+ __version__ = "0.3.4"
2
2
 
3
3
 
4
4
  from fastapi import FastAPI
@@ -12,6 +12,7 @@ def setup(app: FastAPI) -> None:
12
12
 
13
13
  _patch.install_routes(app)
14
14
  origin_routing.solve_dependencies.__globals__['field_annotation_is_sequence'] = _patch.is_sequence_field # type: ignore
15
+ origin_routing.solve_dependencies.__globals__['request_params_to_args'] = _patch.request_params_to_args # type: ignore
15
16
  origin_utils.QueryParams.__init__ = _patch.query_params_init # type: ignore
16
17
  except ImportError: # pragma: nocover
17
18
  pass
@@ -0,0 +1,121 @@
1
+ __author__ = "ziyan.yin"
2
+ __date__ = "2026-01-13"
3
+
4
+ import functools
5
+ from typing import Any, Mapping, Sequence, Union
6
+
7
+ from fastapi import FastAPI, params
8
+ from fastapi._compat import (ModelField, get_cached_model_fields,
9
+ lenient_issubclass, shared)
10
+ from fastapi.dependencies import utils
11
+ from pydantic import BaseModel
12
+ from starlette import datastructures
13
+
14
+ from fastapi_extra import routing
15
+ from fastapi_extra.urlparse import parse_qsl
16
+
17
+
18
+ def install_routes(app: FastAPI) -> None:
19
+ routing.install(app)
20
+
21
+
22
+ @functools.lru_cache
23
+ def is_sequence_field(annotation: type[Any]) -> bool:
24
+ return shared.field_annotation_is_sequence(annotation)
25
+
26
+
27
+ def request_params_to_args(
28
+ fields: Sequence[ModelField],
29
+ received_params: Union[Mapping[str, Any], datastructures.QueryParams, datastructures.Headers],
30
+ ) -> tuple[dict[str, Any], list[Any]]:
31
+ values: dict[str, Any] = {}
32
+ errors: list[dict[str, Any]] = []
33
+
34
+ if not fields:
35
+ return values, errors
36
+
37
+ first_field = fields[0]
38
+ fields_to_extract = fields
39
+ single_not_embedded_field = False
40
+ default_convert_underscores = True
41
+ if len(fields) == 1 and lenient_issubclass(
42
+ first_field.field_info.annotation, BaseModel
43
+ ):
44
+ fields_to_extract = get_cached_model_fields(first_field.field_info.annotation)
45
+ single_not_embedded_field = True
46
+ # If headers are in a Pydantic model, the way to disable convert_underscores
47
+ # would be with Header(convert_underscores=False) at the Pydantic model level
48
+ default_convert_underscores = getattr(
49
+ first_field.field_info, "convert_underscores", True
50
+ )
51
+
52
+ params_to_process: dict[str, Any] = {}
53
+
54
+ processed_keys = set()
55
+
56
+ for field in fields_to_extract:
57
+ alias = field_alias = utils.get_validation_alias(field)
58
+ if isinstance(received_params, datastructures.Headers):
59
+ # Handle fields extracted from a Pydantic Model for a header, each field
60
+ # doesn't have a FieldInfo of type Header with the default convert_underscores=True
61
+ convert_underscores = getattr(
62
+ field.field_info, "convert_underscores", default_convert_underscores
63
+ )
64
+ if convert_underscores and alias == field.name:
65
+ alias = alias.replace("_", "-")
66
+ value = utils._get_multidict_value(field, received_params, alias=alias)
67
+ if value is not None:
68
+ params_to_process[field_alias] = value
69
+ processed_keys.add(alias)
70
+
71
+ for key in received_params.keys():
72
+ if key not in processed_keys:
73
+ if hasattr(received_params, "getlist"):
74
+ value = received_params.getlist(key)
75
+ if isinstance(value, list) and (len(value) == 1):
76
+ params_to_process[key] = value[0]
77
+ else:
78
+ params_to_process[key] = value
79
+ else:
80
+ params_to_process[key] = received_params.get(key)
81
+
82
+ if single_not_embedded_field:
83
+ field_info = first_field.field_info
84
+ assert isinstance(field_info, params.Param), (
85
+ "Params must be subclasses of Param"
86
+ )
87
+ loc: tuple[str, ...] = (field_info.in_.value,)
88
+ v_, errors_ = utils._validate_value_with_model_field(
89
+ field=first_field, value=params_to_process, values=values, loc=loc
90
+ )
91
+ return {first_field.name: v_}, errors_
92
+
93
+ for field in fields:
94
+ field_alias = utils.get_validation_alias(field)
95
+ value = params_to_process.get(field_alias, None)
96
+ field_info = field.field_info
97
+ assert isinstance(field_info, params.Param), (
98
+ "Params must be subclasses of Param"
99
+ )
100
+ loc = (field_info.in_.value, field_alias)
101
+ v_, errors_ = utils._validate_value_with_model_field(
102
+ field=field, value=value, values=values, loc=loc
103
+ )
104
+ if errors_:
105
+ errors.extend(errors_)
106
+ else:
107
+ values[field.name] = v_
108
+ return values, errors
109
+
110
+
111
+ def query_params_init(obj: datastructures.QueryParams, *args, **kwargs) -> None:
112
+ value = args[0] if args else []
113
+
114
+ if isinstance(value, bytes):
115
+ super(datastructures.QueryParams, obj).__init__(parse_qsl(value, keep_blank_values=True), **kwargs)
116
+ elif isinstance(value, str):
117
+ super(datastructures.QueryParams, obj).__init__(parse_qsl(value.encode("latin-1"), keep_blank_values=True), **kwargs)
118
+ else:
119
+ super(datastructures.QueryParams, obj).__init__(*args, **kwargs) # type: ignore[arg-type]
120
+ obj._list = [(str(k), str(v)) for k, v in obj._list]
121
+ obj._dict = {str(k): str(v) for k, v in obj._dict.items()}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-extra
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: extra package for fastapi.
5
5
  Author-email: Ziyan Yin <408856732@qq.com>
6
6
  License: BSD-3-Clause
@@ -1,33 +0,0 @@
1
- __author__ = "ziyan.yin"
2
- __date__ = "2026-01-13"
3
-
4
-
5
- from fastapi import FastAPI
6
- from fastapi._compat import v2, shared
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", shared.field_annotation_is_sequence(field.field_info.annotation))
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()}
File without changes
File without changes
File without changes