module Noir::TreeSitterPythonRouteExtractor

Overview

Tree-sitter-backed port of PythonRouteExtractor.

Unlike the regex extractor (which is line-oriented and relies on the caller to loop over lines), this one parses the whole source once and walks the resulting AST. That buys us:

Behaviour mirrors the regex extractor closely enough that the same analyzer adapters could switch to it with minimal changes; parity is verified in spec/unit_test/miniparser/python_route_extractor_ts_spec.cr.

Extended Modules

Defined in:

miniparsers/python_route_extractor_ts.cr

Constant Summary

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

Instance Method Summary

Instance Method Detail

def extract_blueprints(source : String, module_names : Array(String)) : Array(BlueprintDecl) #

Parses source and returns every Blueprint(...) assignment 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 is always accepted, matching the regex extractor.

Uses a tree-sitter query to find the two Blueprint assignment shapes (bare and module-qualified). The url_prefix keyword is still extracted procedurally because a Blueprint may be declared without one and the query language can't express "this keyword, if present" cleanly.


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

[View source]
def extract_decorations(source : String, router_names : Array(String) | Nil = nil, extra_attributes : Hash(String, 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 (matching the regex extractor, which doesn't gate on the name).

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).


[View source]
def extract_decorations_and_blueprints(source : String, module_names : Array(String), router_names : Array(String) | Nil = nil, extra_attributes : Hash(String, String) | Nil = nil) : 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.


[View source]
def 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) : 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.


[View source]