Skip to content

Commit c6653b7

Browse files
authored
Merge pull request #12 from mike-oakley/u/mike/infer-schema-version
feat: Support parsing with schema version inference.
2 parents 4b9c873 + d4f3905 commit c6653b7

File tree

10 files changed

+77
-9
lines changed

10 files changed

+77
-9
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"python.testing.pytestArgs": [
3-
"tests"
3+
"tests",
4+
"-vv"
45
],
56
"python.testing.unittestEnabled": false,
67
"python.testing.pytestEnabled": true,

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,20 @@ Pydantic is a great tool, allow you to use object / dict / mixed data for for in
7474
The following examples give the same OpenAPI result as above:
7575

7676
```python
77-
from openapi_pydantic import OpenAPI, PathItem, Response
77+
from openapi_pydantic import parse_obj, OpenAPI, PathItem, Response
7878

79-
# Construct OpenAPI from dict
79+
# Construct OpenAPI from dict, inferring the correct schema version
80+
open_api = parse_obj({
81+
"info": {"title": "My own API", "version": "v0.0.1"},
82+
"paths": {
83+
"/ping": {
84+
"get": {"responses": {"200": {"description": "pong"}}}
85+
}
86+
},
87+
})
88+
89+
90+
# Construct OpenAPI v3.1.0 schema from dict
8091
open_api = OpenAPI.parse_obj({
8192
"info": {"title": "My own API", "version": "v0.0.1"},
8293
"paths": {

openapi_pydantic/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,6 @@
3131
from .v3 import Server as Server
3232
from .v3 import ServerVariable as ServerVariable
3333
from .v3 import Tag as Tag
34+
from .v3 import parse_obj as parse_obj
3435

3536
logging.getLogger(__name__).addHandler(logging.NullHandler())

openapi_pydantic/v3/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from .parser import parse_obj as parse_obj
12
from .v3_1_0 import XML as XML
23
from .v3_1_0 import Callback as Callback
34
from .v3_1_0 import Components as Components

openapi_pydantic/v3/parser.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import Any, Union
2+
3+
from pydantic import BaseModel, Field
4+
5+
from .v3_0_3 import OpenAPI as OpenAPIv3_0
6+
from .v3_1_0 import OpenAPI as OpenAPIv3_1
7+
8+
9+
class _OpenAPI(BaseModel):
10+
__root__: Union[OpenAPIv3_1, OpenAPIv3_0] = Field(discriminator="openapi")
11+
12+
13+
def parse_obj(data: Any) -> Union[OpenAPIv3_1, OpenAPIv3_0]:
14+
"""Parse a raw object into an OpenAPI model with version inference."""
15+
return _OpenAPI.parse_obj(data).__root__

openapi_pydantic/v3/v3_0_3/open_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List, Optional
1+
from typing import List, Literal, Optional
22

33
from pydantic import BaseModel, Extra
44

@@ -14,7 +14,7 @@
1414
class OpenAPI(BaseModel):
1515
"""This is the root document object of the OpenAPI document."""
1616

17-
openapi: str = "3.0.3"
17+
openapi: Literal["3.0.3", "3.0.2", "3.0.1", "3.0.0"] = "3.0.3"
1818
"""
1919
**REQUIRED**. This string MUST be the [semantic version number](https://semver.org/spec/v2.0.0.html)
2020
of the [OpenAPI Specification version](#versions) that the OpenAPI document uses.

openapi_pydantic/v3/v3_1_0/open_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict, List, Optional, Union
1+
from typing import Dict, List, Literal, Optional, Union
22

33
from pydantic import BaseModel, Extra
44

@@ -16,7 +16,7 @@
1616
class OpenAPI(BaseModel):
1717
"""This is the root document object of the OpenAPI document."""
1818

19-
openapi: str = "3.1.0"
19+
openapi: Literal["3.1.0"] = "3.1.0"
2020
"""
2121
**REQUIRED**. This string MUST be the [version number](#versions)
2222
of the OpenAPI Specification that the OpenAPI document uses.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "openapi-pydantic"
3-
version = "0.1.1"
3+
version = "0.2.0"
44
description = "Pydantic OpenAPI schema implementation"
55
authors = ["Mike Oakley <[email protected]>"]
66
readme = "README.md"

tests/test_parse.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from typing import Literal
2+
3+
import pytest
4+
5+
from openapi_pydantic import parse_obj
6+
from openapi_pydantic.v3 import v3_0_3, v3_1_0
7+
8+
9+
@pytest.mark.parametrize("version", ["3.0.3", "3.0.2", "3.0.1", "3.0.0"])
10+
def test_parse_obj_3_0_3(version: Literal["3.0.3", "3.0.2", "3.0.1", "3.0.0"]) -> None:
11+
result = parse_obj(
12+
{
13+
"openapi": version,
14+
"info": {"title": "foo", "version": "0.1.0"},
15+
"paths": {"/": {}},
16+
}
17+
)
18+
19+
assert result == v3_0_3.OpenAPI(
20+
openapi=version,
21+
info=v3_0_3.Info(title="foo", version="0.1.0"),
22+
paths={"/": v3_0_3.PathItem()},
23+
)
24+
25+
26+
def test_parse_obj_3_1_0() -> None:
27+
result = parse_obj(
28+
{
29+
"openapi": "3.1.0",
30+
"info": {"title": "foo", "version": "0.1.0"},
31+
"paths": {"/": {}},
32+
}
33+
)
34+
35+
assert result == v3_1_0.OpenAPI(
36+
openapi="3.1.0",
37+
info=v3_1_0.Info(title="foo", version="0.1.0"),
38+
paths={"/": v3_1_0.PathItem()},
39+
)

tests/test_swagger_openapi_v3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from pydantic import Field
44

5-
from openapi_pydantic import OpenAPI, Operation, PathItem
5+
from openapi_pydantic.v3.v3_0_3 import OpenAPI, Operation, PathItem
66

77

88
def test_swagger_openapi_v3() -> None:

0 commit comments

Comments
 (0)