# openapi-pydantic
[![PyPI](https://img.shields.io/pypi/v/openapi-pydantic)](https://pypi.org/project/openapi-pydantic/)
[![PyPI - License](https://img.shields.io/pypi/l/openapi-pydantic)](https://github.com/mike-oakley/openapi-pydantic/blob/main/LICENSE)
OpenAPI schema implemented in [Pydantic](https://github.com/samuelcolvin/pydantic). Both Pydantic 1.8+ and 2.x are supported.
The naming of the classes follows the schema in
[OpenAPI specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.1.md#schema).
> This library is forked from [OpenAPI Schema Pydantic](https://github.com/kuimono/openapi-schema-pydantic) (at version [1.2.4](https://github.com/kuimono/openapi-schema-pydantic/releases/tag/v1.2.4)) which is no longer actively maintained.
## Installation
`pip install openapi-pydantic`
## Try me
```python
from openapi_pydantic import OpenAPI, Info, PathItem, Operation, Response
# Construct OpenAPI by pydantic objects
open_api = OpenAPI(
info=Info(
title="My own API",
version="v0.0.1",
),
paths={
"/ping": PathItem(
get=Operation(
responses={
"200": Response(
description="pong"
)
}
)
)
},
)
# Note: for Pydantic 1.x, replace `model_dump_json` with `json`
print(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2))
```
Result:
```json
{
"openapi": "3.1.1",
"info": {
"title": "My own API",
"version": "v0.0.1"
},
"servers": [
{
"url": "/"
}
],
"paths": {
"/ping": {
"get": {
"responses": {
"200": {
"description": "pong"
}
},
"deprecated": false
}
}
}
}
```
## Take advantage of Pydantic
Pydantic is a great tool. It allows you to use object / dict / mixed data for input.
The following examples give the same OpenAPI result as above:
```python
from openapi_pydantic import parse_obj, OpenAPI, PathItem, Response
# Construct OpenAPI from dict, inferring the correct schema version
open_api = parse_obj({
"openapi": "3.1.1",
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {
"/ping": {
"get": {"responses": {"200": {"description": "pong"}}}
}
},
})
# Construct OpenAPI v3.1 schema from dict
# Note: for Pydantic 1.x, replace `model_validate` with `parse_obj`
open_api = OpenAPI.model_validate({
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {
"/ping": {
"get": {"responses": {"200": {"description": "pong"}}}
}
},
})
# Construct OpenAPI with mix of dict/object
# Note: for Pydantic 1.x, replace `model_validate` with `parse_obj`
open_api = OpenAPI.model_validate({
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {
"/ping": PathItem(
get={"responses": {"200": Response(description="pong")}}
)
},
})
```
## Use Pydantic classes as schema
- The [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.4.md#schemaObject)
in OpenAPI has definitions and tweaks in JSON Schema, which are hard to comprehend and define a good data class
- Pydantic already has a good way to [create JSON schema](https://pydantic-docs.helpmanual.io/usage/schema/).
Let's not reinvent the wheel.
The approach to deal with this:
1. Use `PydanticSchema` objects to represent the `Schema` in `OpenAPI` object
2. Invoke `construct_open_api_with_schema_class` to resolve the JSON schemas and references
```python
from pydantic import BaseModel, Field
from openapi_pydantic import OpenAPI
from openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
def construct_base_open_api() -> OpenAPI:
# Note: for Pydantic 1.x, replace `model_validate` with `parse_obj`
return OpenAPI.model_validate({
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {
"/ping": {
"post": {
"requestBody": {"content": {"application/json": {
"schema": PydanticSchema(schema_class=PingRequest)
}}},
"responses": {"200": {
"description": "pong",
"content": {"application/json": {
"schema": PydanticSchema(schema_class=PingResponse)
}},
}},
}
}
},
})
class PingRequest(BaseModel):
"""Ping Request"""
req_foo: str = Field(description="foo value of the request")
req_bar: str = Field(description="bar value of the request")
class PingResponse(BaseModel):
"""Ping response"""
resp_foo: str = Field(description="foo value of the response")
resp_bar: str = Field(description="bar value of the response")
open_api = construct_base_open_api()
open_api = construct_open_api_with_schema_class(open_api)
# print the result openapi.json
# Note: for Pydantic 1.x, replace `model_dump_json` with `json`
print(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2))
```
Result:
```json
{
"openapi": "3.1.1",
"info": {
"title": "My own API",
"version": "v0.0.1"
},
"servers": [
{
"url": "/"
}
],
"paths": {
"/ping": {
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PingRequest"
}
}
},
"required": false
},
"responses": {
"200": {
"description": "pong",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PingResponse"
}
}
}
}
},
"deprecated": false
}
}
},
"components": {
"schemas": {
"PingRequest": {
"title": "PingRequest",
"required": [
"req_foo",
"req_bar"
],
"type": "object",
"properties": {
"req_foo": {
"title": "Req Foo",
"type": "string",
"description": "foo value of the request"
},
"req_bar": {
"title": "Req Bar",
"type": "string",
"description": "bar value of the request"
}
},
"description": "Ping Request"
},
"PingResponse": {
"title": "PingResponse",
"required": [
"resp_foo",
"resp_bar"
],
"type": "object",
"properties": {
"resp_foo": {
"title": "Resp Foo",
"type": "string",
"description": "foo value of the response"
},
"resp_bar": {
"title": "Resp Bar",
"type": "string",
"description": "bar value of the response"
}
},
"description": "Ping response"
}
}
}
}
```
## Notes
### Use of OpenAPI.model_dump() / OpenAPI.model_dump_json() / OpenAPI.json() / OpenAPI.dict()
When using `OpenAPI.model_dump()` / `OpenAPI.model_dump_json()` / `OpenAPI.json()` / `OpenAPI.dict()` functions,
the arguments `by_alias=True, exclude_none=True` have to be in place.
Otherwise the resulting json will not fit the OpenAPI standard.
```python
# OK (Pydantic 2)
open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2)
# OK (Pydantic 1)
open_api.json(by_alias=True, exclude_none=True, indent=2)
# Not good
open_api.model_dump_json(indent=2)
open_api.json(indent=2)
```
More info about field aliases:
| OpenAPI version | Field alias info |
| --------------- | ---------------- |
| 3.1 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_1/README.md#alias) |
| 3.0 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_0/README.md#alias) |
### Non-pydantic schema types
Some schema types are not implemented as pydantic classes.
Please refer to the following for more info:
| OpenAPI version | Non-pydantic schema type info |
| --------------- | ----------------------------- |
| 3.1 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_1/README.md#non-pydantic-schema-types) |
| 3.0 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_0/README.md#non-pydantic-schema-types) |
### Use OpenAPI 3.0 instead of 3.1
Some UI renderings (e.g. Swagger) still do not support OpenAPI 3.1.x.
The old 3.0.x version is available by importing from different paths:
```python
from openapi_pydantic.v3.v3_0 import OpenAPI, ...
from openapi_pydantic.v3.v3_0.util import PydanticSchema, construct_open_api_with_schema_class
```
### Pydantic version compatibility
Compatibility with both major versions of Pydantic (1.8+ and 2.*) is mostly achieved using a module called `compat.py`. It detects the installed version of Pydantic and exports version-specific symbols for use by the rest of the package. It also provides all symbols necessary for type checking. The `compat.py` module is not intended to be imported by other packages, but other packages may find it helpful as an example of how to span major versions of Pydantic.
## Credits
This library is based from the original implementation by Kuimono of [OpenAPI Schema Pydantic](https://github.com/kuimono/openapi-schema-pydantic) which is no longer actively maintained.
## License
[MIT License](https://github.com/mike-oakley/openapi-pydantic/blob/main/LICENSE)
Raw data
{
"_id": null,
"home_page": "https://github.com/mike-oakley/openapi-pydantic",
"name": "openapi-pydantic",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.8",
"maintainer_email": null,
"keywords": "openapi, schema, parser, pydantic, validation",
"author": "Mike Oakley",
"author_email": "mike-oakley@users.noreply.github.com",
"download_url": "https://files.pythonhosted.org/packages/24/83/c6dd05cd518e1217b2096d04f959d7868396a3a99faf9669d14007668c38/openapi_pydantic-0.5.0.tar.gz",
"platform": null,
"description": "# openapi-pydantic\n\n[![PyPI](https://img.shields.io/pypi/v/openapi-pydantic)](https://pypi.org/project/openapi-pydantic/)\n[![PyPI - License](https://img.shields.io/pypi/l/openapi-pydantic)](https://github.com/mike-oakley/openapi-pydantic/blob/main/LICENSE)\n\nOpenAPI schema implemented in [Pydantic](https://github.com/samuelcolvin/pydantic). Both Pydantic 1.8+ and 2.x are supported.\n\nThe naming of the classes follows the schema in \n[OpenAPI specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.1.md#schema).\n\n> This library is forked from [OpenAPI Schema Pydantic](https://github.com/kuimono/openapi-schema-pydantic) (at version [1.2.4](https://github.com/kuimono/openapi-schema-pydantic/releases/tag/v1.2.4)) which is no longer actively maintained.\n\n## Installation\n\n`pip install openapi-pydantic`\n\n## Try me\n\n```python\nfrom openapi_pydantic import OpenAPI, Info, PathItem, Operation, Response\n\n# Construct OpenAPI by pydantic objects\nopen_api = OpenAPI(\n info=Info(\n title=\"My own API\",\n version=\"v0.0.1\",\n ),\n paths={\n \"/ping\": PathItem(\n get=Operation(\n responses={\n \"200\": Response(\n description=\"pong\"\n )\n }\n )\n )\n },\n)\n# Note: for Pydantic 1.x, replace `model_dump_json` with `json`\nprint(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2))\n```\n\nResult:\n\n```json\n{\n \"openapi\": \"3.1.1\",\n \"info\": {\n \"title\": \"My own API\",\n \"version\": \"v0.0.1\"\n },\n \"servers\": [\n {\n \"url\": \"/\"\n }\n ],\n \"paths\": {\n \"/ping\": {\n \"get\": {\n \"responses\": {\n \"200\": {\n \"description\": \"pong\"\n }\n },\n \"deprecated\": false\n }\n }\n }\n}\n```\n\n## Take advantage of Pydantic\n\nPydantic is a great tool. It allows you to use object / dict / mixed data for input.\n\nThe following examples give the same OpenAPI result as above:\n\n```python\nfrom openapi_pydantic import parse_obj, OpenAPI, PathItem, Response\n\n# Construct OpenAPI from dict, inferring the correct schema version\nopen_api = parse_obj({\n \"openapi\": \"3.1.1\",\n \"info\": {\"title\": \"My own API\", \"version\": \"v0.0.1\"},\n \"paths\": {\n \"/ping\": {\n \"get\": {\"responses\": {\"200\": {\"description\": \"pong\"}}}\n }\n },\n})\n\n\n# Construct OpenAPI v3.1 schema from dict\n# Note: for Pydantic 1.x, replace `model_validate` with `parse_obj`\nopen_api = OpenAPI.model_validate({\n \"info\": {\"title\": \"My own API\", \"version\": \"v0.0.1\"},\n \"paths\": {\n \"/ping\": {\n \"get\": {\"responses\": {\"200\": {\"description\": \"pong\"}}}\n }\n },\n})\n\n# Construct OpenAPI with mix of dict/object\n# Note: for Pydantic 1.x, replace `model_validate` with `parse_obj`\nopen_api = OpenAPI.model_validate({\n \"info\": {\"title\": \"My own API\", \"version\": \"v0.0.1\"},\n \"paths\": {\n \"/ping\": PathItem(\n get={\"responses\": {\"200\": Response(description=\"pong\")}}\n )\n },\n})\n```\n\n## Use Pydantic classes as schema\n\n- The [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.4.md#schemaObject)\n in OpenAPI has definitions and tweaks in JSON Schema, which are hard to comprehend and define a good data class\n- Pydantic already has a good way to [create JSON schema](https://pydantic-docs.helpmanual.io/usage/schema/).\n Let's not reinvent the wheel.\n\nThe approach to deal with this:\n\n1. Use `PydanticSchema` objects to represent the `Schema` in `OpenAPI` object\n2. Invoke `construct_open_api_with_schema_class` to resolve the JSON schemas and references\n\n```python\nfrom pydantic import BaseModel, Field\n\nfrom openapi_pydantic import OpenAPI\nfrom openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class\n\ndef construct_base_open_api() -> OpenAPI:\n # Note: for Pydantic 1.x, replace `model_validate` with `parse_obj`\n return OpenAPI.model_validate({\n \"info\": {\"title\": \"My own API\", \"version\": \"v0.0.1\"},\n \"paths\": {\n \"/ping\": {\n \"post\": {\n \"requestBody\": {\"content\": {\"application/json\": {\n \"schema\": PydanticSchema(schema_class=PingRequest)\n }}},\n \"responses\": {\"200\": {\n \"description\": \"pong\",\n \"content\": {\"application/json\": {\n \"schema\": PydanticSchema(schema_class=PingResponse)\n }},\n }},\n }\n }\n },\n })\n\nclass PingRequest(BaseModel):\n \"\"\"Ping Request\"\"\"\n req_foo: str = Field(description=\"foo value of the request\")\n req_bar: str = Field(description=\"bar value of the request\")\n\nclass PingResponse(BaseModel):\n \"\"\"Ping response\"\"\"\n resp_foo: str = Field(description=\"foo value of the response\")\n resp_bar: str = Field(description=\"bar value of the response\")\n\nopen_api = construct_base_open_api()\nopen_api = construct_open_api_with_schema_class(open_api)\n\n# print the result openapi.json\n# Note: for Pydantic 1.x, replace `model_dump_json` with `json`\nprint(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2))\n```\n\nResult:\n\n```json\n{\n \"openapi\": \"3.1.1\",\n \"info\": {\n \"title\": \"My own API\",\n \"version\": \"v0.0.1\"\n },\n \"servers\": [\n {\n \"url\": \"/\"\n }\n ],\n \"paths\": {\n \"/ping\": {\n \"post\": {\n \"requestBody\": {\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/PingRequest\"\n }\n }\n },\n \"required\": false\n },\n \"responses\": {\n \"200\": {\n \"description\": \"pong\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/PingResponse\"\n }\n }\n }\n }\n },\n \"deprecated\": false\n }\n }\n },\n \"components\": {\n \"schemas\": {\n \"PingRequest\": {\n \"title\": \"PingRequest\",\n \"required\": [\n \"req_foo\",\n \"req_bar\"\n ],\n \"type\": \"object\",\n \"properties\": {\n \"req_foo\": {\n \"title\": \"Req Foo\",\n \"type\": \"string\",\n \"description\": \"foo value of the request\"\n },\n \"req_bar\": {\n \"title\": \"Req Bar\",\n \"type\": \"string\",\n \"description\": \"bar value of the request\"\n }\n },\n \"description\": \"Ping Request\"\n },\n \"PingResponse\": {\n \"title\": \"PingResponse\",\n \"required\": [\n \"resp_foo\",\n \"resp_bar\"\n ],\n \"type\": \"object\",\n \"properties\": {\n \"resp_foo\": {\n \"title\": \"Resp Foo\",\n \"type\": \"string\",\n \"description\": \"foo value of the response\"\n },\n \"resp_bar\": {\n \"title\": \"Resp Bar\",\n \"type\": \"string\",\n \"description\": \"bar value of the response\"\n }\n },\n \"description\": \"Ping response\"\n }\n }\n }\n}\n```\n\n## Notes\n\n### Use of OpenAPI.model_dump() / OpenAPI.model_dump_json() / OpenAPI.json() / OpenAPI.dict()\n\nWhen using `OpenAPI.model_dump()` / `OpenAPI.model_dump_json()` / `OpenAPI.json()` / `OpenAPI.dict()` functions,\nthe arguments `by_alias=True, exclude_none=True` have to be in place.\nOtherwise the resulting json will not fit the OpenAPI standard.\n\n```python\n# OK (Pydantic 2)\nopen_api.model_dump_json(by_alias=True, exclude_none=True, indent=2)\n# OK (Pydantic 1)\nopen_api.json(by_alias=True, exclude_none=True, indent=2)\n\n# Not good\nopen_api.model_dump_json(indent=2)\nopen_api.json(indent=2)\n```\n\nMore info about field aliases:\n\n| OpenAPI version | Field alias info |\n| --------------- | ---------------- |\n| 3.1 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_1/README.md#alias) |\n| 3.0 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_0/README.md#alias) |\n\n### Non-pydantic schema types\n\nSome schema types are not implemented as pydantic classes.\nPlease refer to the following for more info:\n\n| OpenAPI version | Non-pydantic schema type info |\n| --------------- | ----------------------------- |\n| 3.1 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_1/README.md#non-pydantic-schema-types) |\n| 3.0 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_0/README.md#non-pydantic-schema-types) |\n\n### Use OpenAPI 3.0 instead of 3.1\n\nSome UI renderings (e.g. Swagger) still do not support OpenAPI 3.1.x.\nThe old 3.0.x version is available by importing from different paths:\n\n```python\nfrom openapi_pydantic.v3.v3_0 import OpenAPI, ...\nfrom openapi_pydantic.v3.v3_0.util import PydanticSchema, construct_open_api_with_schema_class\n```\n\n### Pydantic version compatibility\n\nCompatibility with both major versions of Pydantic (1.8+ and 2.*) is mostly achieved using a module called `compat.py`. It detects the installed version of Pydantic and exports version-specific symbols for use by the rest of the package. It also provides all symbols necessary for type checking. The `compat.py` module is not intended to be imported by other packages, but other packages may find it helpful as an example of how to span major versions of Pydantic.\n\n## Credits\n\nThis library is based from the original implementation by Kuimono of [OpenAPI Schema Pydantic](https://github.com/kuimono/openapi-schema-pydantic) which is no longer actively maintained.\n\n## License\n\n[MIT License](https://github.com/mike-oakley/openapi-pydantic/blob/main/LICENSE)\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Pydantic OpenAPI schema implementation",
"version": "0.5.0",
"project_urls": {
"Homepage": "https://github.com/mike-oakley/openapi-pydantic",
"Repository": "https://github.com/mike-oakley/openapi-pydantic",
"changelog": "https://github.com/mike-oakley/openapi-pydantic/releases"
},
"split_keywords": [
"openapi",
" schema",
" parser",
" pydantic",
" validation"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "8b8187f16c4bf2e73ea337b5bb1e074e3ddbdf3e5aabaaf3f8551aad6a9ac9fa",
"md5": "6e86bedf12a67372be5a693afbc729c6",
"sha256": "06458efd34969446f42d96d51de39cdef4a9b19daf3cc456a2dfa697458ac542"
},
"downloads": -1,
"filename": "openapi_pydantic-0.5.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6e86bedf12a67372be5a693afbc729c6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.8",
"size": 95858,
"upload_time": "2024-11-04T22:50:44",
"upload_time_iso_8601": "2024-11-04T22:50:44.359781Z",
"url": "https://files.pythonhosted.org/packages/8b/81/87f16c4bf2e73ea337b5bb1e074e3ddbdf3e5aabaaf3f8551aad6a9ac9fa/openapi_pydantic-0.5.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "2483c6dd05cd518e1217b2096d04f959d7868396a3a99faf9669d14007668c38",
"md5": "2f404beb6df1d9e22c992ef5d35e1c0e",
"sha256": "a48f88e2904a056e1ef6d4728cfb2f36aa3213ce194fb09fc04259b9007165f0"
},
"downloads": -1,
"filename": "openapi_pydantic-0.5.0.tar.gz",
"has_sig": false,
"md5_digest": "2f404beb6df1d9e22c992ef5d35e1c0e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.8",
"size": 60403,
"upload_time": "2024-11-04T22:50:45",
"upload_time_iso_8601": "2024-11-04T22:50:45.981883Z",
"url": "https://files.pythonhosted.org/packages/24/83/c6dd05cd518e1217b2096d04f959d7868396a3a99faf9669d14007668c38/openapi_pydantic-0.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-04 22:50:45",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mike-oakley",
"github_project": "openapi-pydantic",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "openapi-pydantic"
}