Quick Start#
This is the fastest path to a real SMonitor integration.
Goal#
In this page you will:
configure SMonitor for your library,
emit a catalog-based warning,
decorate one public function for traceability.
At the end, you should see clear user-facing diagnostics and structured context.
1. Install#
conda install -c uibcdf smonitor
or from source:
python -m pip install --no-deps --editable .
2. Create _smonitor.py in your package root#
If your package is mylib, create mylib/_smonitor.py:
from mylib._private.smonitor.catalog import CODES, SIGNALS
PROFILE = "user"
SMONITOR = {
"level": "WARNING",
"trace_depth": 3,
"capture_warnings": True,
"capture_logging": True,
"theme": "plain",
}
# Keep runtime templates/contracts in one source of truth.
CODES = CODES
SIGNALS = SIGNALS
3. Create the catalog#
Create mylib/_private/smonitor/catalog.py:
CATALOG = {
"codes": {
"MYLIB-W001": {
"title": "Selection is broad",
"user_message": "Selection '{selection}' may be too broad.",
"user_hint": "Use a more specific selector, for example '{example}'.",
"dev_message": "Broad selection received in parser path.",
"dev_hint": "Review selector normalization for this call.",
}
},
"warnings": {
"BroadSelectionWarning": {
"code": "MYLIB-W001",
"source": "mylib.select_atoms",
"category": "selection",
"level": "WARNING",
}
},
"signals": {},
}
CODES = CATALOG["codes"]
SIGNALS = CATALOG["signals"]
codes holds the wording, one entry per profile. warnings maps a warning class
to the code it carries, plus the source and category every event of that kind
should report. DiagnosticBundle looks a warning up here by its class name, so a
catalog without a warnings group emits nothing through warn().
Create mylib/_private/smonitor/__init__.py:
from pathlib import Path
from .catalog import CATALOG, CODES, SIGNALS
PACKAGE_ROOT = Path(__file__).resolve().parents[2]
Create mylib/_private/smonitor/emitter.py:
from smonitor.integrations import DiagnosticBundle
from . import CATALOG, PACKAGE_ROOT
bundle = DiagnosticBundle(CATALOG, meta={"library": "mylib"}, package_root=PACKAGE_ROOT)
warn = bundle.warn
warn_once = bundle.warn_once
Create mylib/_private/smonitor/warnings.py, one class per catalog entry:
from smonitor.integrations import CatalogWarning
from . import CATALOG
class BroadSelectionWarning(CatalogWarning):
catalog_key = "BroadSelectionWarning"
def __init__(self, message=None, *, selection=None, example=None):
super().__init__(
message,
catalog=CATALOG,
extra={"selection": selection, "example": example},
)
Two rules, and both matter:
The class takes the data, never the finished sentence. The template in codes
owns the wording, and selection reaches report() and the event fingerprints
as a typed field.
message comes first and the fields are keyword-only. Python rebuilds a warning
as type(w)(*w.args) — pickle, copy.deepcopy, warnings.warn(text, category) and pytest-xdist between a worker and the controller all do it — so a
class naming a field first receives its own rendered sentence as that field and
renders around it. Keyword-only keeps a misspelled field an error instead of one
that is silently ignored.
warn() raises the warning through Python’s machinery as well as emitting the
event, so pytest.warns and your users’ filterwarnings keep working on it.
4. Enable SMonitor on import#
In mylib/__init__.py:
from smonitor.integrations import ensure_configured
from ._private.smonitor import PACKAGE_ROOT
ensure_configured(PACKAGE_ROOT)
5. Emit via catalog and instrument one API function#
from smonitor import signal
from ._private.smonitor.emitter import warn
from ._private.smonitor.warnings import BroadSelectionWarning
@signal(tags=["selection"])
def select_atoms(selection: str):
if selection == "all":
warn(BroadSelectionWarning(selection=selection, example="atom_name == 'CA'"))
Expected result#
When selection == "all", users get a warning that:
explains what happened,
suggests a concrete fix,
stays consistent with your library profile.
Quick smoke command#
python -c "import mylib; from mylib.api import select_atoms; select_atoms('all')"
Expected:
import does not fail,
one warning event with
MYLIB-W001,message/hint resolved from catalog.
Common wiring mistakes#
_smonitor.pyin repo root instead of package root:
correct path is
mylib/_smonitor.py.
PACKAGE_ROOTpointing to wrong level:
use
Path(__file__).resolve().parents[2]frommylib/_private/smonitor/__init__.py.
Catalog not imported into
_smonitor.py:
ensure
CODESandSIGNALSare available at runtime.
Circular imports during package init:
keep
_private/smonitormodules lightweight and avoid importing heavy modules there.
You are done when#
importing
mylibconfigures SMonitor without errors,calling
select_atoms("all")emitsMYLIB-W001,message and hint are resolved from catalog templates.
Next#
Continue with Mini Library Walkthrough for a complete example.