manedpy
Call .mnd scripts from Python, in-process — the JSON document /run.json returns,
minus the socket.
from manedpy import maned as mnd
payload = {"x": [[2, 0], [0, 2]], "w": [[3, 4], [5, 6]]}
result = mnd.io.scoring(payload)
scoring.mnd must declare in::x and in::w: a payload key with no matching in:: is a
400. mnd.load(path) is the real API and mnd.io.<name> is sugar over it.
A response computed here is byte-identical to what maned-serve answers over HTTP for
the same script and body — that is a CI fact, not a promise: tests/test_rest_parity.py
launches maned-serve, POSTs the same bytes, and compares.
Quickstart
pip install manedpy
The wheel bundles the engine library — nothing else to install (macOS universal, Linux x86-64/arm64; Python ≥3.9). Then, step by step:
- Save a script as
scoring.mnd:
mnd::quantmax=30000;
mnd::quantmin=-30000;
mnd::quantres=0;
in::w = [[1,2],[0,1]];
calc::lambda_flow scoring(x, w) {
w x matmul y =
} return y;
out::y = csv("y.csv");
- Save this as
scoring.pynext to it:
from manedpy import maned as mnd
script = mnd.load("scoring.mnd")
result = script.run({"x": [[2, 0], [0, 2]]})
print("y =", result.outputs["y"])
- Run it:
$ python scoring.py
y = [[2, 4], [0, 2]]
The same pair lives in examples/, runnable as
python examples/scoring.py — alongside three deep dives:
files.py (file routes: bytes in, output files back), decimals.py
(exact decimals), routed.py (the local Bark VM).
Installing from a source checkout
A wheel bundles libmaned_c in manedpy/_lib/. In a source checkout that directory is
empty, so point $MANEDPY_LIB at a libmaned_c from a maned-capi-* tarball:
export MANEDPY_LIB=/path/to/maned-capi-*/lib/libmaned_c.dylib
The library is loaded by absolute path with ctypes.CDLL — no @rpath, no RPATH, no
LD_LIBRARY_PATH. On load, manedpy checks that mnd_abi_version() falls in the range it
accepts and asserts its mnd_response layout against the maned_c_abi.json shipped beside
the library, which is what catches a stale or wrong-architecture $MANEDPY_LIB.
The API
| Python | Route |
|---|---|
s.run(payload) / s(payload) |
POST /run.json → Result |
s.frame(payload) |
POST /frame → Result |
s.frame_csv(payload) |
POST /frame.csv → bytes |
s.run_files(**named_bytes) |
POST /run multipart → {name: bytes} — the only way to get real PNG bytes back |
s.health() |
GET /health → dict |
s.inputs / s.outputs / s.routes |
cached from /health |
s.request(method, target, content_type, body) |
the raw escape hatch: status, bytes, notes, nothing raised |
Result is a dict subclass, so result["outputs"]["a"] and json.dumps(result) both
work, plus .outputs, .profiles, .frames, .notes, .raw and .status.
Errors are a hierarchy under ManedError, all carrying .status, .body and .script:
400/413/431 → ManedInputError (also a ValueError), 404/405 → ManedRouteError, 500 →
ManedRuntimeError (also a RuntimeError), 501 → ManedUnsupportedError, a load failure →
ManedLoadError, and status 0 → ManedInternalError, which means an exception escaped
the engine rather than any HTTP condition.
Scripts are cached on (realpath, mtime, size, flags), so editing a .mnd and re-running
in the same REPL picks the change up. mnd.load(..., reload=True) and mnd.clear_cache()
override that.
Threading
A Script takes a lock per call by default, which makes the routed case correct by
construction. Non-routed scripts are provably safe to call concurrently on one handle, so
mnd.load(path, concurrent=True) drops the lock — and is refused at load time if the
script routes to a device.
Licensing — read this before packaging
The Python source in this repo is Apache-2.0. A built wheel is mixed-licence:
it bundles libmaned_c, a proprietary-but-redistributable binary covered by section 3 of
the maned LICENSE (shipped beside it as LICENSE-libmaned_c). pyproject.toml must
use license-files listing both — never a bare Apache SPDX expression, which would
misdescribe the wheel.
Known limits (by design, v1)
- Calls cannot be interrupted.
ctypes.CDLLreleases the GIL, which is the point — but it also means aKeyboardInterruptis not delivered until the native call returns. Ctrl-C in a REPL and "interrupt kernel" in Jupyter do nothing until then. Usemultiprocessingwithspawnfor work long enough to want to cancel. - Relative data paths in
.mndfiles resolve against the process CWD, not the script's directory.maned-servebehaves the same way; load errors say so. Prefer absolute paths in scripts. - Windows is a non-goal. The serve and net layers are POSIX throughout.
set_kernel_threadsis process-global; it changes timing, not values.requests_servedis not exposed: in-process it counts calls on one handle, which looks like a server metric and is not one.
Tests
python3 -m venv .venv && .venv/bin/pip install pytest
MANEDPY_LIB=/path/to/capi/lib/libmaned_c.dylib .venv/bin/python -m pytest
The suite walks the conformance corpus shipped in the capi tarball (<case>.mnd +
.request.json + .expect.json, compared byte-for-byte), runs the REST-parity gate against
a maned-serve subprocess ($MANED_SERVE, or a sibling binary in the workspace), and
diffs _ffi.SIGNATURES against maned_c.h so the ctypes table cannot drift from the header.
Metadata
Release files for manedpy 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| manedpy-0.4.1.tar.gz | 36.2 kB | Without provenance | Details for manedpy-0.4.1.tar.gz |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| manedpy-0.4.1-py3-none-manylinux_2_35_x86_64.whl | Python 3 | none | Linux glibc 2.35+ x86-64 | Details for manedpy-0.4.1-py3-none-manylinux_2_35_x86_64.whl |
| manedpy-0.4.1-py3-none-manylinux_2_35_aarch64.whl | Python 3 | none | Linux glibc 2.35+ ARM64 | Details for manedpy-0.4.1-py3-none-manylinux_2_35_aarch64.whl |
| manedpy-0.4.1-py3-none-macosx_11_0_universal2.whl | Python 3 | none | macOS 11.0+ universal2 (ARM64, x86-64) | Details for manedpy-0.4.1-py3-none-macosx_11_0_universal2.whl |
Total release size: 8.9 MB
Release files / manedpy-0.4.1.tar.gz
| Download URL | manedpy-0.4.1.tar.gz |
|---|---|
| Size | 36.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c8a622b115d7dcb27e54a1270f037c7962138f852959a862e2c2ed400bf3d10d
|
|
BLAKE2b-256 checksum How to use checksums |
f0b43c8cb349b8e39f9a42473ee672a17abfbd96da3b9a9840d1a6779dfea2eb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / manedpy-0.4.1-py3-none-manylinux_2_35_x86_64.whl
| Download URL | manedpy-0.4.1-py3-none-manylinux_2_35_x86_64.whl |
|---|---|
| Size | 3.5 MB |
| Tags | Linux glibc 2.35+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
525f862920c9f7afa7d31c303c23bb89ad8da753c9f82a281d6fba99a6f0d86c
|
|
BLAKE2b-256 checksum How to use checksums |
1d04a7f3849a9daffe120d6ac6b070c600592dae5024a10947eecb6ec628dea0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / manedpy-0.4.1-py3-none-manylinux_2_35_aarch64.whl
| Download URL | manedpy-0.4.1-py3-none-manylinux_2_35_aarch64.whl |
|---|---|
| Size | 3.4 MB |
| Tags | Linux glibc 2.35+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
93a6e228148ff25962df83bdbc775730a47369bafbafc8ba3b7353d70a6089c5
|
|
BLAKE2b-256 checksum How to use checksums |
45ca495da56730acc18042590efdc2f44e1f268fb125651adba9414bb06d019a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / manedpy-0.4.1-py3-none-macosx_11_0_universal2.whl
| Download URL | manedpy-0.4.1-py3-none-macosx_11_0_universal2.whl |
|---|---|
| Size | 2.0 MB |
| Tags | Python 3 macOS 11.0+ universal2 (ARM64, x86-64) |
|
SHA-256 checksum How to use checksums |
f92b8fc9f086f15dd2c0403aeafc624964e7fe311d9ecb82d66c2bd8065e2f6a
|
|
BLAKE2b-256 checksum How to use checksums |
7bb231f6fde9944cc0fbe8100425038e674a151c6b02a5484dbe6f601645a0ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Security for manedpy 0.4.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.