Splitting a script across multiple files¶
Once a script grows past a few handlers you'll want to move shared helpers into
their own .py files. siphon puts the script's own directory on the Python
sys.path, so a plain import of a sibling module just works — no
sys.path.insert boilerplate.
Sibling helper next to the script¶
# helpers.py
def normalize_number(raw: str) -> str:
digits = "".join(c for c in raw if c.isdigit())
if digits.startswith("00"):
return "+" + digits[2:]
return digits
# main.py
from siphon import proxy, log
import helpers
@proxy.on_request("INVITE")
def on_invite(request):
request.ruri.user = helpers.normalize_number(request.ruri.user or "")
request.relay()
Point the config at the main script as usual — nothing else is needed:
Shared library across scripts¶
For helpers shared by several scripts (or several NFs) that don't live next to
any one script, list the directories in include_paths. They're added to
sys.path after the script's own directory.
script:
path: "/etc/siphon/pcscf.py"
include_paths:
- "/etc/siphon/lib" # e.g. /etc/siphon/lib/ims_common.py
Hot-reload¶
Helper modules hot-reload exactly like the main script. Editing and saving
helpers.py (or anything under an include_paths directory) triggers a reload,
and siphon re-imports the helper from its new source — you don't have to touch
main.py to pick up a helper change.
Rules and limits¶
- Absolute imports only. The main script runs as a plain module, not a
package, so
from . import helpersdoes not work — useimport helpers. - No cross-request state in helpers. The same rule as the main script: don't
keep per-call state in module-level dicts/lists (it isn't shared across the
worker threads or replicas and is wiped on reload). Use the
cachenamespace for shared state. Pure functions and constants are fine. - A helper named after a stdlib module shadows it — the same foot-gun as a
normal
python script.py. Give helpers distinct names (sip_helpers.py, notemail.py).