Skip to main content

Typed Markdown templating: render one or more Markdown strings from an .mdma template and an inputs object.

Project description

python-mdma

Render one or more Markdown strings from an .mdma template and a typed inputs object.

This is the Python reference implementation of MDMA. See the language specification and docs for the full grammar, filter reference, and worked examples.

Install

pip install -e .

Usage

from mdma import render

source = open("release-notes.mdma").read()
result = render(source, {
    "project": "Acme SDK",
    "version": "3.0.0",
    "date": "2026-07-01",
    "added": ["WebSocket support"],
    "breaking": True,
    "releases": [{"version": "2.1.0", "date": "2026-06-01", "added": ["Dark mode"]}],
})

result["slug"]             # "Acme SDK-3.0.0"          (str)
result["release-notes"]    # rendered markdown          (str)
result["changelog-entry"]  # one string per release     (list[str], from `<multiple:>`)

A <multiple:> block can also declare <name:> to key each item by a computed name instead of array position:

<changelog-by-version>
<multiple: entry in releases>
<name: entry.version>

### {{ entry.version }} — {{ entry.date }}
result["changelog-by-version"]
# {"2.1.0": "### 2.1.0 — 2026-06-01\n", "2.0.0": "### 2.0.0 — 2026-05-01\n"}

render_file(path, inputs) reads path as UTF-8 and renders it — equivalent to render(open(path).read(), inputs):

from mdma import render_file

result = render_file("release-notes.mdma", {"project": "Acme SDK", "version": "3.0.0", "date": "2026-07-01"})

write_output(result, output_dir, block=None) writes a render()/render_file() result to .md files. Omit block to write every top-level block; pass a block name to write only that one. A string-valued block becomes {output_dir}/{block}.md; a <multiple:> block becomes a directory {output_dir}/{block}/ with one file per item — {name}.md if the block declared <name:>, otherwise {index}.md. Returns the list of Paths written.

from mdma import render_file, write_output

result = render_file("release-notes.mdma", {...})
write_output(result, "out/")                        # every block
write_output(result, "out/", block="release-notes")  # just that one

render() raises one of the exceptions in mdma.errors on failure:

Exception Condition
MissingInputError a required input (no default) was not supplied
MdmaTypeError an input's runtime type doesn't match its declared type, or a <name:> expression evaluates to something other than a string/number
MdmaReferenceError a forward block reference, or an undefined variable
FilterError a filter was applied to a value of the wrong type
MdmaSyntaxError the .mdma source doesn't conform to the grammar (including <name:> used without a preceding <multiple:>)
DuplicateNameError two items in a <multiple:> block computed the same <name:> value

All are subclasses of mdma.errors.MdmaError.

Behavioral notes not obvious from spec.md

  • The blank line conventionally left between one block's content and the next block's header (or EOF) is treated as file formatting, not part of either block's rendered value — it's stripped from both ends of the block body before parsing. This is required for block references ({{ blockname }}) to be safely embeddable inline; otherwise every block value would carry a stray trailing newline from that separator. Blank lines inside a body are preserved exactly as written.
  • Whitespace control ({%-/-%}) is applied per-tag, exactly as written — a conditional branch that renders empty does not retroactively remove surrounding blank-line text unless that text is trimmed by an adjacent -.
  • Accessing a missing property on an object/object[] value (e.g. entry.description when description wasn't set) yields None rather than raising — objects are untyped maps, so this is normal and is what makes | default(...) useful on them. An undefined root identifier (typo'd variable/block/input name) still raises MdmaReferenceError.
  • default([]) and other array literals ([a, b]) are supported in expressions even though the formal grammar doesn't enumerate an array-literal production — the filter reference relies on this syntax ({{ list | default([]) }}).
  • multiple is a reserved word (can't be used as a block or input name), but name is not — <name:> is only ever recognized in its fixed position right after <multiple:>, so an input or block literally named name (e.g. name: string) is unaffected.

Development

pip install -e ".[dev]"
pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python_mdma-0.1.0.tar.gz (19.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

python_mdma-0.1.0-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

Details for the file python_mdma-0.1.0.tar.gz.

File metadata

  • Download URL: python_mdma-0.1.0.tar.gz
  • Upload date:
  • Size: 19.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for python_mdma-0.1.0.tar.gz
Algorithm Hash digest
SHA256 df84d6c79b0726787cd78ce92c55000e2066f918e3e2fca03ca6e75185162434
MD5 e0cc91648d56f6efec248612c2dbfe45
BLAKE2b-256 c3bbca6a08b0409f501be76ed1ba616eff46d791804474f2f265604b82e2a061

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_mdma-0.1.0.tar.gz:

Publisher: release.yml on Dastfox/mdma-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_mdma-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: python_mdma-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for python_mdma-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c4789f1301cad09438657d7a18836be22f06d7fb7127751a2bfc1a8e8e7fba58
MD5 71e84c87bf489cb07a5243fd2b49f48b
BLAKE2b-256 a9ed39be11d233d4302ca0ded0600c035956e41580a929374641d63ad2a77a20

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_mdma-0.1.0-py3-none-any.whl:

Publisher: release.yml on Dastfox/mdma-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page