Symbol-aware markdown snippet references for pymdownx.snippets
Project description
pymdown-symbolic-snippets
Extension that makes pymdownx.snippets symbol-aware.
Migration
- Canonical package/distribution name is now
pymdown-symbolic-snippets. - Canonical Python module path is now
pymdown_symbolic_snippets. - Compatibility aliases remain for one release:
zensical_symbolic_snippetsextension key andzensical_code_referencesimport path. - Importing
zensical_code_referencesemits aDeprecationWarning.
What this gives you
Markdown usage stays standard:
--8<-- "my_pkg.api:Client.send"
You reference symbols instead of brittle line ranges, so snippets stay correct when code moves.
Backward compatibility
Existing pymdownx.snippets syntax continues to work unchanged. Symbolic
resolution is only applied when the target matches a real Python module under
module_roots.
--8<-- "docs/intro.md"
--8<-- "docs/intro.md:intro"
--8<-- "src/my_pkg/api.py:12:20"
--8<-- "my_pkg.api:Client.send"
Install
uv add pymdown-symbolic-snippets
Or:
pip install pymdown-symbolic-snippets
zensical is optional. This package works as a Python-Markdown extension without
zensical; install zensical only if you want to run Zensical builds.
Why this exists
Raw line ranges are brittle. If code moves, references like file.py:88:121 rot.
This extension allows snippet references by Python symbol and resolves them to real line spans at build time using AST.
Symbol reference format
<module.path>:<symbol>(.<nested>)[:start[:end]]
Examples:
my_pkg.api:build_payloadmy_pkg.api:Client.sendmy_pkg.config:DEFAULT_TIMEOUT:-1:2
Resolved output is rewritten to standard snippets format:
path/to/file.py:start:end
For method references without selectors, output uses a multi-range selector so the class header and method body are included together:
path/to/file.py:class_header_start:class_header_end,method_start:method_end
Selector behavior
- If you don't add a selector, the whole symbol is used.
- For method references with no selector, the selection includes the class declaration line(s) and that method's body, but not other methods in the class (for example, not
__init__). :startmeans "start at this line (relative to the symbol) and go to the end of the symbol.":start:endmeans "use only this relative line range inside the symbol."- Positive numbers are 1-based:
1is the first line of the symbol. 0and negative numbers are offsets:0is the symbol start,-1is one line above the symbol start.- If
startorendgoes past the file limits, values are clamped to valid file bounds. - If the range is invalid (
end < start), resolution fails.
Ecosystem comparison
Research summary across pymdown-extensions, mkdocs, mkdocs-material, and
common include plugins:
| Tool / project | Symbol path include (module:Class.method) |
AST/introspection-based resolution | Generic snippet include | Equivalent to this project |
|---|---|---|---|---|
pymdownx.snippets |
No | No | Yes (file, line ranges, named marker sections) | No |
mkdocs |
No | No | Not in core (delegates to extensions/plugins) | No |
mkdocs-material |
No | No | Yes via upstream pymdownx.snippets |
No |
mkdocstrings/python |
Partial (API object rendering) | Yes (object collection) | No (not a general snippets include engine) | Partial |
mkdocs-codeinclude-plugin |
No | No | Yes (token/brace-targeted blocks) | No |
mkdocs-include-markdown-plugin |
No | No | Yes (delimiter-based includes) | No |
pymdown-symbolic-snippets (this project) |
Yes | Yes | Yes (rewrites to pymdownx.snippets line spans) |
Yes |
Bottom line: existing options either slice by lines/markers or render API docs;
none provide first-class symbol-addressed snippet transclusion in the same
workflow as pymdownx.snippets.
Python-Markdown configuration
Use it as a normal Python-Markdown extension, placed before
pymdownx.snippets:
import markdown
md = markdown.Markdown(
extensions=[
"pymdown_symbolic_snippets",
"pymdownx.snippets",
],
extension_configs={
"pymdown_symbolic_snippets": {
"module_roots": ["src"],
"fail_on_unresolved": True,
},
"pymdownx.snippets": {
"base_path": ["src"],
"check_paths": True,
},
},
)
Zensical configuration (site.toml)
[project.markdown_extensions.pymdown_symbolic_snippets]
module_roots = ["src"]
fail_on_unresolved = true
[project.markdown_extensions.pymdownx.highlight]
anchor_linenums = true
line_spans = "__span"
pygments_lang_class = true
[project.markdown_extensions.pymdownx.snippets]
base_path = ["src"]
check_paths = true
[project.markdown_extensions.pymdownx.superfences]
If you define project.markdown_extensions explicitly, include all extensions
you rely on. Leaving out pymdownx.superfences/pymdownx.highlight causes
fenced blocks to render as plain text.
Included proof project
This repo includes a working Zensical example that references this package's own source to prove behavior:
- Config:
examples/symbolic-snippets/site.toml - Docs page:
examples/symbolic-snippets/docs/index.md
Build it:
uv run zensical build --config-file examples/symbolic-snippets/site.toml
Tiny output example (from examples/symbolic-snippets/site/index.html):
def parse_symbolic_reference(value: str) -> SymbolicReference | None:
if ":" not in value:
return None
Tests
Run:
uv run pytest
The suite includes parser/resolver tests plus an E2E Zensical build test that asserts resolved symbols are rendered in generated HTML.
Release automation
GitHub Actions is configured to publish to PyPI on strict semver tags:
- CI workflow:
.github/workflows/ci.yml - Release workflow:
.github/workflows/release.yml - Trigger: push tag matching
vX.Y.Z - Guardrails: tag must be strict semver and must match
project.versioninpyproject.toml - Gate: test matrix (
3.13,3.14) must pass before publish
One-time GitHub setup for trusted publishing:
- Create environment
pypiin repository settings. - Configure PyPI Trusted Publisher for this repository/workflow.
Release command:
git tag v0.1.0
git push upstream v0.1.0
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 pymdown_symbolic_snippets-0.1.0.tar.gz.
File metadata
- Download URL: pymdown_symbolic_snippets-0.1.0.tar.gz
- Upload date:
- Size: 7.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"26.05","id":"yarara","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4079a389cbfd484dfe9a4bc082f8ca6c58af01e055421811e72b43cd4d86e631
|
|
| MD5 |
b4e2b10add509f4a07158ff26c1e6202
|
|
| BLAKE2b-256 |
93c3cb3b7787d3c80edac5c344799acdb999ced4f6243dce80d9455532f410e5
|
File details
Details for the file pymdown_symbolic_snippets-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pymdown_symbolic_snippets-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"26.05","id":"yarara","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1a452e4b56174fd004f3c9ef89ff554cb0e1e6969ceb85eb1b3e6b9d8213841
|
|
| MD5 |
6f906b10dc2548a5640412b3763a351d
|
|
| BLAKE2b-256 |
802101d82f08886f5c23201e8299ed3d9033893d8c234e14132efca5370bc60d
|