fastapi_swagger2 0.2.4__tar.gz → 0.2.6__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.
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/PKG-INFO +17 -15
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/README.md +2 -1
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/pyproject.toml +16 -19
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/scripts/format.sh +1 -1
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/scripts/lint.sh +1 -1
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/src/fastapi_swagger2/__init__.py +1 -1
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/src/fastapi_swagger2/utils.py +107 -11
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/.gitignore +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/LICENSE +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/requirements-dev.txt +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/requirements.txt +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/scripts/build.sh +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/scripts/clean.sh +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/scripts/publish.sh +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/scripts/test.sh +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/setup.cfg +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/src/fastapi_swagger2/constants.py +0 -0
- {fastapi_swagger2-0.2.4 → fastapi_swagger2-0.2.6}/src/fastapi_swagger2/models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi_swagger2
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Swagger2 support for FastAPI framework
|
|
5
5
|
Project-URL: Homepage, https://github.com/virajkanwade/fastapi_swagger2
|
|
6
6
|
Project-URL: Documentation, https://github.com/virajkanwade/fastapi_swagger2
|
|
@@ -40,10 +40,11 @@ Classifier: Operating System :: OS Independent
|
|
|
40
40
|
Classifier: Programming Language :: Python
|
|
41
41
|
Classifier: Programming Language :: Python :: 3
|
|
42
42
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
43
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
44
43
|
Classifier: Programming Language :: Python :: 3.9
|
|
45
44
|
Classifier: Programming Language :: Python :: 3.10
|
|
46
45
|
Classifier: Programming Language :: Python :: 3.11
|
|
46
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
47
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
47
48
|
Classifier: Topic :: Internet
|
|
48
49
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
49
50
|
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
@@ -51,20 +52,20 @@ Classifier: Topic :: Software Development
|
|
|
51
52
|
Classifier: Topic :: Software Development :: Libraries
|
|
52
53
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
53
54
|
Classifier: Typing :: Typed
|
|
54
|
-
Requires-Python: >=3.
|
|
55
|
-
Requires-Dist: fastapi
|
|
55
|
+
Requires-Python: >=3.9
|
|
56
|
+
Requires-Dist: fastapi<0.119.0,>=0.100.0
|
|
56
57
|
Provides-Extra: all
|
|
57
|
-
Requires-Dist: httpx>=0.
|
|
58
|
+
Requires-Dist: httpx>=0.28.1; extra == 'all'
|
|
58
59
|
Provides-Extra: dev
|
|
59
|
-
Requires-Dist: ruff==0.
|
|
60
|
+
Requires-Dist: ruff==0.14.10; extra == 'dev'
|
|
60
61
|
Provides-Extra: test
|
|
61
|
-
Requires-Dist: black==
|
|
62
|
-
Requires-Dist: coverage[toml]<8.0,>=
|
|
63
|
-
Requires-Dist: httpx
|
|
64
|
-
Requires-Dist: isort
|
|
65
|
-
Requires-Dist: mypy==1.
|
|
66
|
-
Requires-Dist: pytest<
|
|
67
|
-
Requires-Dist: ruff==0.
|
|
62
|
+
Requires-Dist: black==25.12.0; extra == 'test'
|
|
63
|
+
Requires-Dist: coverage[toml]<8.0,>=7.13.1; extra == 'test'
|
|
64
|
+
Requires-Dist: httpx>=0.28.1; extra == 'test'
|
|
65
|
+
Requires-Dist: isort>=7.0.0; extra == 'test'
|
|
66
|
+
Requires-Dist: mypy==1.19.1; extra == 'test'
|
|
67
|
+
Requires-Dist: pytest<10.0.0,>=9.0.2; extra == 'test'
|
|
68
|
+
Requires-Dist: ruff==0.14.10; extra == 'test'
|
|
68
69
|
Description-Content-Type: text/markdown
|
|
69
70
|
|
|
70
71
|
# fastapi_swagger2
|
|
@@ -90,11 +91,12 @@ Few API GW services like Google Cloud API GW still support only Swagger 2.0 spec
|
|
|
90
91
|
|
|
91
92
|
## Requirements
|
|
92
93
|
|
|
93
|
-
Python 3.
|
|
94
|
+
Python 3.9+
|
|
94
95
|
|
|
95
96
|
* 0.0.3 - FastAPI >= 0.79.0, <= 0.98.0
|
|
96
97
|
* 0.1.1 - FastAPI >= 0.99.0, <= 0.99.1
|
|
97
98
|
* 0.2.4 - FastAPI >= 0.100.0
|
|
99
|
+
* 0.2.6 - FastAPI >= 0.100.0, < 0.199.0 > + Pydantic v1/v2
|
|
98
100
|
|
|
99
101
|
## Installation
|
|
100
102
|
|
|
@@ -21,11 +21,12 @@ Few API GW services like Google Cloud API GW still support only Swagger 2.0 spec
|
|
|
21
21
|
|
|
22
22
|
## Requirements
|
|
23
23
|
|
|
24
|
-
Python 3.
|
|
24
|
+
Python 3.9+
|
|
25
25
|
|
|
26
26
|
* 0.0.3 - FastAPI >= 0.79.0, <= 0.98.0
|
|
27
27
|
* 0.1.1 - FastAPI >= 0.99.0, <= 0.99.1
|
|
28
28
|
* 0.2.4 - FastAPI >= 0.100.0
|
|
29
|
+
* 0.2.6 - FastAPI >= 0.100.0, < 0.199.0 > + Pydantic v1/v2
|
|
29
30
|
|
|
30
31
|
## Installation
|
|
31
32
|
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
name = "fastapi_swagger2"
|
|
7
7
|
description = "Swagger2 support for FastAPI framework"
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.9"
|
|
10
10
|
license = { file="LICENSE" }
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "Viraj Kanwade", email = "virajk.oib@gmail.com" },
|
|
@@ -30,15 +30,16 @@ classifiers = [
|
|
|
30
30
|
"Intended Audience :: Developers",
|
|
31
31
|
"License :: OSI Approved :: MIT License",
|
|
32
32
|
"Programming Language :: Python :: 3 :: Only",
|
|
33
|
-
"Programming Language :: Python :: 3.8",
|
|
34
33
|
"Programming Language :: Python :: 3.9",
|
|
35
34
|
"Programming Language :: Python :: 3.10",
|
|
36
35
|
"Programming Language :: Python :: 3.11",
|
|
36
|
+
"Programming Language :: Python :: 3.12",
|
|
37
|
+
"Programming Language :: Python :: 3.13",
|
|
37
38
|
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
|
|
38
39
|
"Topic :: Internet :: WWW/HTTP",
|
|
39
40
|
]
|
|
40
41
|
dependencies = [
|
|
41
|
-
"fastapi >=0.100.0",
|
|
42
|
+
"fastapi >=0.100.0,<0.119.0",
|
|
42
43
|
]
|
|
43
44
|
dynamic = ["version"]
|
|
44
45
|
|
|
@@ -48,19 +49,19 @@ Documentation = "https://github.com/virajkanwade/fastapi_swagger2"
|
|
|
48
49
|
|
|
49
50
|
[project.optional-dependencies]
|
|
50
51
|
test = [
|
|
51
|
-
"pytest >=
|
|
52
|
-
"coverage[toml] >=
|
|
53
|
-
"mypy ==1.
|
|
54
|
-
"ruff ==0.
|
|
55
|
-
"black ==
|
|
56
|
-
"isort >=
|
|
57
|
-
"httpx >=0.
|
|
52
|
+
"pytest >=9.0.2,<10.0.0",
|
|
53
|
+
"coverage[toml] >= 7.13.1,< 8.0",
|
|
54
|
+
"mypy ==1.19.1",
|
|
55
|
+
"ruff ==0.14.10",
|
|
56
|
+
"black == 25.12.0",
|
|
57
|
+
"isort >=7.0.0",
|
|
58
|
+
"httpx >=0.28.1",
|
|
58
59
|
]
|
|
59
60
|
dev = [
|
|
60
|
-
"ruff ==0.
|
|
61
|
+
"ruff ==0.14.10",
|
|
61
62
|
]
|
|
62
63
|
all = [
|
|
63
|
-
"httpx >=0.
|
|
64
|
+
"httpx >=0.28.1",
|
|
64
65
|
]
|
|
65
66
|
|
|
66
67
|
[tool.hatch.version]
|
|
@@ -73,7 +74,7 @@ known_third_party = ["fastapi", "pydantic", "starlette"]
|
|
|
73
74
|
[tool.mypy]
|
|
74
75
|
strict = true
|
|
75
76
|
|
|
76
|
-
[tool.ruff]
|
|
77
|
+
[tool.ruff.lint]
|
|
77
78
|
select = [
|
|
78
79
|
"E", # pycodestyle errors
|
|
79
80
|
"W", # pycodestyle warnings
|
|
@@ -87,9 +88,5 @@ ignore = [
|
|
|
87
88
|
"B008", # do not perform function calls in argument defaults
|
|
88
89
|
"C901", # too complex
|
|
89
90
|
]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"__init__.py" = ["F401"]
|
|
93
|
-
|
|
94
|
-
[tool.ruff.isort]
|
|
95
|
-
known-third-party = ["fastapi", "pydantic", "starlette"]
|
|
91
|
+
per-file-ignores = { "__init__.py" = ["F401"] }
|
|
92
|
+
isort = { known-third-party = ["fastapi", "pydantic", "starlette"] }
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import http.client
|
|
2
2
|
import inspect
|
|
3
|
+
from enum import Enum
|
|
3
4
|
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast
|
|
4
5
|
from urllib.parse import ParseResult, urlparse
|
|
5
6
|
|
|
6
7
|
from fastapi import routing
|
|
7
8
|
from fastapi._compat import (
|
|
9
|
+
PYDANTIC_V2,
|
|
8
10
|
GenerateJsonSchema,
|
|
9
11
|
JsonSchemaValue,
|
|
10
12
|
ModelField,
|
|
11
13
|
Undefined,
|
|
12
14
|
get_compat_model_name_map,
|
|
13
|
-
get_definitions,
|
|
14
|
-
get_schema_from_model_field,
|
|
15
15
|
lenient_issubclass,
|
|
16
16
|
)
|
|
17
17
|
from fastapi.datastructures import DefaultPlaceholder
|
|
@@ -29,6 +29,7 @@ from fastapi.params import Body, Param
|
|
|
29
29
|
from fastapi.responses import Response
|
|
30
30
|
from fastapi.types import ModelNameMap
|
|
31
31
|
from fastapi.utils import deep_dict_update, is_body_allowed_for_status_code
|
|
32
|
+
from pydantic import BaseModel
|
|
32
33
|
from starlette.responses import JSONResponse
|
|
33
34
|
from starlette.routing import BaseRoute
|
|
34
35
|
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
|
|
@@ -37,6 +38,65 @@ from typing_extensions import Literal
|
|
|
37
38
|
from fastapi_swagger2.constants import REF_PREFIX, REF_TEMPLATE
|
|
38
39
|
from fastapi_swagger2.models import Swagger2
|
|
39
40
|
|
|
41
|
+
if PYDANTIC_V2:
|
|
42
|
+
from fastapi._compat import get_definitions, get_schema_from_model_field
|
|
43
|
+
else:
|
|
44
|
+
from pydantic.schema import (
|
|
45
|
+
field_schema,
|
|
46
|
+
get_flat_models_from_fields,
|
|
47
|
+
model_process_schema,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def get_model_definitions(
|
|
51
|
+
*,
|
|
52
|
+
flat_models: Set[Union[Type[BaseModel], Type[Enum]]],
|
|
53
|
+
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
|
|
54
|
+
) -> Dict[str, Any]:
|
|
55
|
+
definitions: Dict[str, Dict[str, Any]] = {}
|
|
56
|
+
for model in flat_models:
|
|
57
|
+
m_schema, m_definitions, m_nested_models = model_process_schema(
|
|
58
|
+
model, model_name_map=model_name_map, ref_prefix=REF_PREFIX
|
|
59
|
+
)
|
|
60
|
+
definitions.update(m_definitions)
|
|
61
|
+
model_name = model_name_map[model]
|
|
62
|
+
if "description" in m_schema:
|
|
63
|
+
m_schema["description"] = m_schema["description"].split("\f")[0]
|
|
64
|
+
definitions[model_name] = m_schema
|
|
65
|
+
return definitions
|
|
66
|
+
|
|
67
|
+
def get_definitions(
|
|
68
|
+
*,
|
|
69
|
+
fields: List[ModelField],
|
|
70
|
+
schema_generator: GenerateJsonSchema,
|
|
71
|
+
model_name_map: ModelNameMap,
|
|
72
|
+
separate_input_output_schemas: bool = True,
|
|
73
|
+
) -> Tuple[
|
|
74
|
+
Dict[
|
|
75
|
+
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
|
|
76
|
+
],
|
|
77
|
+
Dict[str, Dict[str, Any]],
|
|
78
|
+
]:
|
|
79
|
+
models = get_flat_models_from_fields(fields, known_models=set())
|
|
80
|
+
return {}, get_model_definitions(
|
|
81
|
+
flat_models=models, model_name_map=model_name_map
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def get_schema_from_model_field(
|
|
85
|
+
*,
|
|
86
|
+
field: ModelField,
|
|
87
|
+
schema_generator: GenerateJsonSchema,
|
|
88
|
+
model_name_map: ModelNameMap,
|
|
89
|
+
field_mapping: Dict[
|
|
90
|
+
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
|
|
91
|
+
],
|
|
92
|
+
separate_input_output_schemas: bool = True,
|
|
93
|
+
) -> Dict[str, Any]:
|
|
94
|
+
# This expects that GenerateJsonSchema was already used to generate the definitions
|
|
95
|
+
return field_schema( # type: ignore[no-any-return]
|
|
96
|
+
field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
|
|
97
|
+
)[0]
|
|
98
|
+
|
|
99
|
+
|
|
40
100
|
validation_error_definition = {
|
|
41
101
|
"title": "ValidationError",
|
|
42
102
|
"type": "object",
|
|
@@ -188,7 +248,10 @@ def get_swagger2_operation_parameters(
|
|
|
188
248
|
}
|
|
189
249
|
schema: Dict[str, Any] = param_schema
|
|
190
250
|
if field_info.in_.value == "body":
|
|
191
|
-
|
|
251
|
+
if "$ref" in schema:
|
|
252
|
+
parameter["schema"] = {"$ref", schema["$ref"]}
|
|
253
|
+
else:
|
|
254
|
+
parameter["schema"] = schema
|
|
192
255
|
else:
|
|
193
256
|
parameter.update({k: v for (k, v) in schema.items() if k != "title"})
|
|
194
257
|
if field_info.description:
|
|
@@ -226,7 +289,14 @@ def get_swagger2_operation_request_body(
|
|
|
226
289
|
if required:
|
|
227
290
|
request_body_oai["required"] = required
|
|
228
291
|
|
|
229
|
-
request_media_content: Dict[str, Any] = {
|
|
292
|
+
request_media_content: Dict[str, Any] = {}
|
|
293
|
+
if "$ref" in body_schema:
|
|
294
|
+
request_media_content["schema"] = {"$ref": body_schema["$ref"]}
|
|
295
|
+
request_media_content.update(
|
|
296
|
+
{k: v for (k, v) in body_schema.items() if k != "$ref"}
|
|
297
|
+
)
|
|
298
|
+
else:
|
|
299
|
+
request_media_content["schema"] = body_schema
|
|
230
300
|
if field_info.example != Undefined:
|
|
231
301
|
request_media_content["example"] = jsonable_encoder(field_info.example)
|
|
232
302
|
# request_body_oai["content"] = {request_media_type: request_media_content}
|
|
@@ -281,6 +351,7 @@ def get_swagger2_path(
|
|
|
281
351
|
model_name_map=model_name_map,
|
|
282
352
|
field_mapping=field_mapping,
|
|
283
353
|
)
|
|
354
|
+
|
|
284
355
|
parameters.extend(operation_parameters)
|
|
285
356
|
if parameters:
|
|
286
357
|
all_parameters = {
|
|
@@ -489,6 +560,14 @@ def get_swagger2(
|
|
|
489
560
|
if result:
|
|
490
561
|
path, security_schemes, path_definitions = result
|
|
491
562
|
|
|
563
|
+
for k, v in path.items():
|
|
564
|
+
for param in v["parameters"]:
|
|
565
|
+
if "$ref" in param and "in" in param and param["in"] != "body":
|
|
566
|
+
definition = definitions[param.pop("$ref").split("/")[2]]
|
|
567
|
+
param.update(
|
|
568
|
+
{k: v for (k, v) in definition.items() if k != "title"}
|
|
569
|
+
)
|
|
570
|
+
|
|
492
571
|
if path:
|
|
493
572
|
paths.setdefault(route.path_format, {}).update(path)
|
|
494
573
|
|
|
@@ -508,16 +587,33 @@ def get_swagger2(
|
|
|
508
587
|
for k in sorted(definitions):
|
|
509
588
|
properties = definitions[k].get("properties", [])
|
|
510
589
|
for p in properties:
|
|
511
|
-
if "anyOf" in properties[p]
|
|
590
|
+
if "anyOf" in properties[p]:
|
|
512
591
|
any_of = properties[p].pop("anyOf")
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
592
|
+
|
|
593
|
+
if len(any_of) == 1:
|
|
594
|
+
# Single item - just use it directly
|
|
595
|
+
properties[p].update(any_of[0])
|
|
596
|
+
elif len(any_of) == 2:
|
|
597
|
+
# Handle the 2-item case (type + null)
|
|
598
|
+
ref_item = None
|
|
599
|
+
has_null = False
|
|
600
|
+
|
|
601
|
+
for item in any_of:
|
|
602
|
+
if item == {"type": "null"}:
|
|
603
|
+
has_null = True
|
|
604
|
+
elif "$ref" in item:
|
|
605
|
+
ref_item = item
|
|
517
606
|
else:
|
|
518
|
-
properties[p].update(
|
|
607
|
+
properties[p].update(item)
|
|
608
|
+
|
|
609
|
+
if ref_item and has_null:
|
|
610
|
+
properties[p]["allOf"] = [ref_item]
|
|
611
|
+
properties[p]["x-nullable"] = True
|
|
612
|
+
elif has_null:
|
|
613
|
+
properties[p]["x-nullable"] = True
|
|
519
614
|
else:
|
|
520
|
-
|
|
615
|
+
# Fallback for complex anyOf cases (len > 2) or empty (len == 0)
|
|
616
|
+
properties[p]["type"] = "string"
|
|
521
617
|
logger.warning(
|
|
522
618
|
f"fastapi_swagger2: Unable to handle anyOf in definitions {any_of}, defaulting to string type."
|
|
523
619
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|