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 preexpands the embeds and extracts fence directives into anmw-fencecomment.mw postapplies the fence styling, resolves the highlights, and injects each embed script once.mw renderruns the full pipeline in one shot for callers without their own renderer.mw configprints the resolved configuration and the source of each value.mw listprints 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| markwright-0.3.0.tar.gz | 73.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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.