Skip to main content

the maned mark, in Python's colors

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:

  1. 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");
  1. Save this as scoring.py next 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"])
  1. 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.

a maned wolf with two snakes

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.CDLL releases the GIL, which is the point — but it also means a KeyboardInterrupt is not delivered until the native call returns. Ctrl-C in a REPL and "interrupt kernel" in Jupyter do nothing until then. Use multiprocessing with spawn for work long enough to want to cancel.
  • Relative data paths in .mnd files resolve against the process CWD, not the script's directory. maned-serve behaves 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_threads is process-global; it changes timing, not values.
  • requests_served is 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.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for manedpy 0.4.2
File Size Uploaded Provenance Actions
manedpy-0.4.2.tar.gz 36.2 kB Without provenance Details for manedpy-0.4.2.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for manedpy 0.4.2
File Interpreter ABI Platform Actions
manedpy-0.4.2-py3-none-manylinux_2_35_x86_64.whl Python 3 none Linux glibc 2.35+ x86-64 Details for manedpy-0.4.2-py3-none-manylinux_2_35_x86_64.whl
manedpy-0.4.2-py3-none-manylinux_2_35_aarch64.whl Python 3 none Linux glibc 2.35+ ARM64 Details for manedpy-0.4.2-py3-none-manylinux_2_35_aarch64.whl
manedpy-0.4.2-py3-none-macosx_11_0_universal2.whl Python 3 none macOS 11.0+ universal2 (ARM64, x86-64) Details for manedpy-0.4.2-py3-none-macosx_11_0_universal2.whl

Total release size: 9.6 MB

Release files / manedpy-0.4.2.tar.gz

Download URL manedpy-0.4.2.tar.gz
Size 36.2 kB
Tags Source
SHA-256 checksum
How to use checksums
a874ec92dd6cfa982e87d8db4655295e08dc407bd735999602a5fd0f89056d0f
BLAKE2b-256 checksum
How to use checksums
102e866bcd7e724a0fc1d80eb8639e55121e39364e484269edcbacf4b25ec2be
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.2-py3-none-manylinux_2_35_x86_64.whl

Download URL manedpy-0.4.2-py3-none-manylinux_2_35_x86_64.whl
Size 3.8 MB
Tags Linux glibc 2.35+ x86-64 Python 3
SHA-256 checksum
How to use checksums
754850d30c65207cc6602b6601c7478670369b96efbf945324bf7a1e0319c317
BLAKE2b-256 checksum
How to use checksums
5d6c11d31d7844e89a454408354c447b9dcd4b7e7214cb77d62ce728da918386
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.2-py3-none-manylinux_2_35_aarch64.whl

Download URL manedpy-0.4.2-py3-none-manylinux_2_35_aarch64.whl
Size 3.7 MB
Tags Linux glibc 2.35+ ARM64 Python 3
SHA-256 checksum
How to use checksums
2ad836269fc9ef6bbeeff8a737ddc95de886fc6d0f35a717cf08c38766f82f8c
BLAKE2b-256 checksum
How to use checksums
8edfe3f0b55f4309ed8012eebcf0483de7a885466aee4d5293e89b57f5220c0f
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.2-py3-none-macosx_11_0_universal2.whl

Download URL manedpy-0.4.2-py3-none-macosx_11_0_universal2.whl
Size 2.2 MB
Tags Python 3 macOS 11.0+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
bb58d450e347152452afd8da455dea97f3d51fc8c2cbc8c1ce6411d62d44d109
BLAKE2b-256 checksum
How to use checksums
718f7120642009f3cdb7167225c7afee0aa4d009b7005184441439ff83ea320c
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.2

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page