Module API¶
The AuditModule base class defines the interface that every audit module must implement. All modules live in scripts/modules/ and are registered via the @register_module decorator.
AuditModule Base Class¶
from abc import ABC, abstractmethod
from typing import Any
class AuditModule(ABC):
MODULE_ID: str = ""
DISPLAY_NAME: str = ""
ALWAYS_ENABLED: bool = False
def __init__(self):
self.findings: list[dict] = []
self.score_data: dict[str, Any] = {}
@abstractmethod
def analyse(self, html: str, url: str = "", headers: dict = None, **kwargs) -> dict:
...
@abstractmethod
def score(self, analysis: dict) -> dict:
...
@classmethod
def detect(cls, html: str) -> bool:
return cls.ALWAYS_ENABLED
def add_finding(self, priority: str, title: str, description: str,
fix: str = "", effort: str = ""):
...
Class Attributes¶
| Attribute | Type | Description |
|---|---|---|
MODULE_ID |
str |
Unique identifier used in the registry (e.g. "seo", "local_seo") |
DISPLAY_NAME |
str |
Human-readable name shown in reports (e.g. "Local SEO") |
ALWAYS_ENABLED |
bool |
When True, the module runs regardless of detection signals |
Instance Attributes¶
| Attribute | Type | Description |
|---|---|---|
findings |
list[dict] |
Accumulated findings from scoring |
score_data |
dict |
Arbitrary scoring metadata available after score() runs |
Methods¶
analyse(html, url, headers, **kwargs) -> dict¶
Extract audit signals from HTML content.
Parameters:
| Parameter | Type | Description |
|---|---|---|
html |
str |
Raw HTML content to analyse |
url |
str |
Page URL for domain extraction and link classification |
headers |
dict |
Optional HTTP response headers |
**kwargs |
Module-specific extra arguments |
Returns: Dict of analysis results keyed by signal name. The structure is module-specific.
score(analysis) -> dict¶
Convert analysis results into a numeric score and findings list.
Parameters:
| Parameter | Type | Description |
|---|---|---|
analysis |
dict |
The dict returned by analyse() |
Returns: Dict containing at least a total key with the numeric score (0--100), plus per-criterion breakdowns.
detect(html) -> bool¶
Class method. Determine whether this module should run for the given HTML.
The default implementation returns ALWAYS_ENABLED. Override to inspect HTML for relevant signals.
add_finding(priority, title, description, fix, effort)¶
Record an actionable finding during scoring.
Parameters:
| Parameter | Type | Description |
|---|---|---|
priority |
str |
"P0" (critical) through "P3" (informational) |
title |
str |
Short one-line summary |
description |
str |
Detailed explanation |
fix |
str |
Recommended remediation |
effort |
str |
"low", "medium", or "high" |
Registry Functions¶
These are exported from scripts/modules/__init__.py:
register_module(cls)¶
Decorator that registers an AuditModule subclass.
from modules import register_module
from modules.base import AuditModule
@register_module
class MyModule(AuditModule):
MODULE_ID = "my_module"
DISPLAY_NAME = "My Module"
...
detect_modules(html, site_type, force_enable, force_disable) -> list[str]¶
Determine which modules to run. Returns a sorted list of module ID strings.
| Parameter | Type | Description |
|---|---|---|
html |
str |
HTML to scan for detection signals |
site_type |
str \| None |
Site type hint (e.g. "ecommerce", "local_business") |
force_enable |
list[str] \| None |
Module IDs to unconditionally enable |
force_disable |
list[str] \| None |
Module IDs to unconditionally disable |
get_module(module_id) -> type | None¶
Return the registered class for a module ID.
list_all_modules() -> list[dict]¶
Return metadata for every registered module. Each dict has id, name, and core keys.
Writing a Custom Module¶
from modules import register_module
from modules.base import AuditModule
@register_module
class MyCustomModule(AuditModule):
MODULE_ID = "my_custom"
DISPLAY_NAME = "My Custom Check"
ALWAYS_ENABLED = False
@classmethod
def detect(cls, html: str) -> bool:
return "my-custom-signal" in html.lower()
def analyse(self, html: str, url: str = "", headers: dict = None, **kwargs) -> dict:
# Extract signals from HTML
has_feature = "my-feature" in html
return {"has_feature": has_feature}
def score(self, analysis: dict) -> dict:
total = 100
if not analysis["has_feature"]:
total -= 20
self.add_finding(
priority="P2",
title="Missing my-feature",
description="The page lacks my-feature which improves UX.",
fix="Add the my-feature element to the page.",
effort="low",
)
return {"total": total}