Skip to main content

datadocs

Generate documents from structured data.

datadocs generates Markdown, HTML, or text documents from structured data using Jinja templates.

The philosophy is simple:

Bring your own data. Bring your own template. Generate documents deterministically.

What it is

datadocs is a small Python package for rendering documents from data you already prepared.

Templates receive exactly two variables:

data
params

What it is not

datadocs is not a workflow engine, reporting platform, data transformation framework, query builder, or AI framework.

Installation

pip install datadocs

Optional integrations:

pip install "datadocs[http]"
pip install "datadocs[bigquery]"

CLI

Render from JSON, YAML, or NDJSON:

datadocs render \
  --template report.md.j2 \
  --data report.json

Write to a file:

datadocs render \
  --template report.md.j2 \
  --data report.yaml \
  --output report.md

Pass template parameters:

datadocs render \
  --template report.md.j2 \
  --data report.ndjson \
  --param period="Q2 2026"

SDK

from datadocs import render

markdown = render(
    template_path="templates/report.md.j2",
    data={"epics": [{"name": "Search"}]},
    params={"period": "Q2 2026"},
)

Use Document with data:

from datadocs import Document

doc = Document(
    template_path="templates/report.md.j2",
    data={"epics": [{"name": "Search"}]},
    params={"period": "Q2 2026"},
)

doc.render_to("report.md")

Use Document with a source:

doc = Document(
    template_path="templates/report.md.j2",
    source=my_source,
)

A source only needs to provide:

def load_data(self):
    ...

Architecture

datadocs has one deterministic rendering path:

Source or prepared data -> Document -> render() -> text

The renderer only knows about templates and data. It does not know whether data came from a file, HTTP API, BigQuery query, or a custom source.

Rendering

render() is the core function:

render(template_path, data, params=None)

It loads a Jinja template from disk and renders it with exactly two template variables:

data
params

This keeps templates predictable and avoids hidden context.

Documents

Document is a small wrapper around the rendering contract. It accepts either prepared data or a source, never both:

Document(template_path="report.md.j2", data=data)
Document(template_path="report.md.j2", source=source)

doc.render() returns text. doc.render_to(path) writes that text to disk.

Sources

A source is any object with:

def load_data(self):
    ...

Sources are responsible only for loading ordinary Python data. They should not render templates, transform data, build queries, or manage workflows.

Built-in sources follow that rule:

Source Responsibility
HttpSource Make one HTTP request and return the parsed JSON response.
BigQuerySource Execute one supplied SQL file and return query rows as dictionaries.

Custom sources can be added without changing the renderer:

class MySource:
    def load_data(self):
        return {"epics": [{"name": "Search"}]}


doc = Document(template_path="report.md.j2", source=MySource())

Loaders

File loaders are intentionally separate from sources. The CLI uses loaders for JSON, YAML, and NDJSON files, while integrations use sources.

HTTP

HTTP auth is passed through headers. datadocs does not manage credentials.

datadocs http render \
  --url https://example.com/api \
  --header "Authorization=Bearer $TOKEN" \
  --query-param period="Q2 2026" \
  --template report.md.j2

POST JSON from a file:

datadocs http render \
  --url https://example.com/api \
  --method POST \
  --json-body request.json \
  --template report.md.j2

The parsed JSON response is used directly as data.

The SDK HTTP source currently supports JSON responses. Use transform to adapt API-specific response envelopes before they reach a Document:

from datadocs import Document
from datadocs.http import HttpSource

source = HttpSource(
    url="https://api.example.com/report",
    headers={"Authorization": "Bearer token"},
    response_format="json",
    transform=lambda data: data["data"]["items"],
)

doc = Document(template_path="report.md.j2", source=source)

response_format="json" is the only implemented response format for now. The argument is explicit so future formats such as text or bytes can be added without changing the default behavior.

BigQuery

BigQuery authentication uses Google Application Default Credentials. datadocs does not expose credential flags.

datadocs bigquery render \
  --query queries/report.sql \
  --template report.md.j2 \
  --project runtime-project \
  --billing-project billing-project \
  --location EU

Typed query parameters use name:TYPE=value:

datadocs bigquery render \
  --query queries/report.sql \
  --template report.md.j2 \
  --query-param period:STRING="Q2 2026"

The SQL file is executed as supplied. datadocs does not build or transform queries.

Template example

# Delivery Report

Period: {{ params.period }}

{% for epic in data.epics %}
## {{ epic.name }}
{% endfor %}

Smoke tests

Opt-in smoke tests and demos live under smoke/. They are intentionally kept outside the normal tests/ suite because they use real external services.

The countries smoke test fetches nested JSON from REST Countries, loads it into BigQuery with a rendered SQL template, queries it back, and renders a Markdown report.

