module

Noir::TreeSitterPythonRouteExtractor

Tree-sitter-backed Python route decorator extractor. It parses the whole source once and walks the resulting AST, which handles:

  • decorators split across multiple lines
  • paths / methods containing commas, brackets, or nested quotes
  • precise def/class line discovery without a forward-scanning heuristic

Constants

HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "trace", "query"] of ::String

query (RFC 10008) recognizes @<var>.query("/path") shortcut decorators (Flask/Werkzeug shipped this; other decorator-based frameworks fall through harmlessly if they never define it).

Instance methods

extract_attribute_calls(source : String, attributes : Array(String)) : Array(AttributeCall)

Every <x>.<attribute>(...) call whose attribute is in attributes — mount calls such as app.include_router(router, prefix="/v1"). Comments and docstrings are not calls, so they never match.

Source
extract_blueprints_from(root : LibTreeSitter::TSNode, source : String, module_names : Array(String), results : Array(BlueprintDecl) = [] of BlueprintDecl) : Array(BlueprintDecl)

Returns every Blueprint(...) assignment under root that matches one of module_names or a bare Blueprint.

module_names is the list of module prefixes allowed before .Blueprint (e.g. ["flask"]). A bare Blueprint call, or one named <Prefix>Blueprint, is always accepted.

Two assignment shapes count: bare name = Blueprint(...) and qualified name = <module>.Blueprint(...). The module is read as full text from any node, so dotted prefixes like my_pkg.flask.Blueprint(...) (an attribute, not an identifier) still match.

Source
extract_decorations(source : String, router_names : Array(String) | Nil = nil, extra_attributes : Hash(String, String) | Nil = nil, bare_route : Bool = false, pathless : Bool = false, bare_routers : Array(String) | Nil = nil) : Array(Decoration)

Parses source and returns every route decoration found.

router_names optionally restricts which variable names count as routers; nil accepts any identifier.

extra_attributes optionally widens the set of decorator attribute names that count as a route. Each entry maps an attribute name (e.g. "websocket") to the HTTP method the synthesised endpoint should carry (Quart's @app.websocket is emitted as GET so the downstream pipeline keeps treating it as an HTTP-shaped endpoint; the caller flips protocol = "ws" based on attribute_name).

bare_route also accepts an unqualified @route(...) (Odoo's from odoo.http import route); it yields an empty router_name.

pathless keeps a decorator that names no path (Frappe's @frappe.whitelist(), whose URL comes from the module); its path is empty.

bare_routers names identifiers that are route decorators themselves, called or not: FastHTML's @rt("/x") and a path-less @rt (its path is empty; the URL comes from the function name).

Source
extract_decorations_and_blueprints(source : String, module_names : Array(String), router_names : Array(String) | Nil = nil, extra_attributes : Hash(String, String) | Nil = nil, rule_wrappers : Bool = false) : Tuple(Array(Decoration), Array(BlueprintDecl))

One parse → decorations + blueprints. Preferred entry for adapters that need both (Flask, Quart, Sanic); result arrays are also published into the individual memos so a later solo call hits.

Source
extract_decorations_from(root : LibTreeSitter::TSNode, source : String, router_names : Array(String) | Nil = nil, extra_attributes : Hash(String, String) | Nil = nil, results : Array(Decoration) = [] of Decoration, bare_route : Bool = false, pathless : Bool = false, bare_routers : Array(String) | Nil = nil, rule_wrappers : Bool = false) : Array(Decoration)

Same as extract_decorations but reuses a pre-parsed root so a caller that also needs blueprints (Flask/Quart/Sanic) pays for one tree-sitter parse instead of two.

Source

Nested types