We can add custom tags to generated OPS

This commit is contained in:
Vincent Maillol
2021-08-22 14:32:19 +02:00
parent cb996860a9
commit fa7e8d914b
9 changed files with 173 additions and 40 deletions

View File

@@ -4,7 +4,7 @@ Utility to extract extra OAS description from docstring.
import re
import textwrap
from typing import Dict
from typing import Dict, List
class LinesIterator:
@@ -47,11 +47,10 @@ def _i_extract_block(lines: LinesIterator):
except StopIteration:
return
# Get the size of the indentation.
if (match := re.search("^ +", line)) is None:
return # No block to extract.
indent = match.group()
yield line[len(indent) :]
indent = re.fullmatch("( *).*", line).groups()[0]
indentation = len(indent)
start_of_other_block = re.compile(f" {{0,{indentation}}}[^ ].*")
yield line[indentation:]
# Yield lines until the indentation is the same or is greater than
# the first block line.
@@ -59,8 +58,8 @@ def _i_extract_block(lines: LinesIterator):
line = next(lines)
except StopIteration:
return
while (is_empty := line.strip() == "") or line.startswith(indent):
yield "" if is_empty else line[len(indent) :]
while not start_of_other_block.fullmatch(line):
yield line[indentation:]
try:
line = next(lines)
except StopIteration:
@@ -87,10 +86,13 @@ def status_code(docstring: str) -> Dict[int, str]:
iterator = LinesIterator(docstring)
for line in iterator:
if re.fullmatch("status\\s+codes?\\s*:", line, re.IGNORECASE):
iterator.rewind()
blocks = []
lines = []
for line_of_block in _i_extract_block(iterator):
if re.search("^\\d{3}\\s*:", line_of_block):
i_block = _i_extract_block(iterator)
next(i_block)
for line_of_block in i_block:
if re.search("^\\s*\\d{3}\\s*:", line_of_block):
if lines:
blocks.append("\n".join(lines))
lines = []
@@ -105,6 +107,19 @@ def status_code(docstring: str) -> Dict[int, str]:
return {}
def tags(docstring: str) -> List[str]:
"""
Extract the "Tags:" block of the docstring.
"""
iterator = LinesIterator(docstring)
for line in iterator:
if re.fullmatch("tags\\s*:.*", line, re.IGNORECASE):
iterator.rewind()
lines = " ".join(_i_extract_block(iterator))
return [" ".join(e.split()) for e in re.split("[,;]", lines.split(":")[1])]
return []
def operation(docstring: str) -> str:
"""
Extract all docstring except the "Status Code:" block.
@@ -112,7 +127,8 @@ def operation(docstring: str) -> str:
lines = LinesIterator(docstring)
ret = []
for line in lines:
if re.fullmatch("status\\s+codes?\\s*:", line, re.IGNORECASE):
if re.fullmatch("status\\s+codes?\\s*:|tags\\s*:.*", line, re.IGNORECASE):
lines.rewind()
for _ in _i_extract_block(lines):
pass
else:

View File

@@ -2,7 +2,7 @@
Utility to write Open Api Specifications using the Python language.
"""
from typing import Union
from typing import Union, List
class Info:
@@ -157,7 +157,7 @@ class Responses:
self._spec = spec.setdefault("responses", {})
def __getitem__(self, status_code: Union[int, str]) -> Response:
if not (100 <= int(status_code) < 600):
if not 100 <= int(status_code) < 600:
raise ValueError("status_code must be between 100 and 599")
spec = self._spec.setdefault(str(status_code), {})
@@ -196,6 +196,17 @@ class OperationObject:
def responses(self) -> Responses:
return Responses(self._spec)
@property
def tags(self) -> List[str]:
return self._spec.get("tags", [])[:]
@tags.setter
def tags(self, tags: List[str]):
if tags:
self._spec["tags"] = tags[:]
else:
self._spec.pop("tags", None)
class PathItem:
def __init__(self, spec: dict):

View File

@@ -1,7 +1,7 @@
import typing
from inspect import getdoc
from itertools import count
from typing import List, Type, Optional, Dict
from typing import List, Type, Optional
from aiohttp.web import Response, json_response
from aiohttp.web_app import Application
@@ -86,6 +86,7 @@ def _add_http_method_to_oas(
description = getdoc(handler)
if description:
oas_operation.description = docstring_parser.operation(description)
oas_operation.tags = docstring_parser.tags(description)
status_code_descriptions = docstring_parser.status_code(description)
else:
status_code_descriptions = {}

View File

@@ -1,6 +1,6 @@
from functools import update_wrapper
from inspect import iscoroutinefunction
from typing import Any, Callable, Generator, Iterable, Set, ClassVar, Literal
from typing import Any, Callable, Generator, Iterable, Set, ClassVar
import warnings
from aiohttp.abc import AbstractView