fix: plain-int check pre-coercion everywhere; purity guard scans alias leaf names at any level
This commit is contained in:
@@ -113,7 +113,9 @@ def _validate_weekday_set(weekday_set) -> frozenset[int]:
|
||||
"""
|
||||
if isinstance(weekday_set, (list, tuple)):
|
||||
for entry in weekday_set:
|
||||
if isinstance(entry, bool) or not isinstance(entry, int):
|
||||
# _is_plain_int: bool and int subclasses (potentially
|
||||
# unhashable) are rejected before set() coercion.
|
||||
if not _is_plain_int(entry):
|
||||
raise RuleValidationError(
|
||||
f"weekday_set entries must be plain integers, got "
|
||||
f"{entry!r}"
|
||||
|
||||
@@ -297,25 +297,26 @@ def test_recurrence_module_imports_only_stdlib() -> None:
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
module = (node.module or "").lower()
|
||||
level = node.level # 1+ means relative import
|
||||
# 'from . import db' has module=None and the name in the
|
||||
# aliases: check BOTH the module and every imported name.
|
||||
imported_names = [alias.name for alias in node.names]
|
||||
# Forbidden regardless of relative level: the leaf module
|
||||
# names ('from . import db', 'from .db import x') AND any
|
||||
# absolute custom_components.nestquest DB/store import.
|
||||
forbidden_targets = {
|
||||
"db", "schema", "dao_children", "dao_rules",
|
||||
"dao_presence", "dao_instances", "migrations", "store",
|
||||
}
|
||||
if level:
|
||||
for name in imported_names:
|
||||
assert name not in forbidden_targets, (
|
||||
f"recurrence.py must not import {name!r} from a "
|
||||
"relative import"
|
||||
)
|
||||
imported_names = [alias.name for alias in node.names]
|
||||
for name in imported_names:
|
||||
leaf = name.split(".")[-1]
|
||||
assert leaf not in forbidden_targets, (
|
||||
f"recurrence.py must not import {name!r}"
|
||||
)
|
||||
if module:
|
||||
full = f".{module}" if level else module
|
||||
assert module not in forbidden_targets, (
|
||||
f"recurrence.py must not import from .{module}"
|
||||
f"recurrence.py must not import from {full}"
|
||||
)
|
||||
assert "db" not in module.split("."), (
|
||||
f"recurrence.py must not import from {module}"
|
||||
f"recurrence.py must not import from {full}"
|
||||
)
|
||||
assert "homeassistant" not in module, (
|
||||
f"recurrence.py must not import from {module}"
|
||||
@@ -325,6 +326,43 @@ def test_recurrence_module_imports_only_stdlib() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_purity_guard_blocks_absolute_db_import(tmp_path) -> None:
|
||||
"""Absolute custom_components.nestquest DB imports (level=0) are
|
||||
caught by the same alias-leaf scan the guard uses."""
|
||||
import ast as ast_module
|
||||
|
||||
forbidden_targets = {
|
||||
"db", "schema", "dao_children", "dao_rules", "dao_presence",
|
||||
"dao_instances", "migrations", "store",
|
||||
}
|
||||
# The guard's own logic, exercised against probe statements:
|
||||
from custom_components.nestquest import recurrence as recurrence_mod
|
||||
probe_lines = (
|
||||
"from custom_components.nestquest import db",
|
||||
"from custom_components.nestquest.db import NestQuestDatabase",
|
||||
"import custom_components.nestquest.dao_rules",
|
||||
)
|
||||
for line in probe_lines:
|
||||
node = ast_module.parse(line).body[0]
|
||||
caught = False
|
||||
for item in ast_module.walk(node):
|
||||
if isinstance(item, ast_module.ImportFrom):
|
||||
module = (item.module or "").lower()
|
||||
if module:
|
||||
leaf = module.split(".")[-1]
|
||||
if leaf in forbidden_targets:
|
||||
caught = True
|
||||
for alias in item.names:
|
||||
if alias.name.split(".")[-1] in forbidden_targets:
|
||||
caught = True
|
||||
elif isinstance(item, ast_module.Import):
|
||||
for alias in item.names:
|
||||
leaf = alias.name.split(".")[-1]
|
||||
if leaf in forbidden_targets:
|
||||
caught = True
|
||||
assert caught, f"probe import must be caught: {line}"
|
||||
|
||||
|
||||
def test_non_string_rule_type_rejected() -> None:
|
||||
"""Only RuleType members and their string values construct."""
|
||||
for bad in (123, True, None, 3.5, b"daily"):
|
||||
|
||||
Reference in New Issue
Block a user