Skip to main content

markwright

markwright is a set of Python-Markdown extensions plus a companion command-line tool, mw, for author-friendly markdown: highlighted text, labeled code fences, and a set of media embeds. Use the extensions in any tool built on Python-Markdown (including MkDocs, Flask, or a plain script), or the mw pipeline in toolchains that are not, like Hugo.

The author-facing syntax and HTML output are derived from DigitalOcean's do-markdownit, so rendered pages match what DigitalOcean produces. markwright ports those extensions to Python-Markdown and adds its own pieces on top, most notably the mw CLI, which runs the same extensions as pre and post filters around any renderer.

Full documentation and a live demo: https://masonegger.github.io/markwright/

Features

Each extension is standalone and can be loaded on its own or alongside the others.

  • Highlight wraps <^>text<^> in <mark>, and works inside inline code and fenced code blocks where ==text== cannot reach.
  • Fence adds labels, secondary labels, environment classes, and command-line prefixes (line numbers, $/# prompts, custom prefixes) to code blocks.
  • YouTube turns [youtube ID] into a responsive iframe embed.
  • CodePen embeds a pen with theme, default-tab, and layout options.
  • Twitter embeds a tweet with theme and alignment options.
  • Instagram embeds a post with caption and alignment options.
  • Slideshow builds an image slideshow with navigation arrows.
  • Image Compare renders two images with a draggable before/after slider.

The embeds that contact a third party (YouTube, CodePen, Twitter, Instagram) take a load option. load = "click" renders an inert preview so nothing reaches the provider until the reader asks; see Click-to-Load. A host tool can validate a markwright config table from its own config file with markwright.config_from_mapping; see Configuration.

Installation

uv add markwright

or

pip install markwright

To work against a local checkout, install it editable:

uv add --editable ../markwright

Usage

With MkDocs

Add the extensions to mkdocs.yml. Load markwright.fence after pymdownx.superfences and pymdownx.highlight so the code-block HTML is in the shape the fence postprocessor expects.

markdown_extensions:
  - pymdownx.superfences
  - pymdownx.highlight:
      pygments_lang_class: true
  - markwright.highlight
  - markwright.fence:
      allowed_environments:
        - local
        - second
        - third
  - markwright.youtube
  - markwright.codepen
  - markwright.twitter
  - markwright.instagram
  - markwright.slideshow
  - markwright.image_compare

Standalone

The extensions work with Python-Markdown directly, so you can use them in any Python program.

import markdown

md = markdown.Markdown(
    extensions=[
        "pymdownx.superfences",
        "pymdownx.highlight",
        "markwright.highlight",
        "markwright.fence",
        "markwright.youtube",
        "markwright.codepen",
        "markwright.twitter",
        "markwright.instagram",
        "markwright.slideshow",
        "markwright.image_compare",
    ]
)

html = md.convert("This has a <^>highlighted word<^> in it.")
print(html)

Load only what you need:

import markdown

# Just YouTube embeds
md = markdown.Markdown(extensions=["markwright.youtube"])
html = md.convert("[youtube dQw4w9WgXcQ]")

The fence extension expects pymdownx.superfences and pymdownx.highlight to be loaded too. Without them the preprocessor still extracts directives, but the code-block HTML may not match what the postprocessor rewrites.

Command-Line Interface (mw)

For a toolchain that is not built on Python-Markdown, the mw command runs the same extensions as a pair of Unix filters around any renderer: a pre stage on the Markdown source, and a post stage on the rendered HTML.

mw pre < in.md | your-renderer | mw post > out.html
  • mw pre expands the embeds and extracts fence directives into an mw-fence comment.
  • mw post applies the fence styling, resolves the highlights, and injects each embed script once.
  • mw render runs the full pipeline in one shot for callers without their own renderer.
  • mw config prints the resolved configuration and the source of each value.
  • mw list prints every extension and the stages it provides.

Which extensions run, and how each is tuned, come from a markwright.toml (or a [tool.markwright] table in pyproject.toml) that the CLI discovers by walking up from the current directory. See the configuration docs.

Your renderer must pass raw HTML and HTML comments through. Hugo is a worked, tested example: see the Hugo guide. The full command and flag reference is in the CLI docs.

Syntax at a Glance

Highlight:       <^>important<^>

Fenced code with a label and command prompts:
    ```command
    [label deploy.sh]
    ssh root@server
    apt update
    ```

YouTube:         [youtube dQw4w9WgXcQ]
                 [youtube dQw4w9WgXcQ 360 640]
CodePen:         [codepen MattCowley vwPzeX]
                 [codepen MattCowley vwPzeX dark css]
Twitter:         [twitter https://twitter.com/github/status/1234567890]
Instagram:       [instagram https://www.instagram.com/p/CkQuv3_LRgS]
Slideshow:       [slideshow https://picsum.photos/id/10/480/270 https://picsum.photos/id/20/480/270]
Image Compare:   [compare before.jpg after.jpg]

See the extension docs for every flag and the exact HTML each one produces.

Development

This project uses uv and just.

just check             # the gate: ruff check, ruff format --check, mypy strict, pytest
just fmt               # format and auto-fix
just test              # pytest (integration tests skip without hugo on PATH)
just test-all          # pytest across Python 3.11 through 3.15
just test-integration  # only the end-to-end Hugo and clean-install tests
just docs-serve        # serve the docs site at localhost:8000

just check must pass before a change is complete. Coverage is reported on every run.

License

markwright is released under the MIT License.

It is a port of do-markdownit by DigitalOcean, which is licensed under the Apache License 2.0. See the NOTICE file for attribution details.

Metadata

Release files for markwright 0.3.0

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

Source distribution (sdist)

Source distribution for markwright 0.3.0
File Size Uploaded Provenance
markwright-0.3.0.tar.gz 73.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for markwright 0.3.0
File Interpreter ABI Platform
markwright-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 126.3 kB

Release files / markwright-0.3.0.tar.gz

Download URL markwright-0.3.0.tar.gz
Size 73.0 kB
Tags Source
SHA-256 checksum
How to use checksums
40dad170207132cde3201dc69dd31ae433fdf08c056dce7947bf28e6e0c846fd
BLAKE2b-256 checksum
How to use checksums
6ac7494055bb66ebadf32171c99ebe2cfb7fcffea27e61bf3d32b68fcaab0d11
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / markwright-0.3.0-py3-none-any.whl

Download URL markwright-0.3.0-py3-none-any.whl
Size 53.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c656b0583bddb3f185b4cb357b993cb34d8ad69e519e19fc570bb8bb14d9d179
BLAKE2b-256 checksum
How to use checksums
af3ae8f8d45b6d41fd3ee1f4738dc54dbb85da574f15b7fbb4557460eb0ec802
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for markwright 0.3.0

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