This release is a pre-release and may not be stable for production use.
Merman For Python
Parse, analyze, lay out, and render Mermaid diagrams from Python without a browser or JavaScript runtime. The package ships Merman's Rust engine and exposes it through UniFFI.
Alpha: Python and native APIs may break before the stable release. The package currently targets direct UniFFI binding API
6, which is independent from the C ABI and text-measurement protocol. Install the Python wheel and native library as one artifact rather than mixing releases. This repository documents the prepared0.8.0a6source candidate; the PyPI prerelease channel is an independent publication and may resolve an older package until the matching wheel is authorized.
Install
Install the published prerelease channel from PyPI only after checking that its version matches the API you intend to use:
python -m pip install --pre merman
For alpha.6 candidate work, build the wheel and native library from the exact commit accepted by release preflight and keep those artifacts paired; do not assume that the registry command above installs the source candidate.
Published wheels currently target CPython-compatible Python 3.9+ on macOS arm64, manylinux x86_64, and Windows x86_64. A platform without a listed wheel is not an officially packaged target; supporting another target requires a native-port contribution rather than only a local pip build.
Render A Diagram
import merman
client = merman.Merman()
source = "flowchart TD\nA[Hello] --> B[World]"
svg = client.render_svg(source, None)
print(svg[:4]) # <svg
The same one-shot facade exposes render_png, render_jpeg, render_pdf, render_ascii, parse_json, layout_json, analyze_json, validate, theme and lint metadata, ASCII support grades, and the complete diagram-family capability catalog. The default wheel supports SVG, ASCII, semantic/layout operations, analysis, validation, and document analysis. Math-bearing SVG and PNG, JPEG, or PDF methods remain available for custom current-contract libraries; the default wheel raises MermanError.Binding with MISSING_CAPABILITY and the exact capability ID. MermanOperationRequestV4 plus client.execute() is the generic, descriptor-owned form of those named methods and returns binary-safe data with media type and typed operation metadata. Generic options belong in MermanOperationRequestV4.options_json; execute() has no separate options argument. The binding API is 6; MermanOperationRequestV4 retains its record name and carries an optional MermanOperationControl for cooperative cancellation and relative deadlines. ASCII capability records expose layout/width/encoding/fallback admission arrays, and ASCII output plans use schema 2 with explicit encoding.
Reuse An Engine
Construct MermanEngine directly when calls share baseline options:
engine = merman.MermanEngine(
options_json='{"svg":{"pipeline":"readable"}}',
services=None,
)
svg = engine.render_svg(source, '{"svg":{"diagram_id":"preview"}}')
facts = engine.analyze_document_facts_json(
"```mermaid\n" + source + "\n```",
"file:///workspace/README.md",
None,
)
engine.close()
options_json is optional and follows the versioned binding options schema. Invalid options and engine failures raise typed MermanError variants. Reusable request options deeply merge over the construction baseline for one operation without mutating it. They cannot change the constructor-owned runtime_policy. MermanError.Binding.kind distinguishes UNKNOWN_OPERATION from MISSING_CAPABILITY; only the latter carries a non-null, descriptor-owned capability_id.
Release wheels use deterministic runtime state and do not bundle native clock, time-zone, or random adapters. A custom source build may enable the atomic native-runtime feature and then select {"runtime_policy":"native"}. The default wheel raises the generated unsupported-operation error when native policy is requested, and runtime discovery reports concrete adapter IDs only when they are present.
Choose a profile from the shared resource decision table, then use the generated builder:
from merman import ResourceOptionsBuilder, ResourceOverrideId, ResourceProfile
resource_options = (
ResourceOptionsBuilder()
.profile(ResourceProfile.CONSTRAINED)
.limit(ResourceOverrideId.MAX_SOURCE_BYTES, 4 * 1024 * 1024)
.build()
.to_options_json()
)
svg = client.render_svg(source, resource_options)
Use CONSTRAINED for untrusted, public, or multi-tenant input; INTERACTIVE is for cooperative local editing. Leave the profile unset when a reusable request must inherit its constructor ceiling. The native CLI's default is intentionally separate (trusted-native).
Call client.runtime_catalog_json() to inspect the loaded runtime catalog and exact resource profile values instead of duplicating limits in application code. Decode client.presentation_catalog_json() for the open-ended theme preset, presentation profile, aspect, and capability-availability catalog. merman.get_runtime_catalog(client) strictly validates its flat schema 1 artifact facts, package identity, transport API, supported options/payload schema IDs, named metadata IDs, sorted stable IDs, and local output/operation relations as one atomic response. New stable IDs remain forward compatible. This direct binding API version is 6 and is independent from native C ABI and the text-measurement protocol version.
Diagnostics use schema 1 and parser facts use schema 2, independently of UniFFI binding API 6. Other facts versions are rejected at the boundary; consumers of the removed TextScan shape and Flowchart-only rich graph must migrate to generic parser-backed items and explicit unavailable bodies.
For generic operations, construct MermanOperationControl(timeout_ms=...), retain it in the host,
and put it in MermanOperationRequestV4.control. Calling cancel() from another thread requests
cooperative termination. MermanError.Binding.cancellation reports the reason and observed phase;
resource failures continue to use the separate resource field. Opaque callbacks may complete
before the next checkpoint, so hard preemption requires a worker or process boundary.
Text Measurement
Merman owns a deterministic, font-agnostic text measurer by default. Keep it for servers, CLIs, CI, and documentation builds.
GUI, browser automation, and WebView hosts can implement MermanTextMeasurer, start with MermanEngineServices(), call with_text_measurer(...), and pass the returned immutable bundle to MermanEngine(options_json, services). The original bundle remains unchanged, and the callback is immutable for that engine; construct a different engine to change or remove it. Text-measurement protocol 1 exposes 19 exact operations (0..18), and each handled MermanTextMeasureResult must use the MermanTextMeasurementResultKind required by request.operation. Return None for operations that cannot be measured synchronously and faithfully. Invalid results and Python exceptions delivered through UniFFI's generated callback trampoline fall back for that operation; Merman does not claim to catch arbitrary foreign unwinds outside that generated boundary.
Use a real font API from the surface that displays the SVG rather than estimating width from character counts. Keep callbacks fast and do not re-enter the same reusable engine while its callback is active. Callback-free engines allow concurrent operations; callback engines serialize admission and report typed BUSY to a competing caller, while same-engine callback reentry reports REENTRANT_CALL. The host measurement guide documents every operation and fallback; the repository's Python smoke example demonstrates one representative callback path while owner-local tests enforce the complete protocol.
The generated callback is measure(self, request), not measure_text. One-shot and reusable operations accept request-local options_json; reusable values deeply merge over the construction baseline for that call. The wheel builder executes the linked smoke example against the installed final wheel, so it is the authoritative copy-paste reference.
Output And Platform Limits
- SVG may contain styles, markers, and
foreignObjectHTML labels; the final viewer must support the selected SVG pipeline. - All APIs are synchronous. Run expensive rendering outside a GUI event loop.
- Wheels bundle a platform-specific native library and are not portable across operating systems or CPU families.
- Merman targets structural and semantic Mermaid compatibility; browser font rendering and DOM measurements can still differ unless the host provides matching metrics.
Query diagram_family_capabilities() and ascii_capabilities() at runtime instead of assuming that every build profile or output format supports every family.
Custom artifacts with binary exports expose MermanOutputPlan as an open record. Switch on kind,
inspect raster, pdf_filter_images, or ascii for known plans, and retain raw_json so a newer
native library can report a future plan without forcing Python into a closed enum. The ASCII plan
records the selected projection, encoding, emitted dimensions, and viewport fallback outcome.
Local Development
Build the descriptor-owned native library, regenerate bindings, assemble a wheel, install it into an isolated environment, and run the smoke test with:
python3 scripts/build-python-uniffi-wheel.py --run-smoke
The helper resolves the python-uniffi-native artifact recipe and chooses the platform library name (.dylib, .so, or .dll). The bundled release library excludes binding-generation; only source generation enables that feature. It also embeds the checked-in Rust dependency license report for the selected target rather than the union of every published wheel target. For manual binding generation, follow the UniFFI maintainer guide.
Documentation And Releases
PyPI is the supported registry channel for the Python package. Release wheels are also attached to the corresponding GitHub release; this README does not imply support for platforms without a listed wheel.
License And Notices
This package is available under MIT or Apache-2.0. The installed distribution carries the exact release license, notices, and upstream texts under its .dist-info/licenses/ directory. Online copies live in the repository's Python package directory.
Metadata
Release files for merman 0.8.0a6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| merman-0.8.0a6-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| merman-0.8.0a6-py3-none-manylinux_2_35_x86_64.whl | Python 3 | none | Linux glibc 2.35+ x86-64 | Details |
| merman-0.8.0a6-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
Total release size: 21.9 MB
Release files / merman-0.8.0a6-py3-none-win_amd64.whl
| Download URL | merman-0.8.0a6-py3-none-win_amd64.whl |
|---|---|
| Size | 7.5 MB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
88e20ace1842acfc7345c8654081d5a100a596fdc350470d722ccc7d087b4535
|
|
BLAKE2b-256 checksum How to use checksums |
d314dcb3eedb96deefc948929a6e0fd7a903fee348c26763ca44ef50bc3b232d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.
Transparency logRelease files / merman-0.8.0a6-py3-none-manylinux_2_35_x86_64.whl
| Download URL | merman-0.8.0a6-py3-none-manylinux_2_35_x86_64.whl |
|---|---|
| Size | 7.5 MB |
| Tags | Linux glibc 2.35+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
663f83696c3e8153aebd0f0d5ea9c10444bed60913f251e22437cccf05f3a629
|
|
BLAKE2b-256 checksum How to use checksums |
11a53a7b1c832aafd4c20303329a71211eba934ebb3dc9ca1e5d530ca379fd4e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.
Transparency logRelease files / merman-0.8.0a6-py3-none-macosx_11_0_arm64.whl
| Download URL | merman-0.8.0a6-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 6.9 MB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
66e55ae32edd4be5186e269698091678e3d895eba732e881afbe25ad9afffdac
|
|
BLAKE2b-256 checksum How to use checksums |
aa0078f90498c66c16b67983a968416350e9e3efa6ef7110b514dcf13c8febdc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.
Transparency log