ddi-l
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 inddi_l.modelsinstead.ddi_l.serverand 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.txtandreadme.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)
| File | Size | Uploaded | |
|---|---|---|---|
| ddi_l-0.1.0.tar.gz | 2.6 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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