Skip to content

Number as a path parameter doesn't work #1635

@mattixpet

Description

@mattixpet

Description

When using schema: type: number for a path parameter, it gives an error, ValueError: invalid literal for int() with base 10: '-21.5454'

Isn't it supposed to be possible to use number as a path parameter? If I use string or integer it works fine.

I'm really stumped here as to what's going on.

Expected behaviour

I expect the path parameters to be printed out in the console where I started my server when using the swagger ui to execute, as per my test controller function below (see Steps to reproduce)

Actual behaviour

When using latitude=64.5151
longitude=-21.5454
srid=4326

I get the error in description.

127.0.0.1 - - [14/Feb/2023 16:52:12] "GET /v1/lat/64.5151/long/-21.5454/srid/4326 HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/matthias/.local/share/virtualenvs/test-api-Hl7MEg6r/lib/python3.9/site-packages/flask/app.py", line 2548, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/matthias/.local/share/virtualenvs/test-api-Hl7MEg6r/lib/python3.9/site-packages/flask/app.py", line 2528, in wsgi_app
    response = self.handle_exception(e)
  File "/home/matthias/.local/share/virtualenvs/test-api-Hl7MEg6r/lib/python3.9/site-packages/flask_cors/extension.py", line 165, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/home/matthias/.local/share/virtualenvs/test-api-Hl7MEg6r/lib/python3.9/site-packages/flask/app.py", line 2524, in wsgi_app
    ctx.push()
  File "/home/matthias/.local/share/virtualenvs/test-api-Hl7MEg6r/lib/python3.9/site-packages/flask/ctx.py", line 383, in push
    self.match_request()
  File "/home/matthias/.local/share/virtualenvs/test-api-Hl7MEg6r/lib/python3.9/site-packages/flask/ctx.py", line 351, in match_request
    result = self.url_adapter.match(return_rule=True)  # type: ignore
  File "/home/matthias/.local/share/virtualenvs/test-api-Hl7MEg6r/lib/python3.9/site-packages/werkzeug/routing/map.py", line 599, in match
    result = self.map._matcher.match(domain_part, path_part, method, websocket)
  File "/home/matthias/.local/share/virtualenvs/test-api-Hl7MEg6r/lib/python3.9/site-packages/werkzeug/routing/matcher.py", line 173, in match
    value = rule._converters[name].to_python(value)
  File "/home/matthias/.local/share/virtualenvs/test-api-Hl7MEg6r/lib/python3.9/site-packages/connexion/apps/flask_app.py", line 184, in to_python
    return int(value)
ValueError: invalid literal for int() with base 10: '-21.5454'

Steps to reproduce

I've made a minimal yaml file which reproduces the issue:

openapi: 3.0.0
info:
  title: Path param bug
  version: 1.0.1
paths:
  /lat/{latitude}/long/{longitude}/srid/{srid}:
    get:
      operationId: test_controller.test_coords
      parameters:
      - in: path
        name: latitude
        required: true
        schema:
          type: number
      - in: path
        name: longitude
        required: true
        schema:
          type: number
      - in: path
        name: srid
        required: true
        schema:
          type: integer
      responses:
        "200":
          content: {}
          description: OK

And with controller code:

def test_coords(*args, body={}, **kwargs):
    print("lat:", kwargs["latitude"])
    print("lon:", kwargs["longitude"])
    print("srid:", kwargs["srid"])

Additional info:

This error happens before the controller code is run, and I managed to pinpoint part of the issue by setting breakpoints in my local lib files mentioned in the stack trace. I found out, that somehow, somewhere, connexion or flask or werkzeug changes the float path parameters into more inputs than they are by taking the numbers after the decimal point and supplying them as separate parameters, this breaks the code, because what the validator receives is the following:
latitude=64.5151
longitude=0.5151
srid=-21.5454

I see this by setting a breakpoint in lib/python3.9/site-packages/werkzeug/routing/matcher.py before the line in the stack trace. Printing vars() there gives this:

{'self': <werkzeug.routing.matcher.StateMachineMatcher object at 0x7f98f57e5850>, 'domain': '', 'path': '/v1/lat/64.5151/long/-21.5454/srid/4326', 'rv': (<Rule '/v1/lat/<latitude>/long/<longitude>/srid/<srid>' (GET, HEAD, OPTIONS) -> /v1.test_controller_test_coords>, ['64.5151', '.5151', '-21.5454', '.5454', '4326']), 'rule': <Rule '/v1/lat/<latitude>/long/<longitude>/srid/<srid>' (GET, HEAD, OPTIONS) -> /v1.test_controller_test_coords>, 'values': ['64.5151', '.5151', '-21.5454', '.5454', '4326'], 'result': {}, 'name': 'latitude', 'value': '64.5151', '_match': <function StateMachineMatcher.match.<locals>._match at 0x7f98f3c01b80>, 'have_match_for': set(), 'websocket_mismatch': False, 'method': 'GET', 'websocket': False}

Notice ['64.5151', '.5151', '-21.5454', '.5454', '4326'], these are the path parameters the validator/controller is receiving. Somehow the .5151 and .5454 are added as parameters messing everything up.

Pressing continue prints the same thing, but with longitude as the value above

{'self': <werkzeug.routing.matcher.StateMachineMatcher object at 0x7f98f57e5850>, 'domain': '', 'path': '/v1/lat/64.5151/long/-21.5454/srid/4326', 'rv': (<Rule '/v1/lat/<latitude>/long/<longitude>/srid/<srid>' (GET, HEAD, OPTIONS) -> /v1.test_controller_test_coords>, ['64.5151', '.5151', '-21.5454', '.5454', '4326']), 'rule': <Rule '/v1/lat/<latitude>/long/<longitude>/srid/<srid>' (GET, HEAD, OPTIONS) -> /v1.test_controller_test_coords>, 'values': ['64.5151', '.5151', '-21.5454', '.5454', '4326'], 'result': {'latitude': 64.5151}, 'name': 'longitude', 'value': '.5151', '_match': <function StateMachineMatcher.match.<locals>._match at 0x7f98f3c01b80>, 'have_match_for': set(), 'websocket_mismatch': False, 'method': 'GET', 'websocket': False}

And continue again gives the srid value above

{'self': <werkzeug.routing.matcher.StateMachineMatcher object at 0x7f98f57e5850>, 'domain': '', 'path': '/v1/lat/64.5151/long/-21.5454/srid/4326', 'rv': (<Rule '/v1/lat/<latitude>/long/<longitude>/srid/<srid>' (GET, HEAD, OPTIONS) -> /v1.test_controller_test_coords>, ['64.5151', '.5151', '-21.5454', '.5454', '4326']), 'rule': <Rule '/v1/lat/<latitude>/long/<longitude>/srid/<srid>' (GET, HEAD, OPTIONS) -> /v1.test_controller_test_coords>, 'values': ['64.5151', '.5151', '-21.5454', '.5454', '4326'], 'result': {'latitude': 64.5151, 'longitude': 0.5151}, 'name': 'srid', 'value': '-21.5454', '_match': <function StateMachineMatcher.match.<locals>._match at 0x7f98f3c01b80>, 'have_match_for': set(), 'websocket_mismatch': False, 'method': 'GET', 'websocket': False}

Output of the commands:

  • python --version
    Python 3.9.2
  • pip show connexion | grep "^Version\:"
    Version: 2.14.0

(this is how I use connexion in pipfile: connexion = {version = "==2.14.0", extras = ["swagger-ui"]})

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions