Handlers¶
Handlers execute rule definitions from src/azure_functions_doctor/assets/rules/v2.json.
Each rule has a type, and HandlerRegistry routes the rule to the corresponding
handler implementation in src/azure_functions_doctor/handlers.py.
Contract¶
- Input:
ruleand projectpath - Output: a dictionary with
statusanddetail - Status values:
passorfail
Optional rules are converted to warn later in the aggregation layer.
Rule Input Contract¶
Rules are JSON objects validated by the schema in
src/azure_functions_doctor/schemas/rules.schema.json.
The minimum practical structure for handler execution is:
{
"id": "check_example",
"type": "file_exists",
"label": "host.json",
"required": true,
"condition": {
"target": "host.json"
}
}
Handler Output Contract¶
Handlers return a normalized dictionary:
| Key | Type | Meaning |
|---|---|---|
status |
"pass" or "fail" |
Raw check result before optional-to-warn mapping. |
detail |
str |
Human-readable diagnostic detail used in reports. |
internal_error |
"true" (optional) |
Present when an internal exception is captured. |
HandlerRegistry Pattern¶
HandlerRegistry centralizes dispatch so each rule type maps to one method.
This keeps Doctor focused on orchestration while handlers focus on evaluation.
Dispatch flow:
Doctor.run_all_checks()passes each rule togeneric_handler(rule, path).generic_handlerforwards execution to a globalHandlerRegistryinstance.HandlerRegistry.handle()resolvesrule["type"].- A concrete
_handle_*method returns{"status": ..., "detail": ...}. Doctormaps optional failures towarnand builds section results.
Built-in Handlers¶
compare_versionfile_existsenv_var_existspath_existspackage_installedpackage_declaredsource_code_containsconditional_existscallable_detectionexecutable_existsany_of_existsfile_glob_checkhost_json_property
Handler Reference¶
| Handler Type | Condition Keys | Typical Use |
|---|---|---|
compare_version |
target, operator, value |
Python version and Core Tools version checks. |
env_var_exists |
target |
Environment variable presence checks. |
path_exists |
target |
Check concrete paths or sys.executable. |
file_exists |
target |
Required project files (host.json, requirements.txt). |
package_installed |
target |
Validate importable module availability. |
package_declared |
package, optional file |
Confirm package declaration in dependency file. |
source_code_contains |
keyword, optional mode |
Detect decorators or source signals. |
conditional_exists |
jsonpath |
Conditional host checks (for example Durable settings). |
callable_detection |
none | Detect ASGI/WSGI callable exposure patterns. |
executable_exists |
target |
Ensure local binaries exist on PATH. |
any_of_exists |
targets |
Pass when any env/file/host signal is present. |
file_glob_check |
patterns |
Detect junk files and deployment artifacts. |
host_json_property |
jsonpath |
Validate specific host.json properties. |
Example Rule JSON by Handler Type¶
[
{
"id": "python_min",
"type": "compare_version",
"condition": {"target": "python", "operator": ">=", "value": "3.10"}
},
{
"id": "venv_active",
"type": "env_var_exists",
"condition": {"target": "VIRTUAL_ENV"}
},
{
"id": "python_path",
"type": "path_exists",
"condition": {"target": "sys.executable"}
},
{
"id": "host_file",
"type": "file_exists",
"condition": {"target": "host.json"}
},
{
"id": "module_installed",
"type": "package_installed",
"condition": {"target": "azure.functions"}
},
{
"id": "package_declared",
"type": "package_declared",
"condition": {"package": "azure-functions", "file": "requirements.txt"}
},
{
"id": "decorator_signal",
"type": "source_code_contains",
"condition": {"keyword": "@app.", "mode": "ast"}
},
{
"id": "durable_host",
"type": "conditional_exists",
"condition": {"jsonpath": "$.extensions.durableTask"}
},
{
"id": "asgi_wsgi",
"type": "callable_detection",
"condition": {}
},
{
"id": "func_cli",
"type": "executable_exists",
"condition": {"target": "func"}
},
{
"id": "telemetry_any",
"type": "any_of_exists",
"condition": {
"targets": [
"APPLICATIONINSIGHTS_CONNECTION_STRING",
"APPINSIGHTS_INSTRUMENTATIONKEY",
"host.json:instrumentationKey"
]
}
},
{
"id": "junk_files",
"type": "file_glob_check",
"condition": {"patterns": ["**/*.pyc", "**/__pycache__"]}
},
{
"id": "extension_bundle",
"type": "host_json_property",
"condition": {"jsonpath": "$.extensionBundle"}
}
]
Notes¶
source_code_containssupports a simple string mode and an AST-based mode.conditional_existsis used for checks that only matter when a related feature is detected.- Handler implementations live in
src/azure_functions_doctor/handlers.py.
Programmatic Usage Examples¶
Execute One Rule Through HandlerRegistry¶
from pathlib import Path
from azure_functions_doctor.handlers import HandlerRegistry
def run_host_check(project_path: str) -> dict[str, str]:
registry = HandlerRegistry()
rule = {
"id": "host_file",
"type": "file_exists",
"label": "host.json",
"required": True,
"condition": {"target": "host.json"},
}
return registry.handle(rule=rule, path=Path(project_path))
Register a Custom Handler in a Subclass¶
from pathlib import Path
from azure_functions_doctor.handlers import HandlerRegistry
class ExtendedRegistry(HandlerRegistry):
def __init__(self) -> None:
super().__init__()
self._handlers["always_pass"] = self._handle_always_pass
def _handle_always_pass(self, rule: dict, path: Path) -> dict[str, str]:
_ = (rule, path)
return {"status": "pass", "detail": "Custom handler executed"}
def run_custom_rule(project_path: str) -> dict[str, str]:
registry = ExtendedRegistry()
custom_rule = {
"id": "custom_demo",
"type": "always_pass",
"condition": {},
}
return registry.handle(custom_rule, Path(project_path))
Development¶
When adding a new handler:
- Extend the
Rule["type"]literal - Register the handler in
HandlerRegistry - Update
rules.schema.json - Add tests in
tests/test_handler.py