"""Resolve per-view marker attributes across function and MethodView routes. View-tag decorators (``@no_store``, ``@rate_category``) set an attribute on the view they decorate. For a plain function view that is the object Flask stores in ``view_functions[endpoint]``, so the attribute is found directly. For a class-based ``MethodView`` (the dominant style here: ItemView/ListView), ``view_functions`` holds the ``as_view()`` dispatch closure while the decorator's attribute lives on the class's ``get()``/``post()`` method -- so the marker must be resolved off ``view_class`` for the request's verb. HEAD is special-cased to fall back to ``get`` because Flask's ``MethodView.dispatch_request`` runs ``get()`` for a HEAD request when no ``head()`` is defined, so a HEAD must be classified like the GET it actually executes. """ from typing import Any from flask import Flask _SENTINEL = object() def resolve_view_marker( app: Flask, endpoint: str | None, method: str, attr: str, default: Any = None ) -> Any: """Return marker ``attr`` for ``endpoint``/``method``, or ``default`` if unset. Checks the endpoint's view function first (function views), then, for MethodView routes, the class method for the request verb (mirroring Flask's HEAD->get fallback). """ view = app.view_functions.get(endpoint or '') value = getattr(view, attr, _SENTINEL) if value is not _SENTINEL: return value view_class = getattr(view, 'view_class', None) if view_class is not None: handler = getattr(view_class, method.lower(), None) if handler is None and method == 'HEAD': handler = getattr(view_class, 'get', None) if handler is not None: return getattr(handler, attr, default) return default