"""Reading a typed value out of the query string, without a 500. WHY THIS EXISTS List endpoints filtered like this: if type_id := request.args.get('typeid'): query = query.filter(Machine.machinetypeid == int(type_id)) `int('abc')` raises, nothing catches it, and the caller gets 500 INTERNAL_ERROR with a traceback in the log. A wrong query argument is the caller's mistake and deserves a 400 saying which argument; a 500 says the server is broken and sends whoever is reading the log looking for a fault that is not there. The feature test run found the same shape in six places across five plugins, which makes it a property of how list routes are written rather than of any one route. The helpers raise QueryArgError. Routes turn it into a 400 - or, more usually, nothing does anything at all, because the app-level handler in shopdb/__init__.py answers it. That is the point: a route should not have to remember. Deliberately NOT the same policy as get_pagination_params, which falls back to a default for a bad page number. A nonsense page is harmless and the caller still wants their list; a nonsense FILTER is not, because silently ignoring it returns rows the caller believes they excluded. """ from flask import request class QueryArgError(ValueError): """A query argument was present but not of the type the route needs.""" def __init__(self, name, value, expected='a whole number'): self.name = name self.value = value super().__init__('%s must be %s (got %r)' % (name, expected, value)) def intarg(name, default=None, request_obj=None): """Read an integer query argument. Absent or empty -> default. Present but not an integer -> QueryArgError, which the app answers as a 400 naming the argument. """ source = request_obj if request_obj is not None else request raw = source.args.get(name) if raw is None or raw == '': return default try: return int(raw) except (TypeError, ValueError): raise QueryArgError(name, raw) def intargs(*names, request_obj=None): """Read several integer arguments at once; returns a dict of those present.""" found = {} for name in names: value = intarg(name, request_obj=request_obj) if value is not None: found[name] = value return found