Unpack Markdown-embedded code into real source files.
Project description
DeCatenator
decat is a small command-line tool that unpacks code embedded in Markdown.
It reads headings that look like file paths and writes the accompanying code
to real files.
- Input rule – a level-2 or level-3 heading whose text is a single path
segment (no spaces).
Examples:
## main.py,### src/utils/helpers.js. Anything with spaces (e.g.## My Heading) is ignored. - Code source
- If the heading is followed (after optional blank lines) by a fenced block
opened with triple back-ticks, those lines become the file content.
The closing fence must be exactly
```(no language tag). - Otherwise, all lines until the next matching heading (or EOF) are taken as the file content.
- If the heading is followed (after optional blank lines) by a fenced block
opened with triple back-ticks, those lines become the file content.
The closing fence must be exactly
- Creates parent directories as needed and overwrites existing files.
- Works with a file argument or
stdin.
Note: This project is the inverse of Catenator.
Quick start
pip install decat
CLI
# write files into the current directory
decat article.md
# write into a different root
decat article.md -o ./extracted
# read from stdin
cat article.md | decat -
# version
decat --version
Example – fenced
### src/hello.py
```python
print("Hello, world!")
Running `decat readme.md` generates `src/hello.py` containing:
```python
print("Hello, world!")
Example – unfenced
## scripts/build.sh
#!/usr/bin/env bash
echo "building"
decat docs.md writes scripts/build.sh with the two lines above.
Programmatic use
from pathlib import Path
from decat import extract_files, write_files
with open("notes.md", "r", encoding="utf-8") as f:
lines = f.readlines()
pairs = extract_files(lines) # yields (rel_path, code)
write_files(pairs, Path("out")) # creates ./out/...
How it works
-
Regex
^#{2,3}\s+([^\s]+)\s*$recognises valid headings. -
Searches forward:
- if the next non-blank line matches
^```.*$, capture until a bare ```; otherwise capture until the next valid heading.
- if the next non-blank line matches
-
Raises
SyntaxErrorif a fenced block is unterminated. -
All IO logic is isolated in
src/decat/_core.py; CLI lives insrc/decat/cli.py.
Repository layout
./
│ README.md
│ pyproject.toml
│ MANIFEST.in
├── src/
│ └── decat/
│ ├── _core.py
│ ├── cli.py
│ └── __init__.py
└── tests/
└── test_core.py
Development
# editable install
pip install -e .
# run the test suite
pytest
# build sdist & wheel
python -m build
# upload (requires credentials)
twine upload dist/*
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file decatenator-0.1.0.tar.gz.
File metadata
- Download URL: decatenator-0.1.0.tar.gz
- Upload date:
- Size: 13.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c699575b8be28ee081dceeac45357459e5702c9bbda9c5fdb6b300f6c795992
|
|
| MD5 |
2f68b029c3ac98a627dc26e3a0225ea0
|
|
| BLAKE2b-256 |
915130a960d119ef479adae248027d914133c28aab6408b6e25f811e34bded40
|
File details
Details for the file decatenator-0.1.0-py3-none-any.whl.
File metadata
- Download URL: decatenator-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a20b63934fee86c29bb238c82319be50645ec6e7c868c288ab4b5949fc29c26
|
|
| MD5 |
ab2aff5a5aca0587d73e8befd6060bf1
|
|
| BLAKE2b-256 |
52511773ad0f36cc9698a0d2eba35ccf9399853f33ab57d26a2fc7b963d4f595
|