Writing a plugin¶
Add support for your instrument, daemon or algorithm without forking ntpstats. Ship it as a small Python package that declares entry points. Once it is installed, ntpstats finds it:
| Entry-point group | Object | Appears in |
|---|---|---|
ntpstats.parsers |
ntpstats.plugins.ParserPlugin |
-f/--format, auto-detection, the UI's format list |
ntpstats.estimators |
an ntpstats.estimators.Estimator (class or instance) |
ntpstats bench |
ntpstats.detectors |
ntpstats.plugins.DetectorPlugin |
ntpstats events, the UI's event list, audit --events |
ntpstats.masks |
mask text (tau,tdev CSV), a Mask, or a callable returning one |
--mask NAME |
ntpstats.profiles |
a dict with the keys of a TOML import profile | -f profile:NAME |
ntpstats plugins # what is installed; exit code 1 if a plugin failed to load
ntpstats plugins --all # including the built-ins
A parser in 30 lines¶
# my_pkg/__init__.py
import numpy as np
from ntpstats.plugins import ParserPlugin
from ntpstats.series import TimeSeries
def detect(lines): # confidence 0..1 from the first lines
return 1.0 if lines and lines[0].startswith("#TOYCSV") else 0.0
def parse(lines, name):
rows = [ln.split(";") for ln in lines[1:] if ln.strip()]
t = np.array([int(ms) / 1000 for ms, _ in rows])
x = np.array([-float(us) * 1e-6 for _, us in rows]) # local - reference -> reference - local
return [TimeSeries(t, x, name=name, source_format="toycsv")]
PARSER = ParserPlugin("toycsv", parse, detect, "Toy CSV dialect", example="#TOYCSV 1\n1790000000000;12.5\n...")
Rules:
- Conventions. Times are POSIX seconds (UTC). Offsets are reference − local, in seconds.
Extra per-sample columns go in extra.
- Detection. detect scores 0.9 or more only when sure; a plugin that is sure wins over the
built-in detection. From 0.5 it is tried before the generic CSV fallback. It must never claim
other formats with certainty.
- Built-in names. A plugin cannot replace a built-in format name.
- Failures. A plugin that raises while loading is listed by ntpstats plugins with its error
and otherwise ignored.
The built-in research formats (CGGTTS, Circular T, RIPE Atlas, …) are registered as
ParserPlugins too, so the API is used in-house.
Contract tests¶
pip install -e . # your plugin
pytest --pyargs ntpstats.testing.plugin_contract # ntpstats' checks for every installed plugin
NTPSTATS_PLUGIN=toycsv pytest --pyargs ntpstats.testing.plugin_contract # only one
The suite checks the following:
- the example parses into sorted, finite series;
- detection recognises the example and does not claim unrelated formats;
- load() and auto-detection work;
- detectors return Events;
- masks and profiles validate.
Add it to your plugin's CI.
Template¶
examples/plugins/ntpstats-toy-csv
is a complete plugin with a parser, a detector, a mask and a profile. ntpstats' own CI installs
it and runs the contract suite. Copy it, rename the package and replace the functions. Then share
it, and open an issue so it can be listed here.