Skip to main content

openapi specification parser based on pydantic

Project description

Openapydantic

openapi specification validator based on pydantic.

Python version support

3.8+

Openapi versions support

  • ❌ 2.0
  • 🟠 3.0.0
  • 🟠 3.0.1
  • ✅ 3.0.2
  • ❌ 3.0.3
  • ❌ 3.1.0

Openapi versions are retrocompatibles (except for major version).

So 3.0.2 specification should be able to handle 3.0.0 and 3.0.1 data.

Unit tests handle this case (3.0.2 object automatically try to load previous version fixtures).

Installation

Depending on your preference...

    pip install openapydantic

...or...

    poetry add openapydantic

Basic usage

Api loader

Openapydantic provide an openapi specification (a.k.a "swagger file") loader.

This loader returns a pydantic model so you can work with your specification like a common pydantic python object.

For each openapi specification version, there's a dedicated python class.

The loader can either automatically determine the class to provide...

import asyncio

import openapydantic

api = asyncio.run(
    openapydantic.load_api(
        file_path="openapi-spec.yaml",
    ),
)
print(api.info)
# if my openapi version is "3.0.2", 'api' is an instance of OpenApi302
# if the version is not implemented, it will crash

... or you can also specify a specific version.

It may be useful for backward compatibility (for eg: create an OpenApi302 object using data from an 3.0.1 openapi specfication ).

import asyncio

import openapydantic

OpenApiVersion = openapydantic.OpenApiVersion

api = asyncio.run(
    openapydantic.load_api(
        file_path="openapi-spec-3-0-1.yaml",
        version=OpenApiVersion.v3_0_2
    ),
)
# Here ,'api' is an OpenApi302 object, event if you send an 3.0.1 spec.

print(api.openapi)
>> 3.0.1 # version in the spec file
print(api.__version__)
>> 3.0.2 # openapi version supported for the object class

Reference interpolation

Openapydantic will interpolate openapi references.

If your api looks like this:

# my-api.yaml
openapi: 3.0.2
info:
  version: "1.0.0"
  title: Example
paths:
  /user:
    get:
      summary: Get user
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
          example: "John Doe"

Once loaded, it will be usable like if it was ...

openapi: 3.0.2
info:
  version: "1.0.0"
  title: Example
paths:
  /user:
    get:
      summary: Get user
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    format: int64
                  name:
                    type: string
                    example: "John Doe"

And so you will be able to do things like...

import asyncio

import openapydantic

api = asyncio.run(
    openapydantic.load_api(
        file_path="my-api.yaml",
    ),
)
print(api.info)
print(
    api.paths["/user"]
    .get.responses["200"]
    .content["application/json"]
    .schema_.properties["name"]
    .example
)
>> John Doe

As describe in the openapi specification some attributes are fix ('paths', 'content' etc...) and some can be mapping with a free key.

Mapping must be accessed like common dict, either by direct key loading, either using .get('key')

Note that file reference (e.g: "#/file.yaml" are currently not supported)

Reference that reference themself will not be interpolated so ...

# my-api.yaml
openapi: 3.0.2
info:
  version: "1.0.0"
  title: Example
paths:
  /user:
    get:
      summary: Get user
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
          example: "John Doe"
        brother:
          $ref: "#/components/schemas/User"

.. will stay the same ...

import asyncio

import openapydantic

api = asyncio.run(
    openapydantic.load_api(
        file_path="my-api.yaml",
    ),
)
print(api.components.schemas["User"].properties["brother"].ref)
>> '#/components/schemas/User'

Attributes name collision

Openapi specify some attribute which name are already reserved either by pydantic,either by the python language itself.

To access these attributes, you must use the Openapydantic specific name

Attribute name Openapydantic specific name
schema schema_
in in_
not not_

e.g:

print(
    api.paths["/user"]
    .get.responses["200"]
    .content["application/json"]
    .schema_
)

Model export

You can access the original api you provided as a dict using the raw_api attribute.

import asyncio

import openapydantic

api = asyncio.run(
    openapydantic.load_api(
        file_path="my-api.yaml",
    ),
)

