Parse Markdown model definitions into RDF, diagrams, and docs.
Project description
SPDX-FileCopyrightText: 2026 Arthit Suriyawongkul SPDX-FileType: DOCUMENTATION SPDX-License-Identifier: CC0-1.0
SpecMD
Convert Markdown model definitions to RDF ontologies and specification documents.
Contents
- Functionality
- Installation
- Prerequisites
- Usage
- Model configuration
- spec-parser compatibility
- Testing
- Design notes
Functionality
SpecMD reads and validates the complete model directory before generating output. It can then generate one or more of the following outputs:
| Format | Description |
|---|---|
jsondump |
JSON dump of the parsed model |
mkdocs |
MkDocs source files for website generation |
plantuml |
PlantUML diagram source |
rdf |
OWL+SHACL ontology and JSON-LD context |
tex |
TeX source for printable specification |
singlefile |
Single Markdown file (suitable for conversion to Word, PDF, etc.) |
webpages |
Per-IRI web pages (not yet implemented) |
Installation
pip install specmd
Prerequisites
All Python dependencies are installed automatically with pip install specmd.
External tools required for specific formats:
| Format | External prerequisite |
|---|---|
tex |
pandoc -- used to convert Markdown fragments to LaTeX |
| All others | None |
Usage
SpecMD uses subcommands:
specmd <command> [options]
Commands:
generate (gen) Generate output artefacts from a model directory
validate Validate a model directory or a single .md file
migrate Convert spec-parser format to SpecMD format
export Export a SpecMD model to another format
Validate
Check raw YAML syntax of every .md file, then fully parse the model:
specmd validate path/to/model
Validate a single file (raw YAML syntax check only):
specmd validate path/to/file.md
Use --strict to exit with a non-zero code when raw YAML issues are found
(without --strict they are reported as warnings but do not fail the command):
specmd validate --strict path/to/model
The validator runs two passes on a directory:
- Raw YAML check — each
## Entriesand## Propertiessection is parsed withyaml.safe_loadto catch characters and constructs that cause parse errors in strict YAML consumers (e.g. a value starting with[or{, or a bare:inside a plain scalar). - Full model parse — the complete model is loaded and cross-referenced, reporting semantic errors (unknown classes, missing metadata, etc.).
A summary line is always printed: N file(s) checked, M file(s) with issues.
Generate
Generate all output formats into subdirectories under ./out/:
specmd generate path/to/model --output ./out
Generate specific formats only:
specmd generate path/to/model --formats rdf,mkdocs --output ./out
Override the output directory for a single format:
specmd generate path/to/model --output ./out --rdf-dir ./ontology
Available formats:
jsondump, mkdocs, plantuml, rdf, tex, singlefile, webpages.
Migrate
Convert a model written in the spec-parser format to SpecMD format:
specmd migrate path/to/old-model --output path/to/new-model
Export
Export a SpecMD model back to the spec-parser format:
specmd export path/to/model --output path/to/exported --format legacy
Common options
| Option | Commands | Description |
|---|---|---|
-o/--output DIR |
generate, migrate, export |
Output directory |
-f/--force |
generate, migrate, export |
Overwrite existing output |
--strict |
validate |
Exit non-zero on raw YAML warnings |
-q/--quiet |
all | Warnings and errors only |
-v/--verbose |
all | Debug output |
-V/--version |
top-level | Show version and exit |
Full help:
specmd --help
specmd generate --help
Model configuration
An optional specmd.yml file at the model root controls parsing and
generation.
Key settings:
license: CC0-1.0 # SPDX license ID for generated output
base-uri: https://example.org/rdf/terms/ # ontology base URI
namespace-order: [Core] # namespace processing order
ontology: # OWL ontology metadata
preferred-namespace-prefix: myns
label: My Model Ontology
creator: My Organisation
license-uri: https://example.org/licenses/my-license/
vocabulary: # vocabulary / relationship defaults
default-from: Element # default vocab entry source class
default-to: Element # default vocab entry target class
default-relationship-class: Relationship
rdf:
filename: my-model # output filename (no extension)
See docs/format.md for the full reference.
spec-parser compatibility
A main.py compatibility shim is provided for existing CI/scripts that
invoke the spdx/spec-parser CLI.
It accepts spec-parser's original command-line arguments, internally runs
specmd migrate on the input, then specmd generate or specmd validate.
Drop-in from a workflow perspective: replacing spec-parser with specmd
requires only a one-line change in CI (see below), and SpecMD accepts the
original Markdown format as input via automatic migration.
Output will differ: the generated RDF/OWL/SHACL and MkDocs Markdown are not byte-for-byte identical to spec-parser output. SpecMD incorporates correctness fixes and improvements to the generated artefacts (see docs/design.md). Downstream consumers of the RDF or MkDocs output should expect and review these differences.
Simply replace python spec-parser/main.py with python specmd/main.py in
existing workflows.
Or, in a GitHub workflow that checks out the repository, only the repository name needs to change - the checkout path, the pip install command, and every main.py option stay the same:
From:
- uses: actions/checkout@6
with:
repository: spdx/spec-parser
path: spec-parser
- run: |
pip install -r spec-parser/requirements.txt
python3 spec-parser/main.py --force --generate-mkdocs --output-mkdocs spdx-spec/docs/model spdx-3-model/model
To:
- uses: actions/checkout@6
with:
repository: bact/specmd #changed
path: spec-parser
- run: |
pip install -r spec-parser/requirements.txt
python3 spec-parser/main.py --force --generate-mkdocs --output-mkdocs spdx-spec/docs/model spdx-3-model/model
Testing
Run the standard test suite:
pip install pytest
pytest
shacl2code compatibility test
tests/test_shacl2code.py verifies that SpecMD RDF output produces
identical JSON schema to upstream spec-parser when both are fed through
shacl2code. This test is skipped automatically when either dependency is
absent, so it will not block a plain pytest run.
To run it, both tools must be reachable:
# 1. Install shacl2code
pip install shacl2code
# 2. Point PYTHONPATH at your spec-parser checkout
# (spec-parser cannot be pip-installed)
PYTHONPATH=/path/to/spec-parser pytest tests/test_shacl2code.py -v
Example with a sibling checkout:
PYTHONPATH=../spec-parser pytest tests/test_shacl2code.py -v
Expected output:
tests/test_shacl2code.py::TestShacl2codeCompatibility::test_jsonschema_identical PASSED
Design notes
SpecMD is built on the design of spdx/spec-parser, targeting compatibility with existing workflows. The Markdown input format is nearly identical; key differences include standard YAML front matter, camelCase metadata keys, a structured vocabulary entry format, and structured deprecation fields.
The RDF/OWL/SHACL output makes a set of deliberate design decisions around JSON-LD context correctness, SHACL shape simplification, OWL version metadata, and abstract class disjointness. The generated output is not byte-for-byte identical to spec-parser output — existing SHACL validation results are preserved, but OWL reasoning consumers should review the differences before updating.
Full details: docs/design.md.
This project is not an official SPDX project.
Project details
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 specmd-0.2.0.tar.gz.
File metadata
- Download URL: specmd-0.2.0.tar.gz
- Upload date:
- Size: 100.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
517af09b34f438059b1c30a01c255f400c035f8d2038cb183085721e6ca86d3e
|
|
| MD5 |
91cd591c081a1ecc00329b135b834616
|
|
| BLAKE2b-256 |
744afa34b11927f2781d67aa3c4ae8c620b97c4de8963a13e82e6431129c2335
|
Provenance
The following attestation bundles were made for specmd-0.2.0.tar.gz:
Publisher:
pypi-publish.yml on bact/specmd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
specmd-0.2.0.tar.gz -
Subject digest:
517af09b34f438059b1c30a01c255f400c035f8d2038cb183085721e6ca86d3e - Sigstore transparency entry: 1586137692
- Sigstore integration time:
-
Permalink:
bact/specmd@52678fe5656fa9713f451b639634f87a985df267 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/bact
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@52678fe5656fa9713f451b639634f87a985df267 -
Trigger Event:
release
-
Statement type:
File details
Details for the file specmd-0.2.0-py3-none-any.whl.
File metadata
- Download URL: specmd-0.2.0-py3-none-any.whl
- Upload date:
- Size: 65.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50cee123d252271ceffb533e8e743d6f61bab20b7da10aced64ca4f0c7383b5e
|
|
| MD5 |
09812bd8af3584e6cd9323e14e2b5474
|
|
| BLAKE2b-256 |
86d2e9a0aa8f8c103bc1c33921986bc92cfd4706a186c499f298e74012e42f15
|
Provenance
The following attestation bundles were made for specmd-0.2.0-py3-none-any.whl:
Publisher:
pypi-publish.yml on bact/specmd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
specmd-0.2.0-py3-none-any.whl -
Subject digest:
50cee123d252271ceffb533e8e743d6f61bab20b7da10aced64ca4f0c7383b5e - Sigstore transparency entry: 1586137771
- Sigstore integration time:
-
Permalink:
bact/specmd@52678fe5656fa9713f451b639634f87a985df267 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/bact
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@52678fe5656fa9713f451b639634f87a985df267 -
Trigger Event:
release
-
Statement type: