diff --git a/src/apispec_webframeworks/flask.py b/src/apispec_webframeworks/flask.py index 2365087..7a5767d 100644 --- a/src/apispec_webframeworks/flask.py +++ b/src/apispec_webframeworks/flask.py @@ -120,6 +120,15 @@ 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, @@ -127,16 +136,25 @@ def path_helper( 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 diff --git a/tests/test_ext_flask.py b/tests/test_ext_flask.py index c3356d4..9ed260d 100644 --- a/tests/test_ext_flask.py +++ b/tests/test_ext_flask.py @@ -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. @@ -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/") + 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"