Hi luolingchun — found three issues in flask_openapi/utils.py while running adversarial test generation via tailtest. All three are small fixes.
1. get_operation line 58 — backward condition swallows single-line docstring descriptions
doc_description = lines[0] if len(lines) == 0 else "<br/>".join(lines[1:])
doc.split("\n") always returns at least 1 element, so len(lines) == 0 is always False. When a function has a single-line docstring with no explicit summary, doc_description becomes "<br/>".join([]) = "". The description is silently lost from the generated OpenAPI spec.
Suspected intent: len(lines) <= 1 (use the line as description if there's only one).
2. parse_method silently drops HTTPMethod.HEAD
The if/elif ladder handles GET/POST/PUT/PATCH/DELETE. HEAD operations are valid HTTP and the PathItem model has a head field, but routes registered with methods=["HEAD"] are silently dropped — no PathItem entry, no warning.
Fix: add elif method == HTTPMethod.HEAD: paths.setdefault(uri, PathItem()).head = operation
3. parse_method silently drops HTTPMethod.OPTIONS
Same issue as #2 for OPTIONS — PathItem has the field but the parser doesn't populate it.
Reproduction
import pytest
from flask_openapi.models.api_doc import PathItem
from flask_openapi.models.operation import Operation
from flask_openapi.utils import get_operation, parse_method
from flask_openapi.types import HTTPMethod
# Bug 1
def test_single_line_docstring_description_should_not_vanish():
"""get_operation: single-line docstring should NOT lose description.
Reproduction:
- Define a function with a single-line docstring (no blank-line separator)
- Call get_operation(func) without explicit summary kwarg
- Expected: doc_description == the single docstring line
- Observed: doc_description == "" (silently lost)
"""
def my_endpoint():
"""A single-line description."""
pass
op = get_operation(my_endpoint)
assert op.description == "A single-line description.", (
f"Description silently lost; got: {op.description!r}"
)
# Bug 2
def test_head_method_silently_ignored():
"""parse_method: HEAD operation should be added to PathItem."""
paths = {}
op = Operation(summary="test")
parse_method("/test", HTTPMethod.HEAD, paths, op)
assert "/test" in paths, "HEAD method silently ignored by parse_method"
# Bug 3
def test_options_method_silently_ignored():
"""parse_method: OPTIONS operation should be added to PathItem."""
paths = {}
op = Operation(summary="test")
parse_method("/test", HTTPMethod.OPTIONS, paths, op)
assert "/test" in paths, "OPTIONS method silently ignored by parse_method"
Happy to open a PR if useful. Found via tailtest.
Hi luolingchun — found three issues in
flask_openapi/utils.pywhile running adversarial test generation via tailtest. All three are small fixes.1.
get_operationline 58 — backward condition swallows single-line docstring descriptionsdoc.split("\n")always returns at least 1 element, solen(lines) == 0is always False. When a function has a single-line docstring with no explicitsummary,doc_descriptionbecomes"<br/>".join([])="". The description is silently lost from the generated OpenAPI spec.Suspected intent:
len(lines) <= 1(use the line as description if there's only one).2.
parse_methodsilently dropsHTTPMethod.HEADThe if/elif ladder handles GET/POST/PUT/PATCH/DELETE. HEAD operations are valid HTTP and the
PathItemmodel has aheadfield, but routes registered withmethods=["HEAD"]are silently dropped — no PathItem entry, no warning.Fix: add
elif method == HTTPMethod.HEAD: paths.setdefault(uri, PathItem()).head = operation3.
parse_methodsilently dropsHTTPMethod.OPTIONSSame issue as #2 for OPTIONS —
PathItemhas the field but the parser doesn't populate it.Reproduction
Happy to open a PR if useful. Found via tailtest.