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})/
AIOHTTP_FROM_IMPORT_RE = /^[ \t]*from[ \t]+aiohttp[ \t]+import[ \t]*(?:\(((?:[^)#]++|#[^\n]*+)*+)\)|((?:[^\n#\\]++|\\\r?\n)*+))/m

from aiohttp import <names>: capture 1 is a parenthesised list (it can span lines and carry # comments, ) included), capture 2 a plain list (a trailing comment is not part of it, a backslash continues it).

AIOHTTP_SERVER_NAME_RE = /\b(?:web(?:_\w+)?|RouteTableDef)\b/
AIOHTTP_WEB_MODULE_RE = /\baiohttp\.web/

The server half of aiohttp (aiohttp.web, from aiohttp import web), not the client (ClientSession) that FastAPI and Sanic apps pull in for outbound requests. aiohttp.web anywhere covers import aiohttp.web, from aiohttp.web import ... and aiohttp.web.Application() after a plain import aiohttp.

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 has no aiohttp server import 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"}
HA_VIEW_CLASS_RE = /^(\s*)class\s+([A-Za-z_]\w*)\s*\(([^)]*\bHomeAssistantView\b[^)]*)\)\s*:/

Home Assistant's HomeAssistantView.register adds each verb method of a view at its url and extra_urls with router.add_route. requires_auth defaults to True on the base.

HA_VIEW_EXTRA_RE = /^extra_urls\s*(?::[^=]+)?=\s*\[([^\]]*)\]/
HA_VIEW_NO_AUTH_RE = /^requires_auth\s*(?::[^=]+)?=\s*False\b/
HA_VIEW_STRINGS_RE = /[rf]?['"]([^'"]+)['"]/
HA_VIEW_URL_RE = /^url\s*(?::[^=]+)?=\s*(?:[rf]?['"]([^'"]+)['"]|([A-Z_][A-Z0-9_]*)\s*$)/
HTTP_METHOD_NAMES = ["get", "post", "put", "delete", "patch", "head", "options"] of ::String
IMPORT_LIST_COMMENT_RE = /#[^\n]*/
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