12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- """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",
- },
- }
- 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
- 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
- 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
- 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
|