Package Layout
Background and reasoning behind design decisions in splam. This page won’t tell you how to do something. See the How-To Guides for that.
Package layout
The app is packaged with a src/splam/ layout rather than flat scripts, which was necessary for great-docs (and any other tool doing static analysis) to treat it as an importable package rather than loose files.
src/splam/
├── __init__.py
├── app.py
├── auth.py
├── audit.py
├── chat.py
├── highlight.py
├── manage_users.py
├── paths.py
├── service_info.py
└── styles.css
The module split follows single responsibility:
app.py: Shiny UI/server wiring, plus thesystemctl/journalctlwrapper functions.auth.py: password hashing, verification, and login attempt/lockout state.audit.py: the append-only action log.service_info.py: the per-service guidance data (SERVICE_INFO) and its overrides.highlight.py: keyword highlighting for the Status/Logs/Audit Trail output (_PATTERNS), and its overrides.chat.py: chatbot client construction, system prompt, and the read-only tool set (build_client, chat_available, read_only_tools, system_prompt). See Explanation: Chatbot Scope.manage_users.py: thesplam-usersCLI entry point (add/listsubcommands) for managing accounts incredentials.jsonoutside the running app.paths.py: resolves the data directory (data_dir():SPLAM_DATA_DIR, elseXDG_DATA_HOME, else~/.local/share/splam) and enforces owner-only permissions on it (ensure_parent()).
The highlight name shadowing quirk
__init__.py re-exports a function called highlight from highlight.py:
from .highlight import add_highlight_keyword, highlight, highlight_termBecause the re-exported name is identical to the submodule’s own name, this line rebinds the splam.highlight attribute from the submodule to the function. In other words, after import splam, splam.highlight is <function highlight>, not the module. The module is still reachable (e.g. via importlib.import_module("splam.highlight"), sys.modules, or from splam import highlight as _; import splam.highlight as highlight_mod), but plain attribute access no longer finds it.
This is intentional/accepted, not a bug to fix. highlight is the natural public name for the function, and the tests already work around it (tests/conftest.py, tests/test_highlight.py both import the submodule via importlib.import_module("splam.highlight") with a comment explaining why). Just be aware of it if you add new tests or tooling that touches splam.highlight.
It also affects great-docs: when great-docs.yml’s API reference config resolves the bare name highlight, dynamic introspection resolves it to the submodule (not the function) and, under dynamic: true, fails to filter out the module’s own imports (data_dir, ensure_parent from paths.py) as aliases. Left alone, this leaks spurious pages into the API reference (highlight.data_dir, highlight.ensure_parent, plus the module’s constants) and prints a “Dynamic introspection failed” warning during great-docs build. The fix lives in great-docs.yml, which overrides dynamic: false for just that one reference entry so static analysis (which correctly identifies the imports as aliases) is used instead:
reference:
- title: Functions
contents:
- ...
- name: highlight
dynamic: false
- highlight_term
- ...If you rename or restructure highlight.py, or add another submodule whose name collides with one of its own re-exported members, check whether this override is still needed (or needs to move).