CodeNSM Python SDK
Treat every Python function like an employee. The SDK auto-instruments the functions and methods in your packages — no decorating each one — and tracks how often each runs, how reliably, and how slowly. Those signals roll up, in the cloud, into departments, KPIs and a single North-Star Metric on your dashboard.
1 · Install
pip install https://thinknorth.consulting/static/codensm/codensm-0.3.0-py3-none-any.whl
Hosted build (interim). pip install codensm from PyPI is coming. Pure standard library — no third-party dependencies pulled into your app. Python 3.8+. New in 0.3.0: mode="full" — optional local static analysis that unlocks flow diagrams, complexity signals and the IP split (details below). From 0.2.0: code-location reporting (file/line, once per function), a 4,000-function buffer guardrail, and automatic back-off on plan-quota 429s.
Previous build: codensm-0.2.0-py3-none-any.whl remains hosted at the same path if you need to pin it.
2 · Quickstart
Call init() once, as early as possible at startup — before your app's submodules import, so the import hook can wrap them.
import codensm codensm.init( api_key="cnsm_…", # Project → ingest key, from your dashboard include=["myapp"], # your own top-level packages weights={"myapp.checkout": 9}, # optional business value-weights )
That's it. Every function/method under myapp — already imported or imported later — is now tracked. Nothing else to wire.
3 · Configuration
| Option | Default | What it does |
|---|---|---|
| api_key | — | Required. Your project's ingest key (Bearer token). |
| include | — | Required. Package prefixes to auto-instrument, e.g. ["myapp"]. |
| endpoint | …/codensm/ingest | Ingest URL. Defaults to hosted CodeNSM. |
| exclude | () | Prefixes to skip even if under include (hot inner loops, etc.). |
| department | auto | callable(module)->str or {prefix: dept}. Defaults to the package path. |
| weights | 1.0 | {name-or-prefix: value_weight} — business value per successful call (longest-prefix match). |
| flush_interval | 15.0 | Seconds between background flushes. |
| instrument | True | Set False to only enable @track (no auto-wrap). |
| mode | "names" | "names" sends function names + counters only. "full" adds local control-flow analysis — see full-code insight. |
4 · Overriding or opting in one function
Use the decorator only to override a function's department/weight, or to instrument something outside include.
@codensm.track(department="payments", value_weight=9) def charge(card): ...
5 · Short-lived scripts
The flusher runs in the background and on exit; force it when a script ends fast:
codensm.flush() # send now codensm.shutdown() # final send + stop
6 · What leaves your process (and what never does)
CodeNSM is deliberately a thin collector. The interpretation happens in the cloud — your machine ships only lightweight counters.
Sent
Never sent
Wrappers never swallow exceptions or change return values — your behaviour is unchanged. Counters live in memory; a daemon thread batches deltas every flush_interval seconds; a failed send is retried (merged back, never dropped).
7 · Full-code insight — mode="full" (new in 0.3.0)
Pass mode="full" to init() and the SDK also runs static analysis on your machine, using only the standard-library ast module. It parses each of your modules as it imports and extracts the control-flow shape of every function — never the source itself:
| Analysed locally | What ships |
|---|---|
| Control-flow graph | Node/edge shape only — block, decision, loop, end/error node kinds with generic labels. |
| Complexity signals | Decision, loop and return counts; node count; line count. |
| Call edges | calls_out — names of functions called, filtered to your own include packages (max 40). |
| Test scan | A background scan of your test files for each function name — a has_tests verdict, nothing else. |
Flow facts ride along with the normal counter flush as a flows array:
{"flows":[
{"name":"myapp.pay.charge",
"nodes":[{"type":"start"},{"type":"decision"},{"type":"block"},{"type":"end"}],
"edges":[[0,1],[1,2],[2,3]],
"decisions":1,"loops":0,"returns":1,"nodes_count":4,"lines":18,
"calls_out":["myapp.pay.validate","log_event"],
"has_tests":true}
]}
No argument values, no literals, no statements, no source text — the payload above is the complete shape of what full mode adds. Your source never leaves your machine.
mode="names" (default) | mode="full" | |
|---|---|---|
| captured | Function names + counters (calls, errors, latency). | Names + counters, plus control-flow shape, call edges, has-tests. |
| unlocked | Office, live states, NSM dashboard, employees table, debt trajectories, AI fix prompts. | All of the left, plus BPMN-style flow diagrams, complexity signals, the unique-IP vs boilerplate split, and test-coverage reads. |
| overhead | One thin wrapper per call. | Same, plus a one-off ast parse per module at import and one background test scan. |
8 · Quotas, limits & back-off
Plans meter tracked calls per calendar month (Free 100k · Team 5M · Scale 50M — see pricing). The ingest API enforces:
| Limit | Behaviour |
|---|---|
| 512 KB body / 2,000 functions per payload | HTTP 413 with a clear error — flush more often or scope include tighter. |
| 100–120% of monthly quota | Accepted, month flagged as overage — a spike never blinds you mid-incident. |
| >120% of monthly quota | HTTP 429 {"error":"quota_exceeded"}. Clients should back off. |
The Python SDK handles this for you: on a 429 it merges the batch back (nothing is lost) and doubles its flush interval, up to 5 minutes, for the rest of the session. It also caps its in-process buffer at 4,000 distinct functions — beyond that, brand-new names are dropped and counted (reported as "_meta":{"dropped":n}, which the server accepts and ignores).
9 · Any other language
The SDK is just a client for one HTTP contract. Push per-function aggregate deltas from anything:
POST https://thinknorth.consulting/codensm/ingest
Authorization: Bearer <project ingest key>
Content-Type: application/json
{"functions":[
{"name":"checkout.apply_coupon","department":"engine",
"value_weight":9,"calls":1240,"errors":3,"total_ms":47000,
"file":"checkout/coupons.py","line":42}
]}
file/line are optional and stored once (set-if-empty) — they power the file guess inside AI fix prompts and the GitHub commit mapping.
10 · Where it shows up
Calls, errors and latency roll up — in the cloud — into per-department KPIs, a debt-that-hurts heatmap, underuse detection, and a single North-Star Metric, all on your project dashboard. Functions appear as they're actually called.
11 · Overhead
One thin wrapper call per instrumented call. Scope include tightly, use exclude for very hot inner loops, and remember dunder methods are skipped. Sync, async def, and instance/class/static methods are all covered.