Skip to content

support distinct routes for methods #169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/apispec_webframeworks/flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,23 +120,41 @@ def _rule_for_view(
rule = app.url_map._rules_by_endpoint[endpoint][0]
return rule

@staticmethod
def _view_for_rule(
rule: Rule,
app: Optional[Flask] = None,
) -> Union[Callable[..., Any], "RouteCallable"]:
if app is None:
app = current_app
return app.view_functions[rule.endpoint]

def path_helper(
self,
path: Optional[str] = None,
operations: Optional[dict] = None,
parameters: Optional[List[dict]] = None,
*,
view: Optional[Union[Callable[..., Any], "RouteCallable"]] = None,
rule: Optional[Rule] = None,
app: Optional[Flask] = None,
**kwargs: Any,
) -> Optional[str]:
"""Path helper that allows passing a Flask view function."""
assert view is not None
assert operations is not None

rule = self._rule_for_view(view, app=app)
if rule is None:
assert view is not None
rule = self._rule_for_view(view, app=app)
if view is None:
view = self._view_for_rule(rule, app=app)
view_doc = view.__doc__ or ""
doc_operations = yaml_utils.load_operations_from_docstring(view_doc)
doc_operations = {
k: v
for k, v in doc_operations.items()
if rule.methods is None or k.upper() in rule.methods or k.startswith("x-")
}
operations.update(doc_operations)
if hasattr(view, "view_class") and issubclass(view.view_class, MethodView): # noqa: E501
# method attribute is dynamically added, which is supported by mypy
Expand Down
31 changes: 30 additions & 1 deletion tests/test_ext_flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def delete(self):
assert "delete" not in paths["/hi"]

def test_integration_with_docstring_introspection(self, app, spec):
@app.route("/hello")
@app.route("/hello", methods=["GET", "POST"])
def hello():
"""A greeting endpoint.

Expand Down Expand Up @@ -180,3 +180,32 @@ def get_pet(pet_id):

spec.path(view=get_pet, app=app)
assert "/pet/{pet_id}" in get_paths(spec)

def test_multiple_paths(self, app, spec):
@app.put("/user")
@app.route("/user/<user>")
def user(user=None):
"""A greeting endpoint.

---
get:
description: get user details
responses:
200:
description: a user to be returned
put:
description: create a user
responses:
200:
description: some data
"""
pass

for rule in app.url_map.iter_rules():
spec.path(rule=rule)

paths = get_paths(spec)
get_op = paths["/user/{user}"]["get"]
put_op = paths["/user"]["put"]
assert get_op["description"] == "get user details"
assert put_op["description"] == "create a user"