Skip to main content

ddi-l

PyPI version Python versions Development status Typed DDI Lifecycle License: MIT

CI Docs Coverage

uv Ruff Checked with mypy pre-commit

A Python library for creating, reading, updating, and validating DDI Lifecycle 3.3 XML documents.

Documentation: guides, a models reference, CLI recipes and a 16-module training curriculum, in English and French.

Installation

pip install ddi-l

Optional extras:

pip install 'ddi-l[full]'     # lxml: faster parsing and validation
pip install 'ddi-l[server]'   # HTTP API (see `ddi serve` below)

The DDI 3.1, 3.2, and 3.3 XML Schemas are bundled with the package, so validation works offline with no additional download.

Using R? Call ddi-l through reticulate; see Using ddi-l from R.

Quick start

Create a study

import ddi_l as ddi

doc = ddi.new_study(title="Household Survey", agency="example.org")

# Add questions. `label=` is optional, but `ddi lint` reports every
# maintainable without one, so labelling here is the difference between
# output that passes the linter this package ships and output that warns
# about itself.
q_age = doc.add_question(text="How old are you?", label="Age question")
q_gender = doc.add_question(text="What is your gender?", label="Gender question")

# Add variables linked to questions
doc.add_variable(name="Age", question=q_age, label="Age in years")
doc.add_variable(name="Gender", question=q_gender, label="Gender of respondent")

# Add concepts and universes
doc.add_concept(name="Demographics", label="Demographics")
doc.add_universe(name="Canadian adults aged 18+", label="Canadian adults aged 18+")

# Save to XML
doc.save("household-survey.xml")

Open and explore

import ddi_l as ddi

doc = ddi.open_ddi("household-survey.xml")

print(f"Questions: {len(doc.questions)}")
print(f"Variables: {len(doc.variables)}")

for v in doc.variables:
    print(f"  {v.identifier}")

Update and delete

# Find an item by identifier
item = doc.find("some-identifier")

# Remove an item
doc.remove("some-identifier")

# Save changes
doc.save("household-survey.xml")

Validate

import ddi_l as ddi

doc = ddi.open_ddi("household-survey.xml", validate=True)

issues = doc.validate()
for issue in issues:
    print(f"[{issue.severity}] {issue.message}")

Work with any DDI item type

The add_item() method supports all 30 registered DDI item types:

from ddi_l.models.logicalproduct import Category, RepresentedVariable
from ddi_l.models.datacollection import Instrument

doc.add_item(Category, name="Male")
doc.add_item(Category, name="Female")
doc.add_item(RepresentedVariable, name="Gender Representation")
doc.add_item(Instrument, name="CAWI Questionnaire")

# Query items by type
print(f"Categories: {len(doc.items(Category))}")

Command-line interface

ddi --version                                # Version and default DDI schema version
ddi validate my-study.xml                    # Schema validation
ddi lint my-study.xml                        # Lint checks (exits 1 on errors)
ddi to-json my-study.xml                     # Convert to JSON (round-trippable)
ddi to-jsonld my-study.xml                   # Convert to JSON-LD (linked data)
ddi from-json my-study.json -o my-study.xml  # Convert back from JSON
ddi roundtrip my-study.xml -o out.xml        # Round-trip XML
ddi versions                                 # List supported DDI schema versions
ddi serve                                    # HTTP API (needs the 'server' extra)

ddi validate and ddi lint exit non-zero when they find errors, which is what makes them usable as CI gates. Warnings are reported but do not fail the run; add --fail-severity warning to ddi lint once a corpus is clean enough to hold that line.

HTTP API

With ddi-l[server] installed, the same validate, lint and convert operations are available over HTTP, which is useful when DDI validation has to be reachable from outside Python:

ddi serve --port 8080
curl --data-binary @my-study.xml http://localhost:8080/v1/validate

/v1/convert/jsonld renders a study as linked data using the DDI Alliance's own DDI-RDF Discovery vocabulary, ready to load into a triple store. It covers DDI's discovery subset and does not convert back; use /v1/convert/json when the payload has to return to XML.

Open http://localhost:8080/ in a browser and you land on Swagger UI at /schema: every endpoint with its request body, query parameters and response schema, each carrying a real example. "Try it out" comes prefilled with a valid DDI document, so you can validate one without writing a request first. The raw OpenAPI document is at /schema/openapi.json.

See the HTTP API guide for the endpoint reference and the limits to set before exposing it.

What's public, and what's stable

ddi-l has a wide surface, so it is worth saying which part is the front door.

Start here. ddi.new_study() and ddi.open_ddi() return a Document, and Document is the supported API for authoring and editing. Almost everything in the guides uses it.

The advanced layer (DDIDocument, DDIFragment, StudyCursor, and the generated classes under ddi_l.models) is public and documented, and is what you reach for when you need raw elements, partial instances, or a specific study in a multi-study file.

Semantic versioning applies to the ddi_l top-level exports, ddi_l.models, ddi_l.io, ddi_l.lint, ddi_l.validation, ddi_l.operations, and the ddi CLI's commands and exit codes.

Provisional in 0.1.x, and may change without a major bump:

  • ddi_l.models._generated: regenerated from the XSDs; import the public re-exports in ddi_l.models instead.
  • ddi_l.server and its HTTP routes: new in this release and not yet exercised against real deployments.
  • Anything prefixed with _.

While 0.x, breaking changes land in minor versions and are called out in the release notes.

Supported Python versions

Python 3.11, 3.12, 3.13, and 3.14.

Contributing

See CONTRIBUTING.md for development setup, coding standards, and testing instructions.

License

The ddi-l source code is MIT licensed.

Bundled third-party content

This package redistributes the official DDI Lifecycle XML Schemas (versions 3.1, 3.2, and 3.3) under ddi_l/schemas/, so that validation works offline.

  • Schemas: © DDI Alliance, licensed CC-BY-4.0.
  • The full license text and the DDI Alliance release notes ship alongside the schemas as license.txt and readme.txt.

The MIT license above covers the ddi-l code only; it does not alter the terms under which the DDI schemas are provided.

Metadata

Release files for ddi-l 0.1.0

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

Source distribution (sdist)

Source distribution for ddi-l 0.1.0
File Size Uploaded
ddi_l-0.1.0.tar.gz 2.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for ddi-l 0.1.0
File Interpreter ABI Platform
ddi_l-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 4.1 MB

Release files / ddi_l-0.1.0.tar.gz

Download URL ddi_l-0.1.0.tar.gz
Size 2.6 MB
Tags Source
SHA-256 checksum
How to use checksums
208c9afcf0e1c215f6507eb466ebdd60725eeb118f8f3b9acb2152480f047612
BLAKE2b-256 checksum
How to use checksums
0f7dbab9af0eb77c8d7ed398a4998bc91ebf1701086f889e100f2109e2375ef0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 8, 2026.

Transparency log

Release files / ddi_l-0.1.0-py3-none-any.whl

Download URL ddi_l-0.1.0-py3-none-any.whl
Size 1.5 MB
Tags Python 3
SHA-256 checksum
How to use checksums
ed9572ec664cc5bc24bd29f82320c7ec073e9ad00fff8199071b27e51d209858
BLAKE2b-256 checksum
How to use checksums
a28e9103afea9987a36230ec19a1d4c52c3672ca76d2a1367dad77fde0872931
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

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