class
Analyzer::Python::Flask
Defined in:
analyzer/analyzers/python/flask.crConstant Summary
-
ADD_RESOURCE_RE =
/(#{PYTHON_VAR_NAME_REGEX})\s*\.\s*add_resource\s*\((.+)\)/m -
flask_restful / flask-restx register class-based
Resources withapi.add_resource(ResourceClass, "/url"[, "/url2"], endpoint=...). Each Resource exposes one endpoint per HTTP-verb method it defines. Hoisted for the common (alias-free) case so the per-file regex isn't recompiled;ADD_RESOURCE_SUBSTRINGis the cheap per-line guard. -
ADD_RESOURCE_SUBSTRINGS =
[".add_resource("] -
ADD_URL_RULE_RE =
/(#{PYTHON_VAR_NAME_REGEX})\.add_url_rule\((.+)\)/m -
COMPETING_FRAMEWORK_IMPORT_RE =
/^\s*(?:from|import)\s+(?:fastapi|sanic|litestar|starlette|quart|robyn|ninja|bottle|aiohttp|falcon|pyramid|tornado)\b/m -
Decorator-based Python web frameworks whose route syntax is indistinguishable from Flask's at the line level —
@app.get("/x"),@api.post("/x"). A file that imports one of these and never mentions flask belongs to that framework's analyzer.ninja(Django Ninja),bottleandaiohttpwere missing from the list, so the Flask analyzer claimed their handler files and relabelled the routespython_flaskwith Flask-shaped (usually empty) params. -
DOTTED_REFERENCE_RE =
/^#{DOT_NATION}$/ -
FLASK_INSTANCE_RE =
/(#{PYTHON_VAR_NAME_REGEX})(?::#{DOT_NATION})?=(?:flask\.)?Flask\(/ -
Per-line route-discovery patterns. These interpolate only the PYTHON_VAR_NAME_REGEX/DOT_NATION constants, so the inline literals were recompiling identical PCRE2 patterns on every source line of every file (
#analyzeruns them per line). Compile once here; the.to_sexpansion of the interpolated constants is byte-identical to the previous inline form, so matching behaviour is unchanged. -
INIT_APP_RE =
/(#{PYTHON_VAR_NAME_REGEX})\.init_app\((#{PYTHON_VAR_NAME_REGEX})/ -
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 -
extract_request_paramsruns once per route and used to rebuild two PCRE2 patterns per request field on every call (8 fields × 2 = 16 regex compilations per endpoint). PCRE2 JIT-compilation of an interpolated regex literal is ~3µs and dominated Flask scan time (profiling: ~50% of the analyzer). The field names are a fixed set, so precompile the access patterns once here and reuse them. Tuple shape: {noir_param_type, bracket_access_regex, get_access_regex} The get-access pattern accepts.get(and.getlist(: Werkzeug's request MultiDicts (request.args,request.form, …) exposegetlist("key")as the standard accessor for repeated keys (?tag=a&tag=b), and it reads the same first string argument as the key — so.get(?:list)?captures both without a separate pattern. -
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://stackoverflow.com/a/16664376 Reference: https://tedboy.github.io/flask/generated/generated/flask.Request.html
-
REQUEST_PARAM_TYPES =
{"query" => nil, "form" => ["POST", "PUT", "PATCH", "DELETE"], "json" => ["POST", "PUT", "PATCH", "DELETE"], "cookie" => nil, "header" => nil} -
RESOURCE_REGISTRAR_DEF_RE =
/^(\s*)(?:async\s+)?def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*self\s*,\s*resource\b/ -
def add_x(self, resource, ...)head of an Api-subclass method that may wrapadd_resource(e.g. redash'sadd_org_resource). -
ROUTE_DECORATOR_RE =
/^\s*@\s*#{DOT_NATION}\s*\.\s*(?:route|get|post|put|patch|delete|head|options|trace)\s*\(/m -
Source-ownership and add_url_rule helpers — same constant-only interpolation (DOT_NATION), hoisted so the per-file/per-call sites don't recompile them. VIEW_FUNC_KWARG_RE has no whitespace tolerance (unlike Quart's) because Flask's add_url_rule args are matched on space-stripped lines.
-
ROUTE_REGISTRAR_RE =
/\b#{DOT_NATION}\s*\.\s*(?:add_url_rule|register_blueprint)\s*\(/ -
VIEW_ASSIGN_RE =
/(#{PYTHON_VAR_NAME_REGEX})(?::#{DOT_NATION})?=(#{PYTHON_VAR_NAME_REGEX})\.as_view\(/ -
VIEW_FUNC_AS_VIEW_RE =
/view_func=(#{PYTHON_VAR_NAME_REGEX})\.as_view\([rf]?['"]([^'"]*)['"]\)/ -
view_func=extractors for the add_url_rule scan below — same constant-only interpolation, hoisted so they aren't rebuilt on everyadd_url_rule(...)match found in a file. -
VIEW_FUNC_KWARG_RE =
/view_func=(#{DOT_NATION})(?:,|\)|$)/ -
VIEW_FUNC_VAR_RE =
/view_func=(#{PYTHON_VAR_NAME_REGEX})[,\)]/
Class Method Summary
Instance Method Summary
- #analyze
-
#create_parser(path : String, content : String = "") : PythonParser
Create a Python parser for a given path and content.
-
#extract_params_from_decorator(path : String, lines : Array(String), line_index : Int32, direction : Symbol = :down) : Tuple(Array(Param), Int32)
Extracts parameters from the decorator
-
#get_endpoints(method : String, route_path : String, extra_params : String, codeblock_lines : Array(String), prefix : String)
Extracts endpoint information from the given route and code block
-
#get_filtered_params(method : String, params : Array(Param)) : Array(Param)
Filters the parameters based on the HTTP method
-
#get_parser(path : String, content : String = "") : PythonParser
Get a parser for a given path
Instance methods inherited from class Analyzer::Python::PythonEngine
build_callees_from(body : String, body_start_line : Int32, path : String, *, definition_base_path : String | Nil = nil, source : String | Nil = nil) : Array(Callee)
build_callees_from,
find_def_line(lines : Array(String), decorator_line : Int32) : Int32 | Nil
find_def_line,
find_imported_modules(app_base_path : String, file_path : String, content : String | Nil = nil) : Hash(String, Tuple(String, Int32))
find_imported_modules,
find_imported_package(package_path : String, dotted_as_names : String) : Array(Tuple(String, String, Int32))
find_imported_package,
find_json_params(codeblock_lines : Array(String), json_var_names : Array(String)) : Array(Param)
find_json_params,
join_until_python_call_closes(lines : Array(String), index : Int32, line : String) : String
join_until_python_call_closes,
parse_code_block(data : String | Array(String), after : Regex | Nil = nil) : String | Nil
parse_code_block,
parse_function_def(source_lines : Array(String), start_index : Int32) : FunctionDefinition | Nil
parse_function_def,
push_callees_from(endpoint : Endpoint, body : String, body_start_line : Int32, path : String, *, definition_base_path : String | Nil = nil, source : String | Nil = nil) : Nil
push_callees_from,
python_paren_delta(line : String) : Int32
python_paren_delta,
python_signature_line_span(lines : Array(String)) : Int32
python_signature_line_span,
return_literal_value(data : String) : String
return_literal_value
Class methods inherited from class Analyzer::Python::PythonEngine
python_test_path?(path : String, base_path : String | Nil = nil) : Bool
python_test_path?
Instance methods inherited from class Analyzer
analyze
analyze,
base_path : String
base_path,
base_paths : Array(String)
base_paths,
base_relative_path(path : String) : String
base_relative_path,
callees_needed? : Bool
callees_needed?,
content_matches?(content : String, markers : Regex) : Bool
content_matches?,
http_header_name(name : String) : String | Nil
http_header_name,
line_number_for_index(content : String, char_index : Int32) : Int32
line_number_for_index,
logger : NoirLogger
logger,
parallel_analyze(files : Array(String), &block : String -> Nil)
parallel_analyze,
read_file_content(path : String) : String
read_file_content,
result : Array(Endpoint)
result,
unique_params(params : Array(Param)) : Array(Param)
unique_params,
url : String
url,
web_root_path(path : String, markers : Array(String)) : String
web_root_path
Constructor methods inherited from class Analyzer
new(options : Hash(String, YAML::Any))
new
Macros inherited from class Analyzer
analyzer_for(tech)
analyzer_for
Instance methods inherited from module FileHelper
all_files : Array(String)
all_files,
get_files_by_basename(basename : String) : Array(String)
get_files_by_basename,
get_files_by_extension(extension : String) : Array(String)
get_files_by_extension,
get_files_by_extensions(extensions : Array(String)) : Array(String)
get_files_by_extensions,
get_files_by_prefix(prefix : String) : Array(String)
get_files_by_prefix,
get_files_by_prefix_and_extension(prefix : String, extension : String) : Array(String)
get_files_by_prefix_and_extension,
get_files_by_relative_path(relative_path : String, root : String = "") : Array(String)
get_files_by_relative_path,
get_public_dir_files(base_path : String, folder : String) : Array(String)
get_public_dir_files,
get_public_files(base_path : String, anchors : Array(String) = ["shard.yml", "Gemfile"]) : Array(String)
get_public_files,
walked_path(expanded : String) : String
walked_path
Class Method Detail
Instance Method Detail
Create a Python parser for a given path and content. The parser walks the file with tree-sitter and recursively absorbs globals from imported modules — no lexer step.
Extracts parameters from the decorator
Extracts endpoint information from the given route and code block
Filters the parameters based on the HTTP method
Get a parser for a given path