Source code for apispec.utils

"""Various utilities for parsing OpenAPI operations from docstrings and validating against
the OpenAPI spec.
"""

from __future__ import annotations

import re

COMPONENT_SUBSECTIONS = {
    2: {
        "schema": "definitions",
        "response": "responses",
        "parameter": "parameters",
        "security_scheme": "securityDefinitions",
    },
    3: {
        "schema": "schemas",
        "response": "responses",
        "parameter": "parameters",
        "header": "headers",
        "example": "examples",
        "security_scheme": "securitySchemes",
    },
}


[docs] def build_reference( component_type: str, openapi_major_version: int, component_name: str ) -> dict[str, str]: """Return path to reference :param str component_type: Component type (schema, parameter, response, security_scheme) :param int openapi_major_version: OpenAPI major version (2 or 3) :param str component_name: Name of component to reference """ return { "$ref": "#/{}{}/{}".format( "components/" if openapi_major_version >= 3 else "", COMPONENT_SUBSECTIONS[openapi_major_version][component_type], component_name, ) }
# from django.contrib.admindocs.utils
[docs] def trim_docstring(docstring: str) -> str: """Uniformly trims leading/trailing whitespace from docstrings. Based on http://www.python.org/peps/pep-0257.html#handling-docstring-indentation """ if not docstring or not docstring.strip(): return "" # Convert tabs to spaces and split into lines lines = docstring.expandtabs().splitlines() indent = min(len(line) - len(line.lstrip()) for line in lines if line.lstrip()) trimmed = [lines[0].lstrip()] + [line[indent:].rstrip() for line in lines[1:]] return "\n".join(trimmed).strip()
# from rest_framework.utils.formatting
[docs] def dedent(content: str) -> str: """ Remove leading indent from a block of text. Used when generating descriptions from docstrings. Note that python's `textwrap.dedent` doesn't quite cut it, as it fails to dedent multiline docstrings that include unindented text on the initial line. """ whitespace_counts = [ len(line) - len(line.lstrip(" ")) for line in content.splitlines()[1:] if line.lstrip() ] # unindent the content if needed if whitespace_counts: whitespace_pattern = "^" + (" " * min(whitespace_counts)) content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), "", content) return content.strip()
# http://stackoverflow.com/a/8310229
[docs] def deepupdate(original: dict, update: dict) -> dict: """Recursively update a dict. Subdict's won't be overwritten but also updated. """ for key, value in original.items(): if key not in update: update[key] = value elif isinstance(value, dict): deepupdate(value, update[key]) return update