3.1 KiB
Suggested Improvements to common/components.py
Completed
Caching on template rendering
- Added
functools.lru_cacheon_render_cached()wrapper aroundrender_to_string - Cache key:
(template_path, json.dumps(context, sort_keys=True))— deterministic and unique maxsize=4096in production, disabled entirely in DEBUG mode (so template changes are reflected immediately)- Only caches
templatepath calls;tag_namecalls are already nanosecond string ops - Verified working: identical calls return identical output, different inputs produce separate cache entries
Non-deterministic IDs
randomid() was replaced with hashlib.sha1(content_hash.encode()).hexdigest()[:10] for deterministic ID generation.
Popover()passes content hash (wrapped_content:popover_content:wrapped_classes) so IDs are deterministic per unique contentgames/templatetags/randomid.pyuses the same hash-based approach- Fixes: caching (Popover output now cacheable), page consistency, thread safety
Inconsistent return types
All component functions now return SafeText and are annotated accordingly. Redundant mark_safe() wrappers removed from LinkedPurchase() and NameWithIcon().
Fragile A() URL resolution
Replaced single url parameter with explicit url_name (URL pattern name resolved via reverse()) and href (literal path). Removed dead Callable type hint. reverse() now raises NoReverseMatch instead of silently falling back to literal text. Added mutual exclusion check — providing both parameters raises ValueError. Updated all 10 call sites across 6 view files and internal callers (LinkedPurchase(), NameWithIcon()).
Toast XSS vulnerability
The vulnerable Toast() component (which used unsafe string escaping for
Alpine.js interpolation) had no callers and was deleted entirely. Toast display
is handled by the existing event-driven pipeline: middleware → HX-Trigger
headers → show-toast CustomEvent → Alpine store.
Default mutable arguments
All functions with mutable defaults (attributes and children) changed from = [] to | None = None with or [] conversion in the body.
What was fixed: attributes: list[HTMLAttribute] = [] and children: list[HTMLTag] | HTMLTag = [] are a classic Python gotcha — the default is shared across all callers and could silently corrupt state if ever mutated in place. Changed 8 functions (Component, Popover, A, Button, Div, Input, Form, Icon) to use the None sentinel pattern, preventing future bugs and eliminating linter warnings.
Incomplete
No tests
Zero test coverage for the entire component system.
Fix: Add unit tests for each component function — basic rendering, edge cases, and cache hit/miss verification.
Done: 96 unit tests covering all component functions (Component, randomid, Popover, PopoverTruncated, A, Button, Div, Icon, Form, Input, NameWithIcon, LinkedPurchase, PurchasePrice, _render_cached, enable_cache). Includes template rendering, deterministic ID generation, LRU cache behavior, HTML output validation, edge cases, error handling, and model-dependent integration tests.