Repository: marshmallow-code/apispec-webframeworks

Issue:
Bottle plugin path regex bug when using argument type in the url
<a href="https://github.com/genbits"><img src="https://avatars0.githubusercontent.com/u/3424362?v=4" align="left" width="96" height="96" hspace="10"></img></a> **Issue by [genbits](https://github.com/genbits)**
_Monday May 21, 2018 at 10:56 GMT_
_Originally opened as https://github.com/marshmallow-code/apispec/issues/215_

----

When parsing the path of a bottle app in the form: `/path/to/<resourceId:int>`, the generated API path becomes `/path/to/{int}`.

reproduce:

    import re
    RE_URL = re.compile(r'<(?:[^:<>]+:)?([^<>]+)>')
    path = '/path/to/<resourceId:int>'
    RE_URL.sub(r'{\1}', path)  # --> '/path/to/{int}'

This can be fixed easily by replacing RE_URL with: `re.compile(r'<([^:]+)(?::.+)?>')`



Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index b42ae4a..86f2842 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,12 @@
 Changelog
 ---------
 
+0.5.2 (unreleased)
+++++++++++++++++++
+
+* BottlePlugin: Fix support for typed path arguments (:issue:`16`).
+  Thanks :user:`genbits` for reporting and thanks :user:`elfjes` for the fix.
+
 0.5.1 (2019-11-18)
 ++++++++++++++++++
 
diff --git a/src/apispec_webframeworks/bottle.py b/src/apispec_webframeworks/bottle.py
index 4ed5327..b1bbbcf 100644
--- a/src/apispec_webframeworks/bottle.py
+++ b/src/apispec_webframeworks/bottle.py
@@ -27,8 +27,8 @@ from bottle import default_app
 from apispec import BasePlugin, yaml_utils
 from apispec.exceptions import APISpecError
 
+RE_URL = re.compile(r"<([^<>:]+):?[^>]*>")
 
-RE_URL = re.compile(r"<(?:[^:<>]+:)?([^<>]+)>")
 
 _default_app = default_app()
 


Test patch:
diff --git a/src/apispec_webframeworks/tests/test_ext_bottle.py b/src/apispec_webframeworks/tests/test_ext_bottle.py
index bbaf116..47c168a 100644
--- a/src/apispec_webframeworks/tests/test_ext_bottle.py
+++ b/src/apispec_webframeworks/tests/test_ext_bottle.py
@@ -99,10 +99,13 @@ class TestPathHelpers:
         spec.path(view=get_pet)
         assert "/pet/{pet_id}" in get_paths(spec)
 
-    def test_path_with_params(self, spec):
-        @route("/pet/<pet_id>/<shop_id>", methods=["POST"])
-        def set_pet():
-            return "new pet!"
+    @pytest.mark.parametrize(
+        "path", ["/pet/<pet_id:int>/<shop_id:re:[a-z]+>", "/pet/<pet_id>/<shop_id>"],
+    )
+    def test_path_with_params(self, spec, path):
+        @route(path, methods=["GET"])
+        def handler():
+            pass
 
-        spec.path(view=set_pet)
+        spec.path(view=handler)
         assert "/pet/{pet_id}/{shop_id}" in get_paths(spec)