Backwards-incompatible: URLFor and AbsoluteURLFor now do not accept parameters for flask.url_for as top-level parameters. They must always be passed in the values dictionary, as explained in the v0.14.0 changelog.
Since our requirements.txt file didn't previously pin the version of flask-marshmallow, this lead to a (silent) upgrade which broke the sfa-api. The quick fix is to pin the version to v0.14.0, but we should also consider making the relevant changes in sfa-api to move to flask-marshmallow v0.15.0.
How the error showed up
If flask-marshmallow v0.15.0 is used, then GET /sites/ works fine but anything else that relates to Sites (e.g., GET /observations) will fail with this error in the logs:
[13/Apr/2023:22:42:54 +0000] - 200 "GET / HTTP/1.1" 548 "-" "-" 0.002208
[2023-04-13 22:43:13,250] ERROR in app: Exception on /observations/ [GET]
Traceback (most recent call last):
File "/opt/app-root/lib/python3.9/site-packages/flask/app.py", line 2528, in wsgi_app
response = self.full_dispatch_request()
File "/opt/app-root/lib/python3.9/site-packages/flask/app.py", line 1825, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/opt/app-root/lib/python3.9/site-packages/flask/app.py", line 1823, in full_dispatch_request
rv = self.dispatch_request()
File "/opt/app-root/lib/python3.9/site-packages/flask/app.py", line 1799, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "/opt/app-root/lib/python3.9/site-packages/flask/views.py", line 107, in view
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
File "/opt/app-root/lib/python3.9/site-packages/flask/views.py", line 188, in dispatch_request
return current_app.ensure_sync(meth)(**kwargs)
File "/opt/app-root/src/sfa_api/observations.py", line 52, in get
return jsonify(ObservationSchema(many=True).dump(observations))
File "/opt/app-root/lib/python3.9/site-packages/marshmallow/schema.py", line 557, in dump
result = self._serialize(processed_obj, many=many)
File "/opt/app-root/lib/python3.9/site-packages/marshmallow/schema.py", line 519, in _serialize
return [
File "/opt/app-root/lib/python3.9/site-packages/marshmallow/schema.py", line 520, in <listcomp>
self._serialize(d, many=False)
File "/opt/app-root/lib/python3.9/site-packages/marshmallow/schema.py", line 525, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/opt/app-root/lib/python3.9/site-packages/marshmallow/fields.py", line 344, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/opt/app-root/lib/python3.9/site-packages/flask_marshmallow/fields.py", line 180, in _serialize
return _rapply(self.schema, _url_val, key=attr, obj=obj)
File "/opt/app-root/lib/python3.9/site-packages/flask_marshmallow/fields.py", line 133, in _rapply
return {key: _rapply(value, func, *args, **kwargs) for key, value in d.items()}
File "/opt/app-root/lib/python3.9/site-packages/flask_marshmallow/fields.py", line 133, in <dictcomp>
return {key: _rapply(value, func, *args, **kwargs) for key, value in d.items()}
File "/opt/app-root/lib/python3.9/site-packages/flask_marshmallow/fields.py", line 135, in _rapply
return func(d, *args, **kwargs)
File "/opt/app-root/lib/python3.9/site-packages/flask_marshmallow/fields.py", line 143, in _url_val
return val.serialize(key, obj, **kwargs)
File "/opt/app-root/lib/python3.9/site-packages/marshmallow/fields.py", line 344, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/opt/app-root/lib/python3.9/site-packages/flask_marshmallow/fields.py", line 108, in _serialize
return url_for(self.endpoint, **param_values)
File "/opt/app-root/lib/python3.9/site-packages/flask/helpers.py", line 256, in url_for
return current_app.url_for(
File "/opt/app-root/lib/python3.9/site-packages/flask/app.py", line 2034, in url_for
return self.handle_url_build_error(error, endpoint, values)
File "/opt/app-root/lib/python3.9/site-packages/flask/app.py", line 2023, in url_for
rv = url_adapter.build( # type: ignore[union-attr]
File "/opt/app-root/lib/python3.9/site-packages/werkzeug/routing/map.py", line 917, in build
raise BuildError(endpoint, values, method, self)
werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'sites.single'. Did you forget to specify values ['site_id']?
where the key lines are about the current_app.url_for() call and the werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'sites.single'. Did you forget to specify values ['site_id']? message.
The Python dependency
flask-marshmallow
released v0.15.0 on April 5, 2023, which includes a backwards-incompatible change from v0.14.0 (released September 27, 2020) that affects thesfa-api
code: https://github.com/marshmallow-code/flask-marshmallow/blob/dev/CHANGELOG.rstSince our
requirements.txt
file didn't previously pin the version offlask-marshmallow
, this lead to a (silent) upgrade which broke thesfa-api
. The quick fix is to pin the version to v0.14.0, but we should also consider making the relevant changes insfa-api
to move toflask-marshmallow
v0.15.0.How the error showed up
If
flask-marshmallow
v0.15.0 is used, thenGET /sites/
works fine but anything else that relates to Sites (e.g.,GET /observations
) will fail with this error in the logs:where the key lines are about the
current_app.url_for()
call and thewerkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'sites.single'. Did you forget to specify values ['site_id']?
message.