class

Analyzer::Python::Quart

Inherits Analyzer::Python::PythonEngine < Analyzer < FileHelper < Reference < Object

Quart is an ASGI re-implementation of Flask's API. Decorator, Blueprint, and request-object shapes mirror Flask 1:1, so this analyzer leans on the same tree-sitter helpers as the Flask one and adds two Quart-specific pieces:

  • @app.websocket("/ws") — surfaced as GET + protocol = "ws" so the existing endpoint pipeline carries it through unchanged
  • await request.get_json() / request.json body extraction — the same patterns Flask uses; nothing extra is needed because parameter extraction reads the function body line-by-line and the await prefix doesn't change the access shape.

Constants

ADD_URL_RULE_SCAN_RE = /(#{PYTHON_VAR_NAME_REGEX})\.add_url_rule\s*\((.*)\)\s*$/m
DOTTED_REFERENCE_RE = /^#{DOT_NATION}$/
METHOD_VIEW_DIRECT_RE = /view_func\s*=\s*(#{PYTHON_VAR_NAME_REGEX})\.as_view\s*\(/
METHOD_VIEW_POS_VAR_RE = /^\s*[rf]?['"][^'"]*['"]\s*,\s*[rf]?['"][^'"]*['"]\s*,\s*(#{PYTHON_VAR_NAME_REGEX})(?:\s*,|\s*$)/
METHOD_VIEW_POS_VIEW_RE = /^\s*[rf]?['"][^'"]*['"]\s*,\s*[rf]?['"][^'"]*['"]\s*,\s*(#{PYTHON_VAR_NAME_REGEX})\.as_view\s*\(/
METHOD_VIEW_VAR_RE = /view_func\s*=\s*(#{PYTHON_VAR_NAME_REGEX})/
QUART_INSTANCE_RE = /(#{PYTHON_VAR_NAME_REGEX})(?::#{PYTHON_VAR_NAME_REGEX})?=(?:quart\.)?Quart\(/

Per-line route-discovery patterns. These interpolate only the PYTHON_VAR_NAME_REGEX/DOT_NATION constants, so an inline literal inside the per-line analyze loop recompiled an identical PCRE2 pattern on every source line of every file. Compile once here; the .to_s expansion of the interpolated constants is byte-identical to the previous inline form, so matching behaviour is unchanged.

REGISTER_BLUEPRINT_RE = /(#{PYTHON_VAR_NAME_REGEX})\.register_blueprint\((#{DOT_NATION})/
REQUEST_PARAM_FIELD_PATTERNS = REQUEST_PARAM_FIELDS.map do |field_name, tuple| {tuple[1], Regex.new("request\\.#{field_name}\\[[rf]?['\"]([^'\"]*)['\"]\\]"), Regex.new("request\\.#{field_name}\\.get(?:list)?\\([rf]?['\"]([^'\"]*)['\"]")} end

Precompiled per-field access patterns. extract_request_params runs once per route; rebuilding these interpolated regexes on every call recompiled PCRE2 patterns (8 fields × 2) per endpoint. Compile once here and reuse. {noir_param_type, bracket_re, get_re}

REQUEST_PARAM_FIELDS = {"data" => {["POST", "PUT", "PATCH", "DELETE"], "form"}, "args" => {["GET"], "query"}, "form" => {["POST", "PUT", "PATCH", "DELETE"], "form"}, "files" => {["POST", "PUT", "PATCH", "DELETE"], "form"}, "values" => {["GET", "POST", "PUT", "PATCH", "DELETE"], "query"}, "json" => {["POST", "PUT", "PATCH", "DELETE"], "json"}, "cookies" => {nil, "cookie"}, "headers" => {nil, "header"}}

Reference: https://quart.palletsprojects.com/en/latest/reference/source/quart.wrappers.request.html

REQUEST_PARAM_TYPES = {"query" => nil, "form" => ["POST", "PUT", "PATCH", "DELETE", "QUERY"], "json" => ["POST", "PUT", "PATCH", "DELETE", "QUERY"], "cookie" => nil, "header" => nil}

QUERY (RFC 10008) is safe/idempotent like GET but, per the method's whole purpose, carries its filter criteria in a request body like POST — so it joins the body-bearing methods for "form"/"json", not the read-only "query" (query-string) type. Quart mirrors Flask's API 1:1 (including this table), so it gets the same treatment.

VIEW_ASSIGN_RE = /(#{PYTHON_VAR_NAME_REGEX})=(#{PYTHON_VAR_NAME_REGEX})\.as_view\(/
VIEW_FUNC_KWARG_RE = /view_func\s*=\s*(#{DOT_NATION})(?:\s*,|\s*\)|\s*$)/

Constant-only interpolation (DOT_NATION) — hoisted so the per-call sites don't recompile them.

WEBSOCKET_ATTRIBUTES = {"websocket" => "GET"}

@app.websocket("/ws") is the only attribute outside the standard HTTP-method set that the tree-sitter extractor needs to surface here. The synthesised method stays GET so the downstream filter logic (which keys off HTTP methods) keeps working; the analyzer rewrites protocol to "ws" later.

Class methods

tech_name
Source

Instance methods

analyze
Source
create_parser(path : String, content : String = "") : PythonParser
Source
get_endpoints(method : String, route_path : String, extra_params : String, codeblock_lines : Array(String), prefix : String)

Build endpoints from a single route decoration. Mirrors the Flask adapter's behaviour: split on declared methods=[...], default to GET, run parameter extraction over the handler body once and filter the params per method.

Source
get_filtered_params(method : String, params : Array(Param)) : Array(Param)
Source
get_parser(path : String, content : String = "") : PythonParser
Source
tech

Instance-side view of the same declaration. The per-file rescues live on this base class, which has no way to name the analyzer that is running inside them, so a skipped file could not be attributed to a tech. Deriving it from analyzer_for keeps the name written exactly once.

Source