mdfsrun
One Markdown file = one runnable project. Think of it as PEP 723 grown up to several files and a task runner:
- the front matter is a Taskfile,
- the body is documentation plus fenced code blocks marked with a file path,
- the code blocks are extracted into a work directory with Entangled, and the tasks run there.
All you need is uv. Task, Entangled and Python itself are fetched by uv.
---
tasks:
default:
cmds:
- uv run main.py
test:
cmds:
- uv run --with pytest python -m pytest -q
---
# Hello
```python {.python file=main.py}
# /// script
# dependencies = ["rich"]
# ///
from pkg.core import greet
greet("world")
```
```python {.python file=pkg/core.py}
from rich import print
def greet(name: str) -> None:
print(f"[bold green]Hello, {name}![/bold green]")
```
```python {.python file=pkg/__init__.py}
```
uvx mdfsrun app.md # runs the "default" task
uvx mdfsrun app.md test # runs the "test" task
Usage
mdfsrun [--rm] FILE.md [TASK ...] [VAR=VALUE ...] [task options ...]
Everything after the .md file is passed to task unchanged, so uvx mdfsrun app.md --list-all
works too. Options of mdfsrun itself go before the .md file.
| Option / variable | Meaning |
|---|---|
--rm / MDFSRUN_RM=1 |
delete the work directory after the run |
MDFSRUN_DIR |
use this work directory instead of .mdfsrun/<name>/ next to the .md |
How it works
- The
.mdis copied into the work directory (.mdfsrun/<name>/next to the.mdby default) and Entangled extracts the code blocks there. The work directory gets a.gitignorecontaining*, so Git ignores it. - The front matter is passed to
taskas the Taskfile, and the tasks run in the work directory.version: '3'is added if the front matter has noversion.
Tasks receive these environment variables. Use them in templates ({{.MD_DIR}}) or in
commands ($MD_DIR):
| Variable | Value |
|---|---|
MD |
absolute path of the .md |
MD_DIR |
absolute path of the directory containing the .md |
OUT |
absolute path of the work directory |
For example, to load a .env that sits next to the .md:
dotenv: ["{{.MD_DIR}}/.env"]
The work directory is kept after the run, but it is rebuilt from the .md every time:
files edited there are reset, and files whose block was removed from the .md are deleted.
Files created by the tasks themselves (such as .venv) are left alone.
mdfsrun refuses to use (or delete) an existing non-empty directory it did not create.
Code blocks
A code block is extracted when its attributes contain file=. Both forms work:
```python {.python file=main.py}
```
``` {.python file=main.py}
```
Blocks without {...} (```sh, ```python) are plain documentation.
Only fences of exactly three backticks are recognized, so a Markdown file that itself contains
file= blocks cannot be embedded.
Using Entangled locally
The .md is the single source of truth: edit the .md, not the files in the work directory.
Writing changes back from the work directory (entangled stitch) is not supported; the files
there carry no annotations.
You can still run Entangled yourself next to the .md, including entangled stitch. Put this
entangled.toml next to the .md:
version = "2.0"
ignore_list = [".mdfsrun/**/*"]
[markers]
open = '^(?P<indent>\s*)```[^\s{`]*\s*\{(?P<properties>[^{}]*)\}\s*$'
close = '^(?P<indent>\s*)```\s*$'
[markers]makes Entangled accept the```python {.python file=...}form (the same config as mdfs).ignore_listkeeps Entangled away from.mdfiles that a run may have extracted into the work directory. It is not needed if you use--rmor aMDFSRUN_DIRoutside the project.
mdfsrun always runs what is in the .md; changes you have not stitched back are not used.
Notes
- Requires Python 3.13 or later (a requirement of Entangled 2.4.x); uv installs it if needed.
- The first run downloads Task, Entangled and the dependencies of your code; later runs use uv's cache.
- Running an
.mdfile runs whatever its front matter and code say. Only run files you trust.
License
Metadata
Release files for mdfsrun 0.1.3
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 | Actions |
|---|---|---|---|---|
| mdfsrun-0.1.3.tar.gz | 6.6 kB | Without provenance | Details for mdfsrun-0.1.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| mdfsrun-0.1.3-py3-none-any.whl | Python 3 | none | any | Details for mdfsrun-0.1.3-py3-none-any.whl |
Total release size: 14.4 kB
Release files / mdfsrun-0.1.3.tar.gz
| Download URL | mdfsrun-0.1.3.tar.gz |
|---|---|
| Size | 6.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0c209cbb14ea0b1dae00b7b83698c70f865d58549c7d18709ff60f952fbddbbb
|
|
BLAKE2b-256 checksum How to use checksums |
64cb4a490797840b765c555d8270508f2d41f4557074cdddd6670fe3826870ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Release files / mdfsrun-0.1.3-py3-none-any.whl
| Download URL | mdfsrun-0.1.3-py3-none-any.whl |
|---|---|
| Size | 7.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
006bed156eee18a2eb3b3e87e2973a96a323b6ea4ef0512908903ef5e385ab52
|
|
BLAKE2b-256 checksum How to use checksums |
3d361b4e46989163fbc9a77cc2f282f7bf8caf65a77c0d530758d9a6bf8f5d38
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Security for mdfsrun 0.1.3
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.