class

Analyzer::Python::Aiohttp

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

Constants

ADD_METHOD_ORIG_RE = HTTP_METHOD_NAMES.to_h do |m| {m, /\.add_#{m}\s*\(\s*[rf]?['"]([^'"]*)['"]/} end

Spaces are stripped before the add_<method> match, so the route path is re-extracted from the original (spaced) line. Precompile one matcher per method name so the re-extraction isn't a per-route PCRE2 compile. Keyed by the same names captured by ADD_METHOD_RE.

ADD_METHOD_RE = /(#{DOT_NATION})\.add_(#{METHODS_ALT})\([rf]?['"]([^'"]*)['"]\s*,\s*(?:handler\s*=\s*)?(#{DOT_NATION})/
ADD_ROUTE_ALIAS_RE = /^(#{PYTHON_VAR_NAME_REGEX})\([rf]?['"]([A-Za-z*]+)['"]\s*,\s*[rf]?['"]([^'"]*)['"]\s*,\s*(?:handler\s*=\s*)?(#{DOT_NATION})/
ADD_ROUTE_RE = /(#{DOT_NATION})\.add_route\([rf]?['"]([A-Za-z*]+)['"]\s*,\s*[rf]?['"]([^'"]*)['"]\s*,\s*(?:handler\s*=\s*)?(#{DOT_NATION})/
ADD_ROUTES_CALL_RE = /(#{DOT_NATION})\.add_routes\s*\(/
ADD_ROUTES_VAR_RE = /(#{DOT_NATION})\.add_routes\s*\(\s*(#{PYTHON_VAR_NAME_REGEX})/
ADD_STATIC_RE = /(#{DOT_NATION})\.add_static\([rf]?['"]([^'"]*)['"]/
ADD_SUBAPP_RE = /(#{DOT_NATION})\.add_subapp\s*\(\s*[rf]?['"]([^'"]*)['"]\s*,\s*(#{DOT_NATION})/
ADD_VIEW_RE = /(#{DOT_NATION})\.add_view\([rf]?['"]([^'"]*)['"]\s*,\s*(?:handler\s*=\s*)?(#{DOT_NATION})/
APP_PREFIX_RE = /^(#{PYTHON_VAR_NAME_REGEX})(?::#{PYTHON_VAR_NAME_REGEX})?=(?:web\.)?Application\(/

Prefix-collection matchers. collect_app_prefixes / collect_route_list_ranges scan every line of every file, so these were per-line PCRE2 recompiles before hoisting.

COMPETING_FRAMEWORK_IMPORT_RE = /^\s*(?:from|import)\s+(?:sanic|fastapi|litestar|starlette|quart|robyn|ninja|bottle|falcon|flask)\b/m

Frameworks whose registration syntax overlaps aiohttp's enough to trip the loose markers below — sanic's @app.get("/x") and its app.add_route(handler, "/x") in particular. A file that imports one of them and never mentions aiohttp is theirs. Mirrors the same guard in the Flask analyzer.

DEF_METHOD_RE = /^\s*(?:async\s+)?def\s+(#{METHODS_ALT})\s*\(/
DICT_ACCESSORS = {"headers" => "header", "cookies" => "cookie"}
DICT_METHOD_NAMES = Set {"get", "getall", "getone", "items", "keys", "values", "pop"}
HTTP_METHOD_NAMES = ["get", "post", "put", "delete", "patch", "head", "options"] of ::String
LIST_ASSIGN_RE = /^\s*(#{PYTHON_VAR_NAME_REGEX})\s*=\s*\[/
METHODS_ALT = HTTP_METHOD_NAMES.join("|")

The route-discovery patterns below interpolate only the PYTHON_VAR_NAME_REGEX/DOT_NATION constants and this fixed method alternation, so the inline literals were recompiling identical PCRE2 patterns on every source line of every file (and rebuilding methods_re with a join each line). Compile once here; the .to_s expansion of the interpolated constants is byte-identical to the previous inline form, so matching behaviour is unchanged.

ROUTE_ALIAS_RE = /^(#{PYTHON_VAR_NAME_REGEX})=(#{DOT_NATION})\.add_route/
ROUTE_DECO_RE = /@(#{PYTHON_VAR_NAME_REGEX})\.route\([rf]?['"]([A-Za-z*]+)['"]\s*,\s*[rf]?['"]([^'"]*)['"]/
WEB_METHOD_GUARD_RE = /\bweb\.(?:#{METHODS_ALT})\s*\(/
WEB_METHOD_RE = /\bweb\.(#{METHODS_ALT})\s*\(\s*[rf]?['"]([^'"]*)['"]\s*,\s*(?:handler\s*=\s*)?(#{DOT_NATION})/
WEB_ROUTE_RE = /\bweb\.route\s*\(\s*[rf]?['"]([A-Za-z*]+)['"]\s*,\s*[rf]?['"]([^'"]*)['"]\s*,\s*(?:handler\s*=\s*)?(#{DOT_NATION})/

Method-first web.route("METHOD", "/path", handler) — the only way to reach a route whose verb has no dedicated web.<method>() shorthand (e.g. QUERY, RFC 10008). Same free-form method capture as ROUTE_DECO_RE/ADD_ROUTE_RE, so any verb works, not just QUERY.

WEB_VIEW_RE = /\bweb\.view\s*\(\s*[rf]?['"]([^'"]*)['"]\s*,\s*(?:handler\s*=\s*)?(#{DOT_NATION})/

Class methods

tech_name
Source

Instance methods

analyze
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