print(api.raw_api)
>> {'openapi': '3.0.2', 'info': {'version': '1.0.0', 'title': 'Example'}, 'paths': {'/user': {'get': {'summary': 'Get user', 'responses': {'200': {'description': 'successful operation', 'content': {'application/json': {'schema': {'$ref': '#/components/schemas/User'}}}}}}}}, 'components': {'schemas': {'User': {'type': 'object', 'properties': {'id': {'type': 'integer', 'format': 'int64'}, 'name': {'type': 'string', 'example': 'John Doe'}}}}}}

You can export your data as json string or as python dict using specific methods:

import asyncio

import openapydantic

api = asyncio.run(
    openapydantic.load_api(
        file_path="my-api.yaml",
    ),
)

print(api.as_clean_json())
>> {"openapi": "3.0.2", "info": {"title": "Example", "version": "1.0.0"}, "paths": {"/user": {"get": {"summary": "Get user", "responses": {"200": {"description": "successful operation", "content": {"application/json": {"schema": {"type": "object", "properties": {"id": {"type": "integer", "format": "int64"}, "name": {"type": "string", "example": "John Doe"}}}}}}}}}}}


print(api.as_clean_dict())
> {'openapi': <OpenApiVersion.v3_0_2: '3.0.2'>, 'info': {'title': 'Example', 'version': '1.0.0'}, 'paths': {'/user': {'get': {'summary': 'Get user', 'responses': {'200': {'description': 'successful operation', 'content': {'application/json': {'schema': {'type': <JsonType.object_: 'object'>, 'properties': {'id': {'type': <JsonType.integer: 'integer'>, 'format': 'int64'}, 'name': {'type': <JsonType.string: 'string'>, 'example': 'John Doe'}}}}}}}}}}}

Note that these functions are just wrapper to .dict() and .json() pydantic model with specific parameters.

By default, since the references are interpolated, the components root key is exclude.

If you want to have it in the output, you can set the exclude_components parameter to False.

import asyncio

import openapydantic

api = asyncio.run(
    openapydantic.load_api(
        file_path="my-api.yaml",
    ),
)

print(
    api.as_clean_json(
        exclude_components=False,
    ),
)

>> {"components": {"schemas": {"User": {"type": "object", "properties": {"id": {"type": "integer", "format": "int64"}, "name": {"type": "string", "example": "John Doe"}}}}}, "openapi": "3.0.2", "info": {"title": "Example", "version": "1.0.0"}, "paths": {"/user": {"get": {"summary": "Get user", "responses": {"200": {"description": "successful operation", "content": {"application/json": {"schema": {"type": "object", "properties": {"id": {"type": "integer", "format": "int64"}, "name": {"type": "string", "example": "John Doe"}}}}}}}}}}, "raw_api": {"openapi": "3.0.2", "info": {"version": "1.0.0", "title": "Example"}, "paths": {"/user": {"get": {"summary": "Get user", "responses": {"200": {"description": "successful operation", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}}}}}, "components": {"schemas": {"User": {"type": "object", "properties": {"id": {"type": "integer", "format": "int64"}, "name": {"type": "string", "example": "John Doe"}}}}}}}

In the same way,the raw_api attribute is exclude by default.

If you want to have it in the output, you can set the exclude_raw_api parameter to False.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openapydantic-0.2.3.tar.gz (12.8 kB view details)

Uploaded Source

Built Distribution

openapydantic-0.2.3-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

Details for the file openapydantic-0.2.3.tar.gz.

File metadata

  • Download URL: openapydantic-0.2.3.tar.gz
  • Upload date:
  • Size: 12.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.8.10 Linux/5.10.76-linuxkit

File hashes

Hashes for openapydantic-0.2.3.tar.gz
Algorithm Hash digest
SHA256 89f8b3a79f413e8314573631af2ba761fa69eff5101a1e004491983a09e3006c
MD5 7aec4e8f313ff485c023f23d575d1a5b
BLAKE2b-256 6d45d4bdb125158ec5a9ac26d56bded1e785ba21a5070c3268e5f861757b65a9

See more details on using hashes here.

File details

Details for the file openapydantic-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: openapydantic-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 11.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.8.10 Linux/5.10.76-linuxkit

File hashes

Hashes for openapydantic-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 0f9d622b658125af142328d65b99ecc4fddbc7f13faf4b2a6493b4f54c4b3e2b
MD5 12ba3e5eea54dc1fbe92e8e17d722e1b
BLAKE2b-256 7651f691965eaae71269c9e9352d4a9dd01b81b75cca4afdf98d8ba936941fb3

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page