flask-ast-openapi 0.2.0__tar.gz → 0.2.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.
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/PKG-INFO +24 -5
- flask_ast_openapi-0.2.0/src/flask_ast_openapi.egg-info/PKG-INFO → flask_ast_openapi-0.2.1/README.md +37 -30
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/pyproject.toml +1 -1
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi/generator.py +112 -27
- flask_ast_openapi-0.2.0/README.md → flask_ast_openapi-0.2.1/src/flask_ast_openapi.egg-info/PKG-INFO +46 -15
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/tests/test_generator.py +86 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/LICENSE +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/setup.cfg +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi/__init__.py +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi/cli.py +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi/swagger.py +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi.egg-info/SOURCES.txt +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi.egg-info/dependency_links.txt +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi.egg-info/entry_points.txt +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi.egg-info/requires.txt +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi.egg-info/top_level.txt +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/tests/test_cli.py +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/tests/test_package.py +0 -0
- {flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/tests/test_swagger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flask-ast-openapi
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Generate OpenAPI specifications from Flask source code using AST
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -55,6 +55,8 @@ It also provides optional Swagger UI integration for Flask applications.
|
|
|
55
55
|
* Marshmallow request schema support
|
|
56
56
|
* Marshmallow response schema support
|
|
57
57
|
* Cross-file Marshmallow schema resolution
|
|
58
|
+
* Automatic request content-type detection for JSON and multipart forms
|
|
59
|
+
* Request and response content-type docstring overrides
|
|
58
60
|
* Nested Marshmallow schema support
|
|
59
61
|
* OpenAPI component schema generation
|
|
60
62
|
* Configurable authentication decorators
|
|
@@ -431,6 +433,23 @@ def create_user():
|
|
|
431
433
|
...
|
|
432
434
|
```
|
|
433
435
|
|
|
436
|
+
JSON bodies are detected from `request.get_json()` and `request.json`.
|
|
437
|
+
Multipart bodies are detected from `request.form` and `request.files`.
|
|
438
|
+
For custom media types, use docstring directives:
|
|
439
|
+
|
|
440
|
+
```python
|
|
441
|
+
@app.post("/documents")
|
|
442
|
+
def upload_document():
|
|
443
|
+
"""
|
|
444
|
+
:request: DocumentUploadSchema
|
|
445
|
+
:response: DocumentResponseSchema
|
|
446
|
+
:request-content-type: multipart/form-data
|
|
447
|
+
:response-content-type: application/json
|
|
448
|
+
"""
|
|
449
|
+
|
|
450
|
+
...
|
|
451
|
+
```
|
|
452
|
+
|
|
434
453
|
The generator detects `UserRequestSchema` and creates an OpenAPI request-body schema.
|
|
435
454
|
|
|
436
455
|
## Response Schemas
|
|
@@ -785,12 +804,12 @@ Future development may include:
|
|
|
785
804
|
Current version:
|
|
786
805
|
|
|
787
806
|
```text
|
|
788
|
-
0.2.
|
|
807
|
+
0.2.1
|
|
789
808
|
```
|
|
790
809
|
|
|
791
|
-
Version `0.2.
|
|
792
|
-
|
|
793
|
-
|
|
810
|
+
Version `0.2.1` adds automatic JSON and multipart request content-type
|
|
811
|
+
detection, explicit request/response media-type overrides, and avoids creating
|
|
812
|
+
request bodies for header-only and path-only inputs.
|
|
794
813
|
|
|
795
814
|
## License
|
|
796
815
|
|
flask_ast_openapi-0.2.0/src/flask_ast_openapi.egg-info/PKG-INFO → flask_ast_openapi-0.2.1/README.md
RENAMED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: flask-ast-openapi
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Generate OpenAPI specifications from Flask source code using AST
|
|
5
|
-
Requires-Python: >=3.10
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
License-File: LICENSE
|
|
8
|
-
Requires-Dist: Flask>=3.0
|
|
9
|
-
Provides-Extra: dev
|
|
10
|
-
Requires-Dist: pytest>=8; extra == "dev"
|
|
11
|
-
Dynamic: license-file
|
|
12
|
-
|
|
13
1
|
# flask-ast-openapi
|
|
14
2
|
|
|
15
3
|
Generate OpenAPI 3 specifications from Flask source code using Python's Abstract Syntax Tree (AST).
|
|
@@ -31,10 +19,10 @@ It also provides optional Swagger UI integration for Flask applications.
|
|
|
31
19
|
* `@app.put(...)`
|
|
32
20
|
* `@app.patch(...)`
|
|
33
21
|
* `@app.delete(...)`
|
|
34
|
-
* Blueprint route support
|
|
35
|
-
* Blueprint `url_prefix` support
|
|
36
|
-
* Nested and cross-file Blueprint registration support
|
|
37
|
-
* Automatic Blueprint tags for Swagger UI grouping
|
|
22
|
+
* Blueprint route support
|
|
23
|
+
* Blueprint `url_prefix` support
|
|
24
|
+
* Nested and cross-file Blueprint registration support
|
|
25
|
+
* Automatic Blueprint tags for Swagger UI grouping
|
|
38
26
|
* Flask path parameter conversion to OpenAPI format
|
|
39
27
|
* Flask converter support:
|
|
40
28
|
|
|
@@ -53,8 +41,10 @@ It also provides optional Swagger UI integration for Flask applications.
|
|
|
53
41
|
* Function docstrings as OpenAPI descriptions
|
|
54
42
|
* Synchronous and asynchronous Flask route support
|
|
55
43
|
* Marshmallow request schema support
|
|
56
|
-
* Marshmallow response schema support
|
|
57
|
-
* Cross-file Marshmallow schema resolution
|
|
44
|
+
* Marshmallow response schema support
|
|
45
|
+
* Cross-file Marshmallow schema resolution
|
|
46
|
+
* Automatic request content-type detection for JSON and multipart forms
|
|
47
|
+
* Request and response content-type docstring overrides
|
|
58
48
|
* Nested Marshmallow schema support
|
|
59
49
|
* OpenAPI component schema generation
|
|
60
50
|
* Configurable authentication decorators
|
|
@@ -417,7 +407,7 @@ class UserRequestSchema(Schema):
|
|
|
417
407
|
active = fields.Boolean()
|
|
418
408
|
```
|
|
419
409
|
|
|
420
|
-
A route can reference the schema from its docstring:
|
|
410
|
+
A route can reference the schema from its docstring:
|
|
421
411
|
|
|
422
412
|
```python
|
|
423
413
|
@app.post("/users")
|
|
@@ -428,8 +418,25 @@ def create_user():
|
|
|
428
418
|
:request: UserRequestSchema
|
|
429
419
|
"""
|
|
430
420
|
|
|
431
|
-
...
|
|
432
|
-
```
|
|
421
|
+
...
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
JSON bodies are detected from `request.get_json()` and `request.json`.
|
|
425
|
+
Multipart bodies are detected from `request.form` and `request.files`.
|
|
426
|
+
For custom media types, use docstring directives:
|
|
427
|
+
|
|
428
|
+
```python
|
|
429
|
+
@app.post("/documents")
|
|
430
|
+
def upload_document():
|
|
431
|
+
"""
|
|
432
|
+
:request: DocumentUploadSchema
|
|
433
|
+
:response: DocumentResponseSchema
|
|
434
|
+
:request-content-type: multipart/form-data
|
|
435
|
+
:response-content-type: application/json
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
...
|
|
439
|
+
```
|
|
433
440
|
|
|
434
441
|
The generator detects `UserRequestSchema` and creates an OpenAPI request-body schema.
|
|
435
442
|
|
|
@@ -782,15 +789,15 @@ Future development may include:
|
|
|
782
789
|
|
|
783
790
|
## Version
|
|
784
791
|
|
|
785
|
-
Current version:
|
|
786
|
-
|
|
787
|
-
```text
|
|
788
|
-
0.2.
|
|
789
|
-
```
|
|
790
|
-
|
|
791
|
-
Version `0.2.
|
|
792
|
-
|
|
793
|
-
|
|
792
|
+
Current version:
|
|
793
|
+
|
|
794
|
+
```text
|
|
795
|
+
0.2.1
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
Version `0.2.1` adds automatic JSON and multipart request content-type
|
|
799
|
+
detection, explicit request/response media-type overrides, and avoids creating
|
|
800
|
+
request bodies for header-only and path-only inputs.
|
|
794
801
|
|
|
795
802
|
## License
|
|
796
803
|
|
|
@@ -32,8 +32,10 @@ class RouteDefinition:
|
|
|
32
32
|
auth_schemes: list[str] = field(default_factory=list)
|
|
33
33
|
auth_scheme_mode: str = "and"
|
|
34
34
|
description: str | None = None
|
|
35
|
-
request_schema: dict[str, Any] | None = None
|
|
35
|
+
request_schema: dict[str, Any] | None = None
|
|
36
36
|
response_schema: dict[str, Any] | None = None
|
|
37
|
+
request_content_type: str | None = "application/json"
|
|
38
|
+
response_content_type: str = "application/json"
|
|
37
39
|
tag: str | None = None
|
|
38
40
|
@dataclass
|
|
39
41
|
class PathParameter:
|
|
@@ -283,10 +285,16 @@ class FlaskASTOpenAPI:
|
|
|
283
285
|
function
|
|
284
286
|
)
|
|
285
287
|
),
|
|
286
|
-
request_schema=self.build_request_schema_from_docstring(
|
|
287
|
-
tree,
|
|
288
|
-
function,
|
|
289
|
-
),
|
|
288
|
+
request_schema=self.build_request_schema_from_docstring(
|
|
289
|
+
tree,
|
|
290
|
+
function,
|
|
291
|
+
),
|
|
292
|
+
request_content_type=(
|
|
293
|
+
self.extract_request_content_type(function)
|
|
294
|
+
),
|
|
295
|
+
response_content_type=(
|
|
296
|
+
self.extract_response_content_type(function)
|
|
297
|
+
),
|
|
290
298
|
uses_json_body=(
|
|
291
299
|
self.function_uses_json_body(
|
|
292
300
|
function
|
|
@@ -451,16 +459,20 @@ class FlaskASTOpenAPI:
|
|
|
451
459
|
operation: dict[str, Any] = {
|
|
452
460
|
"operationId": route.function_name,
|
|
453
461
|
"parameters": parameters,
|
|
454
|
-
"responses": self.build_openapi_responses(
|
|
455
|
-
response_status_codes,
|
|
456
|
-
response_schemas,
|
|
457
|
-
|
|
462
|
+
"responses": self.build_openapi_responses(
|
|
463
|
+
response_status_codes,
|
|
464
|
+
response_schemas,
|
|
465
|
+
route.response_content_type,
|
|
466
|
+
),
|
|
458
467
|
}
|
|
459
468
|
|
|
460
469
|
if route.tag:
|
|
461
470
|
operation["tags"] = [route.tag]
|
|
462
471
|
|
|
463
|
-
if
|
|
472
|
+
if (
|
|
473
|
+
route.request_content_type is not None
|
|
474
|
+
and (route.uses_json_body or route.request_schema is not None)
|
|
475
|
+
):
|
|
464
476
|
request_schema = route.request_schema
|
|
465
477
|
|
|
466
478
|
if request_schema is None:
|
|
@@ -471,12 +483,12 @@ class FlaskASTOpenAPI:
|
|
|
471
483
|
)
|
|
472
484
|
|
|
473
485
|
operation["requestBody"] = {
|
|
474
|
-
"required": True,
|
|
475
|
-
"content": {
|
|
476
|
-
|
|
477
|
-
"schema": request_schema,
|
|
478
|
-
}
|
|
479
|
-
},
|
|
486
|
+
"required": True,
|
|
487
|
+
"content": {
|
|
488
|
+
route.request_content_type: {
|
|
489
|
+
"schema": request_schema,
|
|
490
|
+
}
|
|
491
|
+
},
|
|
480
492
|
}
|
|
481
493
|
|
|
482
494
|
if route.description:
|
|
@@ -795,7 +807,7 @@ class FlaskASTOpenAPI:
|
|
|
795
807
|
}
|
|
796
808
|
for name in names
|
|
797
809
|
]
|
|
798
|
-
def function_uses_json_body(self,function: ast.FunctionDef | ast.AsyncFunctionDef,) -> bool:
|
|
810
|
+
def function_uses_json_body(self,function: ast.FunctionDef | ast.AsyncFunctionDef,) -> bool:
|
|
799
811
|
"""Check whether a route function reads a JSON request body."""
|
|
800
812
|
|
|
801
813
|
for node in ast.walk(function):
|
|
@@ -816,7 +828,79 @@ class FlaskASTOpenAPI:
|
|
|
816
828
|
):
|
|
817
829
|
return True
|
|
818
830
|
|
|
819
|
-
return False
|
|
831
|
+
return False
|
|
832
|
+
|
|
833
|
+
def function_uses_multipart_body(
|
|
834
|
+
self,
|
|
835
|
+
function: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
836
|
+
) -> bool:
|
|
837
|
+
"""Check whether a route reads form fields or uploaded files."""
|
|
838
|
+
|
|
839
|
+
for node in ast.walk(function):
|
|
840
|
+
if not isinstance(node, ast.Attribute):
|
|
841
|
+
continue
|
|
842
|
+
|
|
843
|
+
if (
|
|
844
|
+
isinstance(node.value, ast.Name)
|
|
845
|
+
and node.value.id == "request"
|
|
846
|
+
and node.attr in {"form", "files"}
|
|
847
|
+
):
|
|
848
|
+
return True
|
|
849
|
+
|
|
850
|
+
return False
|
|
851
|
+
|
|
852
|
+
def extract_docstring_directive(
|
|
853
|
+
self,
|
|
854
|
+
function: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
855
|
+
directive: str,
|
|
856
|
+
) -> str | None:
|
|
857
|
+
"""Extract a named ``:directive: value`` from a docstring."""
|
|
858
|
+
|
|
859
|
+
docstring = ast.get_docstring(function)
|
|
860
|
+
if not docstring:
|
|
861
|
+
return None
|
|
862
|
+
|
|
863
|
+
match = re.search(
|
|
864
|
+
rf"^\s*:{re.escape(directive)}:\s*([^\s]+)",
|
|
865
|
+
docstring,
|
|
866
|
+
re.MULTILINE,
|
|
867
|
+
)
|
|
868
|
+
return match.group(1) if match else None
|
|
869
|
+
|
|
870
|
+
def extract_request_content_type(
|
|
871
|
+
self,
|
|
872
|
+
function: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
873
|
+
) -> str | None:
|
|
874
|
+
"""Infer request body media type, allowing a docstring override."""
|
|
875
|
+
|
|
876
|
+
explicit = self.extract_docstring_directive(
|
|
877
|
+
function,
|
|
878
|
+
"request-content-type",
|
|
879
|
+
)
|
|
880
|
+
if explicit:
|
|
881
|
+
return explicit
|
|
882
|
+
|
|
883
|
+
if self.function_uses_multipart_body(function):
|
|
884
|
+
return "multipart/form-data"
|
|
885
|
+
|
|
886
|
+
if self.function_uses_json_body(function):
|
|
887
|
+
return "application/json"
|
|
888
|
+
|
|
889
|
+
return None
|
|
890
|
+
|
|
891
|
+
def extract_response_content_type(
|
|
892
|
+
self,
|
|
893
|
+
function: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
894
|
+
) -> str:
|
|
895
|
+
"""Return the declared response media type or JSON by default."""
|
|
896
|
+
|
|
897
|
+
return (
|
|
898
|
+
self.extract_docstring_directive(
|
|
899
|
+
function,
|
|
900
|
+
"response-content-type",
|
|
901
|
+
)
|
|
902
|
+
or "application/json"
|
|
903
|
+
)
|
|
820
904
|
def extract_json_body_field_names(self,function: ast.FunctionDef | ast.AsyncFunctionDef,) -> list[str]:
|
|
821
905
|
"""Extract JSON body field names used inside a route function."""
|
|
822
906
|
|
|
@@ -1151,10 +1235,11 @@ class FlaskASTOpenAPI:
|
|
|
1151
1235
|
status_codes.append(status_code)
|
|
1152
1236
|
|
|
1153
1237
|
return status_codes
|
|
1154
|
-
def build_openapi_responses(
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1238
|
+
def build_openapi_responses(
|
|
1239
|
+
self,
|
|
1240
|
+
status_codes: list[int],
|
|
1241
|
+
response_schemas: dict[int, dict[str, Any]] | None = None,
|
|
1242
|
+
content_type: str = "application/json",
|
|
1158
1243
|
) -> dict[str, Any]:
|
|
1159
1244
|
"""Build OpenAPI responses from status codes and schemas."""
|
|
1160
1245
|
|
|
@@ -1176,11 +1261,11 @@ class FlaskASTOpenAPI:
|
|
|
1176
1261
|
schema = response_schemas.get(status_code)
|
|
1177
1262
|
|
|
1178
1263
|
if schema:
|
|
1179
|
-
response["content"] = {
|
|
1180
|
-
|
|
1181
|
-
"schema": schema,
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1264
|
+
response["content"] = {
|
|
1265
|
+
content_type: {
|
|
1266
|
+
"schema": schema,
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1184
1269
|
|
|
1185
1270
|
responses[str(status_code)] = response
|
|
1186
1271
|
|
flask_ast_openapi-0.2.0/README.md → flask_ast_openapi-0.2.1/src/flask_ast_openapi.egg-info/PKG-INFO
RENAMED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flask-ast-openapi
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Generate OpenAPI specifications from Flask source code using AST
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: Flask>=3.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
1
13
|
# flask-ast-openapi
|
|
2
14
|
|
|
3
15
|
Generate OpenAPI 3 specifications from Flask source code using Python's Abstract Syntax Tree (AST).
|
|
@@ -19,10 +31,10 @@ It also provides optional Swagger UI integration for Flask applications.
|
|
|
19
31
|
* `@app.put(...)`
|
|
20
32
|
* `@app.patch(...)`
|
|
21
33
|
* `@app.delete(...)`
|
|
22
|
-
* Blueprint route support
|
|
23
|
-
* Blueprint `url_prefix` support
|
|
24
|
-
* Nested and cross-file Blueprint registration support
|
|
25
|
-
* Automatic Blueprint tags for Swagger UI grouping
|
|
34
|
+
* Blueprint route support
|
|
35
|
+
* Blueprint `url_prefix` support
|
|
36
|
+
* Nested and cross-file Blueprint registration support
|
|
37
|
+
* Automatic Blueprint tags for Swagger UI grouping
|
|
26
38
|
* Flask path parameter conversion to OpenAPI format
|
|
27
39
|
* Flask converter support:
|
|
28
40
|
|
|
@@ -41,8 +53,10 @@ It also provides optional Swagger UI integration for Flask applications.
|
|
|
41
53
|
* Function docstrings as OpenAPI descriptions
|
|
42
54
|
* Synchronous and asynchronous Flask route support
|
|
43
55
|
* Marshmallow request schema support
|
|
44
|
-
* Marshmallow response schema support
|
|
45
|
-
* Cross-file Marshmallow schema resolution
|
|
56
|
+
* Marshmallow response schema support
|
|
57
|
+
* Cross-file Marshmallow schema resolution
|
|
58
|
+
* Automatic request content-type detection for JSON and multipart forms
|
|
59
|
+
* Request and response content-type docstring overrides
|
|
46
60
|
* Nested Marshmallow schema support
|
|
47
61
|
* OpenAPI component schema generation
|
|
48
62
|
* Configurable authentication decorators
|
|
@@ -419,6 +433,23 @@ def create_user():
|
|
|
419
433
|
...
|
|
420
434
|
```
|
|
421
435
|
|
|
436
|
+
JSON bodies are detected from `request.get_json()` and `request.json`.
|
|
437
|
+
Multipart bodies are detected from `request.form` and `request.files`.
|
|
438
|
+
For custom media types, use docstring directives:
|
|
439
|
+
|
|
440
|
+
```python
|
|
441
|
+
@app.post("/documents")
|
|
442
|
+
def upload_document():
|
|
443
|
+
"""
|
|
444
|
+
:request: DocumentUploadSchema
|
|
445
|
+
:response: DocumentResponseSchema
|
|
446
|
+
:request-content-type: multipart/form-data
|
|
447
|
+
:response-content-type: application/json
|
|
448
|
+
"""
|
|
449
|
+
|
|
450
|
+
...
|
|
451
|
+
```
|
|
452
|
+
|
|
422
453
|
The generator detects `UserRequestSchema` and creates an OpenAPI request-body schema.
|
|
423
454
|
|
|
424
455
|
## Response Schemas
|
|
@@ -770,15 +801,15 @@ Future development may include:
|
|
|
770
801
|
|
|
771
802
|
## Version
|
|
772
803
|
|
|
773
|
-
Current version:
|
|
774
|
-
|
|
775
|
-
```text
|
|
776
|
-
0.2.
|
|
777
|
-
```
|
|
778
|
-
|
|
779
|
-
Version `0.2.
|
|
780
|
-
|
|
781
|
-
|
|
804
|
+
Current version:
|
|
805
|
+
|
|
806
|
+
```text
|
|
807
|
+
0.2.1
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
Version `0.2.1` adds automatic JSON and multipart request content-type
|
|
811
|
+
detection, explicit request/response media-type overrides, and avoids creating
|
|
812
|
+
request bodies for header-only and path-only inputs.
|
|
782
813
|
|
|
783
814
|
## License
|
|
784
815
|
|
|
@@ -6405,3 +6405,89 @@ def create_user():
|
|
|
6405
6405
|
response_schema = operation["responses"]["200"]["content"]["application/json"]["schema"]
|
|
6406
6406
|
assert request_schema["properties"]["name"] == {"type": "string"}
|
|
6407
6407
|
assert response_schema["properties"]["id"] == {"type": "integer"}
|
|
6408
|
+
|
|
6409
|
+
|
|
6410
|
+
def test_generate_infers_multipart_form_data(tmp_path):
|
|
6411
|
+
(tmp_path / "routes.py").write_text(
|
|
6412
|
+
"""
|
|
6413
|
+
from flask import Blueprint, request
|
|
6414
|
+
from marshmallow import Schema, fields
|
|
6415
|
+
|
|
6416
|
+
class UploadSchema(Schema):
|
|
6417
|
+
title = fields.String(required=True)
|
|
6418
|
+
file = fields.Raw(required=True)
|
|
6419
|
+
|
|
6420
|
+
upload_bp = Blueprint("upload", __name__)
|
|
6421
|
+
|
|
6422
|
+
@upload_bp.route("/upload", methods=["POST"])
|
|
6423
|
+
def upload():
|
|
6424
|
+
\"\"\":request: UploadSchema\"\"\"
|
|
6425
|
+
title = request.form.get("title")
|
|
6426
|
+
file = request.files.get("file")
|
|
6427
|
+
return {}, 200
|
|
6428
|
+
""",
|
|
6429
|
+
encoding="utf-8",
|
|
6430
|
+
)
|
|
6431
|
+
|
|
6432
|
+
spec = FlaskASTOpenAPI(tmp_path).generate()
|
|
6433
|
+
content = spec["paths"]["/upload"]["post"]["requestBody"]["content"]
|
|
6434
|
+
|
|
6435
|
+
assert "multipart/form-data" in content
|
|
6436
|
+
assert "application/json" not in content
|
|
6437
|
+
|
|
6438
|
+
|
|
6439
|
+
def test_generate_does_not_create_body_for_header_only_schema(tmp_path):
|
|
6440
|
+
(tmp_path / "routes.py").write_text(
|
|
6441
|
+
"""
|
|
6442
|
+
from flask import Blueprint, request
|
|
6443
|
+
from marshmallow import Schema, fields
|
|
6444
|
+
|
|
6445
|
+
class TokenSchema(Schema):
|
|
6446
|
+
token = fields.String(required=True)
|
|
6447
|
+
|
|
6448
|
+
auth_bp = Blueprint("auth", __name__)
|
|
6449
|
+
|
|
6450
|
+
@auth_bp.route("/validate", methods=["POST"])
|
|
6451
|
+
def validate():
|
|
6452
|
+
\"\"\":request: TokenSchema\"\"\"
|
|
6453
|
+
token = request.headers.get("Authorization")
|
|
6454
|
+
return {}, 200
|
|
6455
|
+
""",
|
|
6456
|
+
encoding="utf-8",
|
|
6457
|
+
)
|
|
6458
|
+
|
|
6459
|
+
spec = FlaskASTOpenAPI(tmp_path).generate()
|
|
6460
|
+
|
|
6461
|
+
assert "requestBody" not in spec["paths"]["/validate"]["post"]
|
|
6462
|
+
|
|
6463
|
+
|
|
6464
|
+
def test_docstring_can_override_request_and_response_content_types(tmp_path):
|
|
6465
|
+
(tmp_path / "routes.py").write_text(
|
|
6466
|
+
"""
|
|
6467
|
+
from flask import Blueprint, request
|
|
6468
|
+
from marshmallow import Schema, fields
|
|
6469
|
+
|
|
6470
|
+
class PayloadSchema(Schema):
|
|
6471
|
+
value = fields.String(required=True)
|
|
6472
|
+
|
|
6473
|
+
bp = Blueprint("custom", __name__)
|
|
6474
|
+
|
|
6475
|
+
@bp.route("/custom", methods=["POST"])
|
|
6476
|
+
def custom():
|
|
6477
|
+
\"\"\"
|
|
6478
|
+
:request: PayloadSchema
|
|
6479
|
+
:response: PayloadSchema
|
|
6480
|
+
:request-content-type: application/x-www-form-urlencoded
|
|
6481
|
+
:response-content-type: application/problem+json
|
|
6482
|
+
\"\"\"
|
|
6483
|
+
value = request.form.get("value")
|
|
6484
|
+
return {"value": value}, 200
|
|
6485
|
+
""",
|
|
6486
|
+
encoding="utf-8",
|
|
6487
|
+
)
|
|
6488
|
+
|
|
6489
|
+
spec = FlaskASTOpenAPI(tmp_path).generate()
|
|
6490
|
+
operation = spec["paths"]["/custom"]["post"]
|
|
6491
|
+
|
|
6492
|
+
assert "application/x-www-form-urlencoded" in operation["requestBody"]["content"]
|
|
6493
|
+
assert "application/problem+json" in operation["responses"]["200"]["content"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi.egg-info/requires.txt
RENAMED
|
File without changes
|
{flask_ast_openapi-0.2.0 → flask_ast_openapi-0.2.1}/src/flask_ast_openapi.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|