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.1

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.1
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)

Table of built distributions (wheels) for manedpy 0.4.1
File Interpreter ABI Platform Actions
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.

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