Install the optional integration dependencies:

uv sync --extra dev --extra http --extra bigquery

Authenticate with Google Application Default Credentials:

gcloud auth application-default login

Run the smoke test:

DATADOCS_BQ_PROJECT=bigquery-sandbox-cpvuft6h \
DATADOCS_BQ_DATASET=test_datadocs \
DATADOCS_BQ_LOCATION=EU \
DATADOCS_COUNTRIES_API_TOKEN=your-rest-countries-token \
uv run python smoke/countries/run.py

The script prints only the generated report to stdout. If the configured dataset already exists, the smoke test reuses it and does not delete it. If the script creates the dataset, it deletes it by default unless DATADOCS_SMOKE_KEEP_DATASET=1 is set. See smoke/countries/README.md for sample output and cleanup controls.

Development and publishing

The tracked uv.lock keeps development and CI dependencies aligned. Install the locked environment and run the checks with:

uv sync --locked --all-extras
uv run --locked --all-extras python -m pytest -q
uv run --locked --all-extras ruff check .
uv run --locked --all-extras python -m mypy

CI runs on Python 3.11–3.14 on Linux, plus Python 3.12 on Windows and macOS. It builds the wheel and source distribution and validates their metadata. The automated checks use mocked integrations; the external countries smoke test above remains opt-in.

.github/workflows/publish.yml publishes only after successful checks:

Event Result
Pull request Tests and distribution builds; no publishing.
New main commit with the same version as the latest release tag Development build on TestPyPI.
main commit with a version above the latest release tag Create the matching version tag and publish to PyPI.
Push of a matching vMAJOR.MINOR.PATCH tag Verify the tag and tests, then publish to PyPI.
No release tags yet Development builds on TestPyPI until the first release is explicitly tagged.

The existing v0.1.0 tag is preserved. With version = "0.1.0", a new main commit publishes a development version such as 0.1.1.dev1230001 to TestPyPI. The suffix includes the workflow run ID and attempt, so retries get distinct development versions. That version is applied only in the build checkout; the repository's version is unchanged.

To request the next production release, update the version and lockfile:

uv version 0.1.1

Commit pyproject.toml and uv.lock, preferably in a release PR, and merge into main. pyproject.toml is the version source. Release versions use stable MAJOR.MINOR.PATCH numbers, compared with Python packaging version rules. The workflow rejects regressions and tag/version mismatches and never moves an existing tag. Tags identify the commit CI actually tested. A failed upload can be retried on that tagged commit without changing the release version.

Before uploading, the publishing workflow installs both the wheel and source distribution in isolated environments. Each installation must report the expected distribution version and render a sample document through both the SDK and the installed datadocs command.

Publishing uses OIDC trusted publishers, with no stored PyPI API token. The publisher settings on each registry must match this workflow:

Setting PyPI TestPyPI
Project datadocs datadocs
Repository owner AndresVelasco AndresVelasco
Repository datadocs datadocs
Workflow filename publish.yml publish.yml
GitHub environment pypi testpypi

The environment field in the publisher configuration is optional; if supplied, it must match the corresponding job. See the PyPI trusted-publishing documentation.

Future integrations

Future integrations such as datadocs.langchain should be optional layers on top of the deterministic rendering pipeline.

Metadata

Release files for datadocs 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for datadocs 0.1.1
File Size Uploaded Provenance Actions
datadocs-0.1.1.tar.gz 8.7 kB With provenance Details for datadocs-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for datadocs 0.1.1
File Interpreter ABI Platform Actions
datadocs-0.1.1-py3-none-any.whl Python 3 none any Details for datadocs-0.1.1-py3-none-any.whl

Total release size: 22.2 kB

Release files / datadocs-0.1.1.tar.gz

Download URL datadocs-0.1.1.tar.gz
Size 8.7 kB
Tags Source
SHA-256 checksum
How to use checksums
2b26ec1d46d5637112386d18e8b03ef44856203eab0a0c838dc86caa8f8f0724
BLAKE2b-256 checksum
How to use checksums
81e3b6baef6a29e56ea0f84dacd7f24789ece9352457576268a115091ea10b80
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Release files / datadocs-0.1.1-py3-none-any.whl

Download URL datadocs-0.1.1-py3-none-any.whl
Size 13.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
42d846b4da2476aafe8c419de1893adc4b6a39cbf4c7c96731ebd2a5cc383ed6
BLAKE2b-256 checksum
How to use checksums
c2bf020714586b843a5ff1b876f6799b94eadd5f65226c9e464609778c7a4a32
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Security for datadocs 0.1.1

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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for datadocs-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

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.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page