odmlib

Submodules

builder module

Optional builder/fluent API for constructing ODM documents.

The builder provides a chainable interface for constructing complex documents with less boilerplate. It wraps the standard odmlib model classes and is purely additive — all existing APIs continue to work unchanged.

Example:

from odmlib.builder import ODMBuilder

odm = (ODMBuilder("odm_1_3_2")
    .set_file(FileOID="F.001", FileType="Snapshot",
              CreationDateTime="2024-01-01T00:00:00")
    .add_study(OID="S.001",
               study_name="My Study",
               study_description="A study",
               protocol_name="PROT-001")
    .add_metadata_version(OID="MDV.001", Name="Version 1")
    .add_item_group_def(OID="IG.DM", Name="Demographics", Repeating="No")
    .add_item_ref(ItemOID="IT.SUBJID", Mandatory="Yes", OrderNumber=1)
    .add_item_ref(ItemOID="IT.AGE", Mandatory="No", OrderNumber=2)
    .build())
class builder.ODMBuilder(model_package='odm_1_3_2')[source]

Bases: object

Fluent builder for constructing ODM documents.

Maintains state for the currently active Study, MetaDataVersion, and ItemGroupDef so that add_* calls automatically append to the right parent.

Works with odm_1_3_2, define_2_0, define_2_1 and odm_2_0. Model-shape differences are handled automatically — notably the ODM 2.0 Study (scalar StudyName / ProtocolName, no GlobalVariables) and its object-valued Description. Note that ODM 2.0 is structurally flatter: StudyEventDef references ItemGroupRef directly (there is no FormDef / FormRef), so the form-level helpers do not apply to odm_2_0.

Parameters:

model_package (str) – The odmlib model package to use. Defaults to "odm_1_3_2".

set_file(**kwargs)[source]

Set ODM file-level attributes (FileOID, FileType, CreationDateTime, …).

These are passed directly to the ODM constructor when build() is called.

Returns:

self, for chaining.

Parameters:

kwargs (Any)

Return type:

ODMBuilder

add_study(OID, study_name, study_description, protocol_name, **kwargs)[source]

Add a Study element.

The Study is constructed in whichever shape the active model uses:

  • ODM 1.3.2 / Define-XML: a GlobalVariables element wrapping StudyName / StudyDescription / ProtocolName children.

  • ODM 2.0: scalar StudyName / ProtocolName attributes on Study plus an object-valued Description (there is no GlobalVariables element in ODM 2.0).

Parameters:
  • OID (str) – Study OID.

  • study_name (str) – Study name (StudyName content/attribute).

  • study_description (str) – Study description.

  • protocol_name (str) – Protocol name (ProtocolName content/attribute).

  • kwargs (Any) – Additional Study attributes.

Returns:

self, for chaining.

Return type:

ODMBuilder

add_metadata_version(**kwargs)[source]

Add a MetaDataVersion to the current Study.

Parameters:

kwargs (Any) – MetaDataVersion attributes (OID, Name, …).

Returns:

self, for chaining.

Raises:

RuntimeError – if no Study has been added yet.

Return type:

ODMBuilder

add_item_group_def(**kwargs)[source]

Add an ItemGroupDef to the current MetaDataVersion.

Parameters:

kwargs (Any) – ItemGroupDef attributes (OID, Name, Repeating, …).

Returns:

self, for chaining.

Raises:

RuntimeError – if no MetaDataVersion has been added yet.

Return type:

ODMBuilder

add_item_ref(**kwargs)[source]

Add an ItemRef to the current ItemGroupDef.

Parameters:

kwargs (Any) – ItemRef attributes (ItemOID, Mandatory, OrderNumber, …).

Returns:

self, for chaining.

Raises:

RuntimeError – if no ItemGroupDef has been added yet.

Return type:

ODMBuilder

add_item_def(**kwargs)[source]

Add an ItemDef to the current MetaDataVersion.

Parameters:

kwargs (Any) – ItemDef attributes (OID, Name, DataType, …).

Returns:

self, for chaining.

Raises:

RuntimeError – if no MetaDataVersion has been added yet.

Return type:

ODMBuilder

add_code_list(OID, Name, DataType, items=None, **kwargs)[source]

Add a CodeList to the current MetaDataVersion.

Parameters:
  • OID (str) – CodeList OID.

  • Name (str) – CodeList name.

  • DataType (str) – Coded value data type.

  • items (list | None) – Optional list of dicts. Each dict must have a "CodedValue" key and may have a "Decode" key (a plain English label string). If "Decode" is present a CodeListItem is created; otherwise an EnumeratedItem.

  • kwargs (Any) – Additional CodeList attributes.

Returns:

self, for chaining.

Raises:

RuntimeError – if no MetaDataVersion has been added yet.

Return type:

ODMBuilder

with_description(text, lang='en')[source]

Attach a Description to the most recently created element.

Applies to the most recently created ItemGroupDef or ItemDef. Falls back to the current MetaDataVersion if neither is set.

Parameters:
  • text (str) – Description text.

  • lang (str) – Language code (default "en").

Returns:

self, for chaining.

Return type:

ODMBuilder

add_study_event_def(**kwargs)[source]

Add a StudyEventDef to the current MetaDataVersion.

The StudyEventDef is appended to MetaDataVersion.StudyEventDef and becomes the current study event so that subsequent add_form_ref() (ODM 1.3.2) or add_item_group_ref() (ODM 2.0) calls attach to it. This does not create a Protocol reference — use add_study_event_ref() for that.

Parameters:

kwargs (Any) – StudyEventDef attributes (OID, Name, Repeating, Type, …).

Returns:

self, for chaining.

Raises:

RuntimeError – if no MetaDataVersion has been added yet.

Return type:

ODMBuilder

add_study_event_ref(**kwargs)[source]

Add a StudyEventRef to the current MetaDataVersion’s Protocol.

The Protocol element is created automatically if it does not exist yet.

ODM 1.3.2 / Define-XML only. The ODM 2.0 XSD has no Protocol/StudyEventRef: a Protocol references StudyEventGroupRef, and StudyEventRef lives one level down on StudyEventGroupDef. Calling this under odm_2_0 therefore raises rather than emitting schema-invalid markup — build the StudyEventGroupDef yourself and graft it on with attach().

Changed in version 0.2.1: Raises under odm_2_0 instead of silently producing an invalid Protocol/StudyEventRef.

Parameters:

kwargs (Any) – StudyEventRef attributes (StudyEventOID, Mandatory, OrderNumber, …).

Returns:

self, for chaining.

Raises:

RuntimeError – if no MetaDataVersion has been added yet, or if the active model is ODM 2.0.

Return type:

ODMBuilder

add_form_def(**kwargs)[source]

Add a FormDef to the current MetaDataVersion (ODM 1.3.2 only).

Becomes the current form so that add_item_group_ref() attaches its ItemGroupRefs here.

Parameters:

kwargs (Any) – FormDef attributes (OID, Name, Repeating, …).

Returns:

self, for chaining.

Raises:

RuntimeError – if no MetaDataVersion yet, or model is ODM 2.0.

Return type:

ODMBuilder

add_form_ref(**kwargs)[source]

Add a FormRef to the current StudyEventDef (ODM 1.3.2 only).

Parameters:

kwargs (Any) – FormRef attributes (FormOID, Mandatory, OrderNumber, …).

Returns:

self, for chaining.

Raises:

RuntimeError – if no StudyEventDef yet, or model is ODM 2.0.

Return type:

ODMBuilder

add_item_group_ref(**kwargs)[source]

Add an ItemGroupRef to the appropriate parent.

In ODM 1.3.2 / Define-XML the ItemGroupRef is added to the current FormDef (call add_form_def() first). In ODM 2.0 — which has no FormDef — it is added to the current StudyEventDef (call add_study_event_def() first).

Parameters:

kwargs (Any) – ItemGroupRef attributes (ItemGroupOID, Mandatory, …).

Returns:

self, for chaining.

Raises:

RuntimeError – if the required parent has not been added yet.

Return type:

ODMBuilder

add_method_def(OID, Name, Type, description, formal_expression=None, expression_context='Python', lang='en', **kwargs)[source]

Add a MethodDef to the current MetaDataVersion.

MethodDef requires a Description; it is built from description. An optional FormalExpression is added when formal_expression is given.

formal_expression is plain text for every model. Under odm_2_0 it is wrapped in the Code child the XSD requires; under ODM 1.3.2 / Define-XML it stays element text.

Under odm_2_0 the XSD also makes MethodSignature a required child of MethodDef, so an empty one is emitted for you (both of its children are optional, so <MethodSignature/> is valid); populate it via attach() if the method takes parameters. ODM 1.3.2 adds no MethodSignature.

Changed in version 0.2.1: formal_expression is now valid under odm_2_0, and a MethodSignature is emitted there, so the output is schema-valid.

Parameters:
  • OID (str) – Method OID.

  • Name (str) – Method name.

  • Type (str) – Method type (e.g. “Computation”, “Imputation”).

  • description (str) – Human-readable description text.

  • formal_expression (str | None) – Optional expression source code.

  • expression_context (str) – FormalExpression Context (e.g. “Python”, “SAS”); used only when formal_expression is given.

  • lang (str) – Language code for the Description (default "en").

  • kwargs (Any) – Additional MethodDef attributes.

Returns:

self, for chaining.

Raises:

RuntimeError – if no MetaDataVersion has been added yet.

Return type:

ODMBuilder

add_condition_def(OID, Name, description=None, formal_expression=None, expression_context='Python', lang='en', **kwargs)[source]

Add a ConditionDef to the current MetaDataVersion.

Under odm_2_0 the XSD makes both Description and MethodSignature required children, so description is mandatory there and an empty MethodSignature is emitted for you (both of its children are optional, so <MethodSignature/> is valid). Populate it further via attach() if the condition’s expressions take parameters. ODM 1.3.2 is unchanged: description stays optional and no MethodSignature is added.

formal_expression is plain text for every model; under odm_2_0 it is wrapped in a Code child (see add_method_def()).

Changed in version 0.2.1: Produces schema-valid output under odm_2_0.

Parameters:
  • OID (str) – Condition OID.

  • Name (str) – Condition name.

  • description (str | None) – Description text. Required under odm_2_0, optional for ODM 1.3.2.

  • formal_expression (str | None) – Optional expression source code.

  • expression_context (str) – FormalExpression Context; used only when formal_expression is given.

  • lang (str) – Language code for the Description (default "en").

  • kwargs (Any) – Additional ConditionDef attributes.

Returns:

self, for chaining.

Raises:

RuntimeError – if no MetaDataVersion has been added yet, or if description is omitted under odm_2_0.

Return type:

ODMBuilder

add_measurement_unit(OID, Name, symbol, lang='en', **kwargs)[source]

Add a MeasurementUnit to the Study’s BasicDefinitions.

BasicDefinitions is created automatically if absent. Not available in ODM 2.0 (which has no BasicDefinitions/MeasurementUnit).

Parameters:
  • OID (str) – MeasurementUnit OID.

  • Name (str) – MeasurementUnit name.

  • symbol (str) – Symbol text (wrapped in Symbol/TranslatedText).

  • lang (str) – Language code for the Symbol (default "en").

  • kwargs (Any) – Additional MeasurementUnit attributes.

Returns:

self, for chaining.

Raises:

RuntimeError – if no Study yet, or model is ODM 2.0.

Return type:

ODMBuilder

with_question(text, lang='en')[source]

Set the Question on the most recently created ItemDef.

Parameters:
  • text (str) – Question text.

  • lang (str) – Language code (default "en").

Returns:

self, for chaining.

Raises:

RuntimeError – if no ItemDef has been added yet.

Return type:

ODMBuilder

with_codelist_ref(codelist_oid)[source]

Attach a CodeListRef to the most recently created ItemDef.

Parameters:

codelist_oid (str) – OID of the referenced CodeList.

Returns:

self, for chaining.

Raises:

RuntimeError – if no ItemDef has been added yet.

Return type:

ODMBuilder

with_measurement_unit_ref(mu_oid)[source]

Add a MeasurementUnitRef to the most recent ItemDef (ODM 1.3.2 only).

Parameters:

mu_oid (str) – OID of the referenced MeasurementUnit.

Returns:

self, for chaining.

Raises:

RuntimeError – if no ItemDef yet, or model is ODM 2.0.

Return type:

ODMBuilder

with_range_check(comparator, check_values, soft_hard='Soft', **kwargs)[source]

Add a RangeCheck to the most recently created ItemDef.

Parameters:
  • comparator (str | None) – Comparator (e.g. “GE”, “LE”, “IN”); may be None.

  • check_values (list) – List of check values (rendered as CheckValue).

  • soft_hard (str) – SoftHard flag (default "Soft").

  • kwargs (Any) – Additional RangeCheck attributes.

Returns:

self, for chaining.

Raises:

RuntimeError – if no ItemDef has been added yet.

Return type:

ODMBuilder

with_alias(context, name)[source]

Add an Alias to the most recent ItemDef (or ItemGroupDef).

Applies to the most recently created ItemDef; falls back to the current ItemGroupDef.

Parameters:
  • context (str) – Alias Context.

  • name (str) – Alias Name.

Returns:

self, for chaining.

Raises:

RuntimeError – if neither an ItemDef nor ItemGroupDef exists.

Return type:

ODMBuilder

attach(parent, element)[source]

Attach a pre-built ODMElement onto an explicit parent element.

Escape hatch for any model element that has no dedicated add_* method. The target child field is resolved from the parent’s model definition — appended if it is a list element, assigned otherwise.

Parameters:
  • parent (Any) – An ODMElement already in the tree being built (e.g. a value returned via current).

  • element (Any) – A pre-built ODMElement to attach.

Returns:

self, for chaining.

Raises:

TypeError – if element is not an ODMElement or parent has no child field able to hold it.

Return type:

ODMBuilder

attach_to_current(element)[source]

Attach a pre-built ODMElement to the most specific current parent.

Tries the active ItemDef, FormDef, StudyEventDef, ItemGroupDef, MetaDataVersion and Study in turn, attaching to the first that accepts element’s type.

Parameters:

element (Any) – A pre-built ODMElement to attach.

Returns:

self, for chaining.

Raises:

TypeError – if no active element can hold element.

Return type:

ODMBuilder

property current: dict

The active context elements, for use with attach().

Keys: study, mdv, item_group_def, item_def, form_def, study_event_def (value is None if not set).

build()[source]

Construct and return the top-level ODM object.

Returns:

An ODM instance containing all added elements.

Return type:

Any

context module

Context managers for common odmlib load-modify-save workflows.

Provides open_odm() and open_define() context managers that automatically load an ODM or Define-XML document on entry and, when a write target is configured, write it back on clean exit.

Opening a document with only an input_file is read-only: nothing is written when the with block exits. Provide output_file to write the (possibly modified) document to a different path, or pass write_on_exit=True to explicitly opt in to updating input_file in place.

Example — read an ODM file (read-only, nothing written):

from odmlib.context import open_odm

with open_odm("study.xml") as odm:
    print(odm.FileOID)

Example — modify an ODM file, write to a different path:

with open_odm("study.xml", output_file="study_updated.xml") as odm:
    odm.FileOID = "F.002"

Example — update an ODM file in place (explicit opt-in):

with open_odm("study.xml", write_on_exit=True) as odm:
    mdv = odm.Study[0].MetaDataVersion[0]
    mdv.ItemGroupDef.append(new_igd)
# study.xml is overwritten with the modified content

Example — read a Define-XML 2.1 file:

from odmlib.context import open_define

with open_define("define.xml") as define:
    # Define-XML: Study and MetaDataVersion are single objects, not lists
    mdv = define.Study.MetaDataVersion
    print(len(mdv.ItemDef))
class context.ODMContext(input_file, output_file=None, model_package='odm_1_3_2', format=None, permissive=False, write_on_exit=None)[source]

Bases: object

Context manager for load-modify-save ODM workflows.

The document is loaded when the with block is entered and written back to output_file when the block exits normally. If an exception propagates out of the with block the file is not written (the exception is re-raised unchanged).

Parameters:
  • input_file (str) – Path to the ODM file to load.

  • output_file (Optional[str]) – Path to write on clean exit. When omitted, the document is opened read-only and nothing is written (unless write_on_exit=True explicitly opts in to updating input_file in place).

  • model_package (str) – odmlib model package name. Defaults to "odm_1_3_2".

  • format (Optional[str]) – "xml" or "json". Auto-detected from the file extension when not specified.

  • write_on_exit (Optional[bool]) – When omitted, the document is written on clean exit only if output_file was provided. Pass True to force writing (to output_file, or in place to input_file when no output_file is given). Pass False to suppress writing entirely.

  • permissive (Union[bool, _mode.ValidationMode])

class context.DefineContext(input_file, output_file=None, model_package='define_2_1', format=None, permissive=False, write_on_exit=None)[source]

Bases: ODMContext

Context manager for load-modify-save Define-XML workflows.

Identical to ODMContext but uses Define-XML loaders by default.

Parameters:
  • input_file (str) – Path to the Define-XML file to load.

  • output_file (Optional[str]) – Path to write on clean exit. When omitted, the document is opened read-only and nothing is written (unless write_on_exit=True explicitly opts in to updating input_file in place).

  • model_package (str) – odmlib model package name. Defaults to "define_2_1".

  • format (Optional[str]) – "xml" or "json".

  • write_on_exit (Optional[bool]) – When omitted, the document is written on clean exit only if output_file was provided. True forces writing (in place when no output_file); False suppresses writing entirely.

  • permissive (Union[bool, _mode.ValidationMode])

context.open_odm(input_file, output_file=None, model_package='odm_1_3_2', format=None, permissive=False, write_on_exit=None)[source]

Open an ODM document as a context manager.

Parameters:
  • input_file (str) – Path to the ODM file.

  • output_file (str | None) – Write path on clean exit. When omitted, the document is opened read-only (nothing is written unless write_on_exit=True opts in to an in-place update).

  • model_package (str) – odmlib model package (default "odm_1_3_2").

  • format (str | None) – "xml" or "json" (auto-detected if omitted).

  • permissive (bool | ValidationMode) – If True, load in fully permissive mode. Pass a ValidationMode flag combination for targeted relaxation. Defaults to False (strict).

  • write_on_exit (bool | None) – When omitted, the document is written on clean exit only if output_file was provided. True forces writing (in place when no output_file); False suppresses writing entirely.

Returns:

An ODMContext instance.

Return type:

ODMContext

Example:

# Read-only inspection — input file is never modified
with open_odm("study.xml") as odm:
    print(odm.FileOID)

with open_odm("broken.xml", permissive=True) as odm:
    ...  # permissive mode active during load

# Modify and write to a new file
with open_odm("study.xml", output_file="study_v2.xml") as odm:
    odm.FileOID = "F.002"

# Update the input file in place (explicit opt-in)
with open_odm("study.xml", write_on_exit=True) as odm:
    odm.FileOID = "F.002"
context.open_define(input_file, output_file=None, model_package='define_2_1', format=None, permissive=False, write_on_exit=None)[source]

Open a Define-XML document as a context manager.

Parameters:
  • input_file (str) – Path to the Define-XML file.

  • output_file (str | None) – Write path on clean exit. When omitted, the document is opened read-only (nothing is written unless write_on_exit=True opts in to an in-place update).

  • model_package (str) – odmlib model package (default "define_2_1").

  • format (str | None) – "xml" or "json" (auto-detected if omitted).

  • permissive (bool | ValidationMode) – If True, load in fully permissive mode. Pass a ValidationMode flag combination for targeted relaxation. Defaults to False (strict).

  • write_on_exit (bool | None) – When omitted, the document is written on clean exit only if output_file was provided. True forces writing (in place when no output_file); False suppresses writing entirely.

Returns:

A DefineContext instance.

Return type:

DefineContext

Example:

# Read-only inspection — input file is never modified
with open_define("define.xml") as define:
    # Define-XML: Study/MetaDataVersion are single objects, not lists
    mdv = define.Study.MetaDataVersion

with open_define("broken.xml", permissive=True) as define:
    ...  # permissive mode active during load

descriptor module

Base descriptor protocol for ODM element attributes.

This module provides the Descriptor base class that implements the Python descriptor protocol for ODM element attributes. All odmlib attribute types inherit from this class.

descriptor.is_pristine_auto_created(elem)[source]

True if elem was auto-created by attribute access and never populated.

Used by serialization to skip empty elements that exist only because an unset optional child attribute was read.

Parameters:

elem (Any)

Return type:

bool

class descriptor.Descriptor(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: object

Base descriptor for all ODM element attributes.

Implements the Python descriptor protocol (__get__, __set__, __delete__). When accessed on a class, returns the descriptor itself. When accessed on an instance, returns the value from the instance’s __dict__.

For required attributes without a set value, raises OdmlibRequiredAttributeError. For optional list attributes without a value, auto-initializes to [] (stored on the instance so the append idiom works). For unset optional scalar attributes, returns None. For unset single child elements, auto-instantiates the element class when it can be constructed empty (returns None otherwise); the auto-created child is skipped by serialization unless populated.

Parameters:
  • name (str) – Attribute name (set by ODMMeta during class creation).

  • required (bool) – If True, attribute must be provided at construction.

  • element_class (type) – For child element descriptors, the expected class.

  • valid_values (list) – List of permitted values (empty = any value).

  • namespace (str) – XML namespace prefix (default: “odm”).

document_loader module

Abstract base class for all odmlib document loaders.

Defines the interface that all concrete loaders (XMLODMLoader, JSONODMLoader, XMLDefineLoader, JSONDefineLoader, XMLArmLoader, JSONArmLoader).

Loaders are not typically used directly; pass an instance to ODMLoader for a unified facade.

document_loader.resolve_model_class(model_module, elem_name)[source]

Return the model class for an element tag, or raise a clear error.

Previously an unknown element in a document surfaced as a bare AttributeError from getattr deep inside the recursive load.

Parameters:
  • model_module – The imported model module (e.g. odmlib.odm_1_3_2.model).

  • elem_name – The element tag / dict key naming the class.

Raises:

OdmlibParsingError – If the model has no class for elem_name.

class document_loader.DocumentLoader[source]

Bases: ABC

Abstract base class for odmlib document loaders.

All concrete loaders implement this interface so they can be wrapped by the ODMLoader facade.

abstractmethod load_document(doc)[source]

Recursively load a parsed document element or dict into odmlib objects.

Parameters:

doc – An XML Element (XML loaders) or dict (JSON loaders).

Returns:

An odmlib element object populated from doc.

abstractmethod create_document(filename)[source]

Parse a document file and store its contents internally.

Parameters:

filename (str) – Path to the ODM file (XML or JSON).

Returns:

The parsed root element or dict.

abstractmethod create_document_from_string(odm_string)[source]

Parse a document string and store its contents internally.

Parameters:

odm_string (str) – XML or JSON string containing the ODM document.

Returns:

The parsed root element or dict.

abstractmethod load_metadataversion(idx)[source]

Return the MetaDataVersion at the given index.

Parameters:

idx (int) – Zero-based index of the MetaDataVersion to return.

Returns:

A MetaDataVersion odmlib object.

abstractmethod load_study(idx)[source]

Return the Study at the given index.

Parameters:

idx (int) – Zero-based index of the Study to return.

Returns:

A Study odmlib object.

abstractmethod load_odm()[source]

Return the root ODM element loaded from the stored document.

Returns:

The root odmlib ODM object.

exceptions module

odmlib exception hierarchy.

All odmlib-specific exceptions inherit from OdmlibError, which itself inherits from Exception. During the v0.2.x transition period, validation and type exceptions also inherit from ValueError/TypeError respectively so that existing except clauses continue to work.

Deprecation schedule:
v0.2.x: New exceptions dual-inherit from ValueError/TypeError.

All existing except ValueError and except TypeError clauses continue to work unchanged.

v0.3.0: Remove the ValueError/TypeError base classes.

Update except ValueError → except OdmlibValidationError and except TypeError → except OdmlibTypeError.

exception exceptions.OdmlibError[source]

Bases: Exception

Base exception for all odmlib errors.

exception exceptions.OdmlibValidationError(message, *, element_path=None, hint=None, attribute=None, element_type=None, actual_value=None)[source]

Bases: OdmlibError, ValueError

Raised when an element fails conformance, OID, or schema validation.

element_path

Dot-separated path from root to the failing element, e.g. “ODM > Study > MetaDataVersion > ItemGroupDef(OID=’IG.VS’)”

hint

Optional human-readable suggestion for fixing the problem.

attribute

The attribute name that caused the error (if applicable).

element_type

The class name of the element that failed validation.

exception exceptions.OdmlibRequiredAttributeError(message, *, element_path=None, hint=None, attribute=None, element_type=None, actual_value=None)[source]

Bases: OdmlibValidationError

Raised when a required attribute is missing during construction.

exception exceptions.OdmlibOIDError(message, *, element_path=None, hint=None, attribute=None, element_type=None, actual_value=None)[source]

Bases: OdmlibValidationError

Raised for OID uniqueness or ref/def integrity failures.

exceptions.flatten_cerberus_errors(errors, prefix='')[source]

Flatten a nested Cerberus error dict into (dotted_path, message) pairs.

Cerberus reports {field: [msg | {subkey: [...]}, ...]} where subkey is a nested field name or, for sequences, the integer index of the offending list item:

{'ItemGroupDef': [{0: [{'Name': ['required field']}]}]}
-> [('ItemGroupDef.0.Name', 'required field')]
Parameters:
  • errors – A Cerberus error dict (typically OdmlibConformanceError.cerberus_errors). Anything that is not a dict yields an empty list.

  • prefix – Dotted path accumulated so far; used by the recursion.

Returns:

(path, message) tuples in Cerberus’ own document order, which is deterministic — no sets are involved.

Return type:

list

exception exceptions.OdmlibConformanceError(message, *, cerberus_errors=None, field_path=None, **kwargs)[source]

Bases: OdmlibValidationError

Raised when Cerberus conformance validation fails.

A single instance normally bundles every violation Cerberus found, so validate() calls expand() in collect mode to turn it into one error per failing field.

cerberus_errors

The raw Cerberus error dict for programmatic access. On errors produced by expand() this remains the complete dict for the document, shared by reference with its siblings.

field_path

Dotted path to the single failing field (e.g. "ItemGroupDef.0.Name"). Set only on errors produced by expand(); None on the bundled error.

expand()[source]

Split this bundled error into one error per failing leaf field.

Returns:

One OdmlibConformanceError per Cerberus leaf message, each carrying field_path, attribute (the leaf name) and element_path (the containing path). Returns [self] when there is no Cerberus payload to split — e.g. the “No conformance schema registered” error — so nothing is lost.

Return type:

list

exception exceptions.OdmlibElementOrderError(message, *, element_path=None, hint=None, attribute=None, element_type=None, actual_value=None)[source]

Bases: OdmlibValidationError

Raised when child elements are not in the order required by the ODM spec.

exception exceptions.OdmlibErrorLimitError(message, *, element_path=None, hint=None, attribute=None, element_type=None, actual_value=None)[source]

Bases: OdmlibValidationError

Appended as the final entry when validate(max_errors=N) stops early.

Its presence means validation was truncated and additional problems may exist. It is never raised — only collected.

exception exceptions.OdmlibSchemaValidationError(wrapped_exception, *, hint=None)[source]

Bases: OdmlibValidationError

Raised when XSD/XML-Schema validation of an ODM document fails.

Wraps the underlying xmlschema exception raised during XSD validation (e.g. by ODMSchemaValidator.validate_file()). The wrapped exception is available at args[0] (for backward compatibility with callers that introspect ex.args[0].msg) and via the wrapped attribute.

exception exceptions.OdmlibTypeError(message, *, attribute=None, expected_type=None, actual_value=None, element_path=None, hint=None, element_type=None)[source]

Bases: OdmlibError, TypeError

Raised when a value has the wrong type for an attribute.

attribute

The attribute name.

expected_type

The expected type description.

actual_value

The value that was provided.

element_path

Path from root to the failing element (if known).

hint

Optional human-readable suggestion for fixing the problem.

exception exceptions.OdmlibParsingError(message, *, hint=None)[source]

Bases: OdmlibError

Raised when an XML or JSON document cannot be parsed into the model.

exception exceptions.OdmlibLoaderStateError(message, *, hint=None)[source]

Bases: OdmlibParsingError, ValueError

Raised when a loader method is called before the document is opened.

Also inherits from ValueError for backward compatibility with code that catches ValueError from loader calls.

exception exceptions.OdmlibSerializationError[source]

Bases: OdmlibError

Raised when an in-memory model cannot be written to XML or JSON.

exception exceptions.OdmlibNamespaceError(message, *, hint=None)[source]

Bases: OdmlibError, ValueError

Raised for namespace registration or lookup failures.

exception exceptions.OdmlibWarning[source]

Bases: UserWarning

Base warning for all odmlib non-fatal issues.

exception exceptions.OdmlibDeprecationWarning[source]

Bases: OdmlibWarning, DeprecationWarning

Issued when a deprecated odmlib feature is used.

exception exceptions.OdmlibInteroperabilityWarning[source]

Bases: OdmlibWarning

Issued for constructs that are valid but may cause interoperability issues.

class exceptions.ErrorCollector(max_errors=None)[source]

Bases: object

Accumulates validation errors instead of raising immediately.

Also serves as the sink for the collecting-checker protocol: a checker that mixes in ErrorReporting hands its errors here instead of raising, so one pass surfaces every problem it can find.

Example:

errors = odm.validate(collect_errors=True, oid_checker=checker)
for err in errors:
    print(err)
Parameters:

max_errors – Optional cap. Once this many errors are collected, the next add_error() sets truncated and raises the internal _ErrorLimitReached sentinel to unwind the in-progress validation walk. None (the default) collects everything and never raises, so hand-built ErrorCollector() instances behave exactly as before.

errors

List of OdmlibError instances collected so far.

warnings

List of OdmlibWarning instances collected so far.

max_errors

The cap, or None for uncapped.

truncated

True once the cap stopped collection short.

property has_errors

True if any errors have been collected.

property is_full

True when max_errors is set and that many errors are collected.

add_error(error)[source]

Add an OdmlibError to the collection.

Raises:

_ErrorLimitReached – If the collector is already at max_errors. Never raised when max_errors is None.

add_warning(warning)[source]

Add an OdmlibWarning to the collection.

raise_if_errors()[source]

Raise if any errors were collected.

Raises the single collected error directly if only one exists, or a new OdmlibValidationError summarising all errors otherwise.

class exceptions.ErrorReporting[source]

Bases: object

Mixin giving a validation checker an optional error sink.

A checker that mixes this in replaces raise SomeOdmlibError(...) with self.report(SomeOdmlibError(...)). With no sink installed (the default) report() re-raises, so fail-fast behaviour is identical to raising directly. Inside a collecting() block the error is handed to the sink and the checker keeps going, so a single pass surfaces every problem rather than only the first.

Third-party checkers may opt in by inheriting this mixin, or by duck-typing supports_error_collection = True plus report() and collecting(). Checkers that do neither still work — they simply contribute at most one error, exactly as before (see is_collecting_checker()).

Note

Not thread-safe: the sink lives on the instance. Checkers are already stateful (they accumulate OIDs), so use one checker per document per thread.

supports_error_collection = True
report(error)[source]

Raise error (fail-fast) or hand it to the installed sink.

Parameters:

error – The OdmlibError describing the problem.

Raises:
  • OdmlibError – The error itself, when no sink is installed.

  • _ErrorLimitReached – When the sink is full (see ErrorCollector).

collecting(sink)[source]

Install sink for the duration of the block, then restore.

Parameters:

sink – Any object with an add_error(error) method — normally an ErrorCollector.

Example:

collector = ErrorCollector()
with checker.collecting(collector):
    odm.verify_oids(checker)
for err in collector.errors:
    print(err)
exceptions.is_collecting_checker(checker)[source]

Return True if checker implements the collecting-checker protocol.

Used by validate() to decide whether a checker can enumerate every problem or should fall back to fail-fast (one error for the whole layer). Duck-typed checkers qualify without inheriting ErrorReporting.

loader module

Facade loader for ODM, Define-XML, and ARM documents.

This module provides ODMLoader, a unified interface that wraps any specialized loader (XMLODMLoader, JSONODMLoader, XMLDefineLoader, JSONDefineLoader, XMLArmLoader, or JSONArmLoader) and delegates all document loading operations to it.

class loader.ODMLoader(odm_loader)[source]

Bases: object

Facade providing a unified interface for loading ODM documents.

Wraps a specialized loader (XMLODMLoader, JSONODMLoader, XMLDefineLoader, JSONDefineLoader, XMLArmLoader, or JSONArmLoader) and delegates all operations to it.

Parameters:

odm_loader (DocumentLoader) – A specialized loader instance.

Raises:

OdmlibTypeError – If odm_loader is not a DocumentLoader instance.

Example:

import odmlib.odm_loader as OL
import odmlib.loader as LD

loader = LD.ODMLoader(OL.XMLODMLoader(model_package="odm_1_3_2"))
loader.open_odm_document("study.xml")
odm = loader.root()
mdv = loader.MetaDataVersion()
create_odmlib(odm_doc, odm_key=None)[source]

Load an odmlib object from an existing document dict or element.

Delegates to the wrapped loader’s load_document method.

Parameters:
  • odm_doc (Any) – The document dict (JSON loaders) or XML Element (XML loaders) to load from.

  • odm_key (str | None) – The element key/class name to use when loading from a dict (JSON loaders only).

Returns:

An odmlib element object populated from odm_doc.

Return type:

Any

open_odm_document(filename)[source]

Parse an ODM document file and prepare for loading.

Parameters:

filename (str) – Path to the ODM XML or JSON file.

Returns:

The parsed root element or dict, depending on the loader type.

Return type:

Any

load_odm_string(odm_string)[source]

Parse an ODM document from a string and prepare for loading.

Parameters:

odm_string (str) – XML or JSON string containing the ODM document.

Returns:

The parsed root element or dict, depending on the loader type.

Return type:

Any

root()[source]

Return the root ODM element.

Must be called after open_odm_document() or load_odm_string().

Returns:

The root odmlib ODM object (e.g., an ODM instance).

Return type:

Any

MetaDataVersion(idx=0)[source]

Return the MetaDataVersion at the specified index.

Must be called after open_odm_document() or load_odm_string().

Parameters:

idx (int) – Zero-based index of the MetaDataVersion to return (default: 0, the first one).

Returns:

A MetaDataVersion odmlib object.

Return type:

Any

Study(idx=0)[source]

Return the Study at the specified index.

Must be called after open_odm_document() or load_odm_string().

Parameters:

idx (int) – Zero-based index of the Study to return (default: 0, the first one).

Returns:

A Study odmlib object.

Return type:

Any

ns_registry module

Global registry for XML namespace prefix-to-URI mappings.

This module provides Borg and NamespaceRegistry for managing XML namespace declarations used in ODM and Define-XML documents.

The Borg pattern ensures that all instances of NamespaceRegistry share the same state, providing a convenient global registry without requiring a strict singleton or global variable.

Example:

import odmlib.ns_registry as NS

NS.NamespaceRegistry(prefix="odm",
    uri="http://www.cdisc.org/ns/odm/v1.3",
    is_default=True, is_reset=True)
NS.NamespaceRegistry(prefix="def",
    uri="http://www.cdisc.org/ns/def/v2.1")
ns_registry.bind_document_namespaces(odm_obj, snapshot=None, recursive=False)[source]

Associate odm_obj with a namespace snapshot for serialization.

Parameters:
  • odm_obj – A loaded odmlib element (typically the document root).

  • snapshot – A dict as returned by NamespaceRegistry.snapshot(). When None, the registry’s current state is captured.

  • recursive – When True, also bind every descendant element, so that serializing a child reached by walking the tree (define.Study.MetaDataVersion) uses the same namespaces as its root instead of falling back to current global registry state.

ns_registry.get_document_namespaces(odm_obj)[source]

Return the namespace snapshot bound to odm_obj, or None.

class ns_registry.Borg[source]

Bases: object

Borg pattern base class providing shared state across instances.

All instances of subclasses share the same namespaces and default_namespace dictionaries, enabling a singleton-like namespace registry without requiring a true singleton.

Class Attributes:

namespaces (dict): Maps prefix to URI for all registered namespaces. default_namespace (dict): Maps the default namespace prefix to URI.

namespaces = {}
default_namespace = {}
classmethod reset()[source]

Reset all shared namespace state to empty dicts.

Should be called between tests or when reinitializing the registry from scratch to prevent cross-test state leakage.

class ns_registry.NamespaceRegistry(prefix=None, uri=None, is_default=False, is_reset=False)[source]

Bases: Borg

Global registry for XML namespace prefix-to-URI mappings.

Uses the Borg pattern so all instances share state. Manages namespace prefix registration and provides methods for XML serialization.

Parameters:
  • prefix (str) – Namespace prefix (e.g., “odm”, “def”, “xs”).

  • uri (str) – Namespace URI (must be a valid URL).

  • is_default (bool) – If True, this becomes the default namespace (used for xmlns= in XML output).

  • is_reset (bool) – If True, clears all existing entries first.

Raises:

OdmlibNamespaceError – If the URI is not a valid URL.

Example:

import odmlib.ns_registry as NS
NS.NamespaceRegistry(prefix="odm",
    uri="http://www.cdisc.org/ns/odm/v1.3",
    is_default=True, is_reset=True)
snapshot()[source]

Return a copy of the current registry state.

Returns:

{"namespaces": {prefix: uri, ...}, "default": {prefix: uri}}.

Return type:

dict

get_odm_namespace_entries()[source]

Return namespace entries as xmlns= strings for XML serialization.

Returns:

A list of strings like ["xmlns=http://...", "xmlns:def=http://..."].

The first entry is always the default namespace.

Return type:

list[str]

get_ns_entry_dict(prefix)[source]

Return a {prefix: uri} dict for the given prefix, or {} if not found.

Used by loaders to build the namespaces dict for ElementTree find and findall calls.

Parameters:

prefix (str) – Namespace prefix to look up.

Returns:

{prefix: uri} if registered, otherwise {}.

Return type:

dict

get_ns_attribute_name(name, prefix)[source]

Return the full ElementTree-style attribute name with namespace braces.

For the default namespace, returns the bare name. For non-default namespaces, returns {uri}name.

Parameters:
  • name (str) – The local attribute name.

  • prefix (str) – The namespace prefix.

Returns:

The qualified attribute name (e.g., "{http://...}lang"

or "lang" for the default namespace).

Return type:

str

Raises:

OdmlibNamespaceError – If prefix has not been registered.

get_prefix_ns_from_uri(uri)[source]

Return the prefix for a given namespace URI.

Parameters:

uri (str) – The namespace URI to look up (case-insensitive match).

Returns:

The registered prefix for that URI.

Return type:

str

Raises:

OdmlibNamespaceError – If no prefix is registered for uri.

set_odm_namespace_attributes(odm_elem, namespaces=None, default=None)[source]

Set xmlns attributes on an ElementTree root element.

Adds xmlns for the default namespace and xmlns:prefix for each additional namespace that is actually used in the tree. The reserved xml prefix is never declared (it is implicitly bound per the XML specification).

Parameters:
  • odm_elem (ET.Element) – The root XML element to annotate.

  • namespaces (dict) – Optional {prefix: uri} map to use instead of the registry’s shared state (e.g. a per-document snapshot).

  • default (dict) – Optional {prefix: uri} default-namespace map to use instead of the registry’s shared state.

Raises:

OdmlibNamespaceError – If no default namespace is registered.

set_odm_namespace_attributes_string(odm_str)[source]

Add xmlns attributes to an ODM XML string.

Deprecated since version 0.2.1: ODMElement.to_xml_string() has declared its own namespaces since 0.2.1, so this string-patching helper is a no-op on any string it would normally be handed. It has no callers in odmlib and will be removed in 0.3.0. Remove the call; no replacement is needed.

Replaces the opening <ODM tag in odm_str with a version that includes all registered namespace declarations.

Parameters:

odm_str (str) – An ODM XML string whose root element is <ODM>.

Returns:

The modified XML string with xmlns attributes injected.

Return type:

str

remove_registry_entry(prefix)[source]

Remove a namespace entry by prefix.

Removes the prefix from both namespaces and default_namespace (if present).

Parameters:

prefix (str) – The namespace prefix to remove.

odm_element module

Core metaclass, writer, and base element class for odmlib ODM models.

This module provides three central components:

  • ODMMeta – metaclass that processes descriptor-based class definitions

  • ODMWriter – utility class for writing ElementTree XML to file

  • ODMElement – base class for all ODM element model objects

All model classes (e.g., Study, ItemDef, MetaDataVersion) inherit from ODMElement and use descriptors from odmlib.typed to define their attributes and child elements.

class odm_element.ODMMeta(clsname, bases, clsdict, merge_fields=False)[source]

Bases: type

Metaclass for all ODM element classes.

Processes class definitions to:

  • Preserve attribute declaration order (using OrderedDict)

  • Separate XML attributes (_attrs) from child elements (_elems)

  • Track non-default namespace prefixes (_attr_ns)

  • Build the _fields list of all declared properties

  • Set the default namespace to “odm” if not specified

class odm_element.ODMWriter[source]

Bases: object

Writes an odmlib ElementTree hierarchy to an XML file.

Handles namespace registration and XML declaration.

static write_odm(odm_file, odm_elem, ns_snapshot=None)[source]

after converting ODMLIB to ElementTree, write the ElementTree to an ODM file :param odm_file: path and file to write the ODM XML :param odm_elem: Element object to write to ODM (presumably an ODM root) :param ns_snapshot: optional per-document namespace snapshot (as returned

by NamespaceRegistry.snapshot()); when omitted, the shared registry state is used

class odm_element.ODMElement(**kwargs)[source]

Bases: object

Base class for all ODM element model objects.

Provides XML and JSON serialization, OID validation, conformance checking, and element ordering verification. All model classes (e.g., Study, ItemDef, MetaDataVersion) inherit from this class.

Serialization:
Validation:
Finding elements:
  • find() – find first child element by attribute value

  • find_all() – find all matching child elements

  • find_by() – find by multiple attribute criteria

Parameters:

kwargs (Any)

__init__(**kwargs)[source]

Initialize an ODM element with keyword arguments.

Sets attributes and child elements on the instance using the provided keyword arguments. Validates that all required attributes are present after construction.

Parameters:

**kwargs (Any) – Attribute names and values for this element. Names must match declared descriptors on the class.

Raises:
Return type:

None

to_json()[source]

transforms odmlib hierarchy into a dict and converts that to JSON and returns it

Returns:

JSON representation of odmlib hierarchy

Return type:

str

to_xml(parent_elem=None, top_elem=None)[source]

Build odmlib’s internal ElementTree serialization buffer.

Prefer :meth:`to_element` if you want a tree to work with. The Element returned here carries no xmlns declarations and uses prefix-literal tags (def:leaf) rather than Clark notation, so on its own it cannot be re-parsed for Define-XML (ParseError: unbound prefix), lands in no namespace for ODM, fails ET.canonicalize(), and does not support namespace-aware find(). Declarations are attached downstream by to_xml_string() and ODMWriter. This method exists as the shared tree builder for those two.

Children are emitted in the model declaration order recorded in _elems by ODMMeta (i.e. the order in which descriptors appear in the class body of the relevant model module). Attribute insertion order on the instance does not affect the output, so users do not need to call reorder_object() before serializing.

Optional children that were never assigned are silently skipped.

Parameters:
  • parent_elem (Element | None) – (obj) Element that is the parent of the element to be created

  • top_elem (Element | None) – (obj) Topmost element that has been created

Returns:

top_elem: (obj) Topmost element created during the recursive calls

Return type:

Element | None

to_xml_string(*, xml_declaration=False)[source]

Convert this element to an XML string.

The string includes xmlns declarations for the default namespace and any prefixes used in the serialized tree, so the result is namespace-well-formed and can be re-parsed on its own.

The two serialization paths line up exactly, which makes a string and a file interchangeable for comparison purposes:

  • xml_declaration=False (the default) returns precisely the bytes write_xml() produces after its <?xml ...?> line.

  • xml_declaration=True returns precisely what write_xml() writes, declaration included.

The default stays False: this string is the documented input to ODMLoader.load_odm_string(), and 0.2.1 shipped it declaration-free.

Parameters:

xml_declaration (bool) – When True, prepend <?xml version='1.0' encoding='UTF-8'?> and a newline. Keyword-only.

Returns:

UTF-8 XML representation of this element.

Return type:

str

to_element()[source]

Return this element as a standard, namespace-resolved ElementTree Element.

Unlike to_xml(), which builds odmlib’s internal serialization buffer with prefix-literal tags (def:leaf) and no declarations, this returns a tree parsed from to_xml_string(). Tags and attributes use Clark notation ({http://www.cdisc.org/ns/def/v2.1}leaf), so namespace-aware find(), ET.canonicalize(), ET.indent() pretty-printing, and grafting the result into a host document you are assembling yourself all behave correctly.

This is the method to reach for when you want an Element; to_xml() is the internal tree builder and its output is not a self-contained document.

Costs one serialize + reparse (~8 ms for a 166 KB Define-XML document).

Note that re-serializing the result with ET.tostring() picks prefixes from ElementTree’s process-global register_namespace() map, so the prefix spelling may differ from the source document (odm:ODM rather than a default xmlns). The namespaces themselves are identical. Use to_xml_string() or write_xml() when you need odmlib’s exact output.

Returns:

a namespace-resolved ET.Element

Raises:

OdmlibNamespaceError – if no default namespace is registered

Return type:

Element

to_dict()[source]

transform odmlib object hierarchy into a Python dictionary and return it

Returns:

dictionary serialization of odmlib hierarchy

Return type:

dict

find(obj_name, attr, val)[source]

Return the first odmlib object where attribute attr equals val.

Searches within the child element(s) named obj_name on this element. For list elements (ODMListObject), returns the first match. For single elements (ODMObject), returns the element if it matches.

Parameters:
  • obj_name (str) – text name of the ODM Element (case sensitive)

  • attr (str) – text name of the ODM Element Attribute (case sensitive)

  • val (Any) – attribute value to search for

Returns:

first matching odmlib object, or None if not found

Return type:

ODMElement | None

find_all(obj_name, attr, val)[source]

Return all odmlib objects where attribute attr equals val.

Like find(), but returns all matches instead of just the first.

Parameters:
  • obj_name (str) – text name of the ODM Element (case sensitive)

  • attr (str) – text name of the ODM Element Attribute (case sensitive)

  • val (Any) – attribute value to search for

Returns:

list of matching odmlib objects (empty list if no matches)

Return type:

List[ODMElement]

find_by(obj_name, **kwargs)[source]

Return the first odmlib object matching all keyword criteria.

A more ergonomic alternative to find() for multi-attribute lookups.

Parameters:
  • obj_name (str) – text name of the ODM Element (case sensitive)

  • kwargs (Any) – attribute name=value pairs to match

Returns:

first matching odmlib object, or None

Return type:

ODMElement | None

write_xml(odm_file, odm_writer=<class 'odm_element.ODMWriter'>)[source]

write the odmlib hierarchy as an XML file

Parameters:
  • odm_file (str) – string ODM filename and path

  • odm_writer (type) – object used to write the elementree XML to a file

Return type:

None

write_json(odm_file)[source]

write the odmlib hierarchy as a JSON file

Parameters:

odm_file (str) – string ODM filename and path

Return type:

None

build_oid_index()[source]

Build an OID lookup index for this element and all descendants.

Traverses the entire object tree and creates an index mapping OID attribute values to their containing odmlib objects.

Returns:

Index supporting find_all(oid) lookups.

Return type:

OIDIndex

verify_oids(oid_checker)[source]

checks all the OIDs for uniqueness and Def/Ref integrity; oid_checker throws a ValueError on failure

Parameters:

oid_checker (Any) – object that performs that checks OID uniqueness and Def/Ref checks

Return type:

bool

unreferenced_oids(oid_checker)[source]

Find OID definitions that are not referenced anywhere.

Calls verify_oids() first if OIDs have not yet been verified.

Parameters:

oid_checker (Any) – An OIDRef instance (from rules/oid_ref.py) or a DynamicOIDRef instance.

Returns:

Mapping of oid_value -> ref_attr_name for each OID that is defined but never referenced. An empty dict means every definition is used.

Return type:

dict

Note

Pass a checker that has been through a collect-mode validate() (or an otherwise complete verify_oids()). After a fail-fast verify_oids() that raised, the checker is only half-populated and this method can report a misleading duplicate naming an OID that is not in fact duplicated.

verify_conformance(validator)[source]

uses validator object to check object for conformance with the model

Parameters:

validator (Any) – object that validates the odmlib object against the model

Return type:

Any

verify_order()[source]

Verify that child elements are in the model declaration order.

Recursively checks this element and all descendants — calling at the odmlib root covers the whole document. Works on any ODMElement subclass; not applicable to xml.etree.ElementTree.Element (load through a loader first if you only have an ElementTree).

Limitations:

  • Each class’s expected order comes from descriptors declared in its own class body. Custom extensions that subclass a model class must redeclare base children in schema order — otherwise inherited children are flagged as unexpected.

  • In permissive mode (SKIP_TYPE) unknown attributes can land in __dict__; they will trip this check until cleaned up.

  • Only the order of different child element kinds is checked. Order within a single list (e.g. multiple ItemRef entries) is not validated — ODM relies on OrderNumber attributes for that.

  • Completeness is not checked. Missing required children must be caught via verify_conformance() or validate().

Note

to_xml() / write_xml() already emit children in _elems order, so verify_order() is not required before XML serialization. It is still useful before to_dict() / to_json(), which walk __dict__ and therefore reflect instance assignment order.

Returns:

True if ordering is correct.

Return type:

bool

Raises:

OdmlibElementOrderError – If any element has children out of order. Use reorder_object() to fix automatically.

See also

validate() with collect_errors=True reports every misordered element in one pass instead of just the first.

reorder_object()[source]

Reorder this element’s children to match model declaration order.

Modifies __dict__ in-place. Issues an OdmlibWarning to indicate that reordering was needed.

Note

Recursion is not automatic — call on each element that needs reordering, or call verify_order() first to identify which elements need reordering.

Return type:

None

validate(collect_errors=False, oid_checker=None, conformance_checker=None, max_errors=None)[source]

Validate this element and all children.

Runs up to three layers: element order, OID uniqueness and ref/def integrity, and Cerberus conformance. In collect mode each layer enumerates every problem it can find rather than stopping at its first.

Parameters:
  • collect_errors – If True, accumulate every error each layer can find and return them as a list instead of raising on the first failure. Defaults to False (fail-fast, existing behaviour).

  • oid_checker – Optional OID checker — a DynamicOIDRef from create_oid_checker(), or a deprecated rules.oid_ref.OIDRef. Checkers that do not implement the collecting protocol (see is_collecting_checker()) still work, but contribute at most one error to the list.

  • conformance_checker – Optional MetadataSchema instance. In collect mode the bundled Cerberus result is expanded into one error per failing field; in fail-fast mode the single bundled OdmlibConformanceError is raised unchanged.

  • max_errors – Optional cap on collected errors. Collection stops the moment the cap is reached and a final OdmlibErrorLimitError is appended, so the list holds at most max_errors + 1 entries. None (the default) collects everything. Ignored when collect_errors is False. max_errors=0 checks nothing and returns just the limit marker.

Returns:

True (or raises on first error). If collect_errors=True: list of OdmlibError instances (an empty list means valid).

Return type:

If collect_errors=False

Note

OID checkers are stateful. Use one checker instance per document, or call reset() on a DynamicOIDRef between runs — otherwise the second pass reports every OID as a duplicate.

Only OdmlibError subclasses are collected. Any other exception (a bug in a custom checker, an AttributeError from a malformed permissively-loaded tree) propagates in both modes. Note that OdmlibTypeError is an OdmlibError and is therefore collected.

Example:

errors = odm.validate(collect_errors=True, oid_checker=checker,
                      max_errors=100)
for err in errors:
    print(err)

Changed in version 0.2.1: Each layer now reports every problem it finds. Previously collect_errors=True returned at most one error per layer (three in total), so len(errors) may be larger than before. Added max_errors.

namespace = 'odm'

odm_loader module

Concrete loaders for standard ODM XML and JSON documents.

This module provides:

  • JSONODMLoader – loads ODM-JSON files into the odmlib object model

  • DictODMLoader – alias for JSONODMLoader (loads from Python dicts)

  • XMLODMLoader – loads ODM-XML files into the odmlib object model

These loaders are typically used via the ODMLoader facade rather than directly.

class odm_loader.JSONODMLoader(model_package='odm_1_3_2')[source]

Bases: DocumentLoader

Loads ODM-JSON documents into the odmlib object model.

Recursively parses JSON dictionaries and instantiates odmlib model classes based on dictionary keys matching class names.

Parameters:

model_package (str) – Package name to load models from (default: “odm_1_3_2”). Options: “odm_1_3_2”, “odm_2_0”, “dataset_1_0_1”, “ct_1_1_1”.

load_document(odm_dict, key)[source]

Recursively load a dict into an odmlib object tree.

Instantiates the odmlib class named key with scalar attributes from odm_dict, then recurses into nested dicts (ODMObject) and lists of dicts (ODMListObject).

Parameters:
  • odm_dict (dict) – Dictionary containing the element’s data.

  • key (str) – Class name to instantiate from the model package.

Returns:

An odmlib element object populated from odm_dict.

Return type:

Any

create_document(filename)[source]

Parse a JSON file and store its contents internally.

Parameters:

filename (str) – Path to the ODM JSON file.

Returns:

The parsed JSON as a Python dictionary.

Return type:

dict

create_document_from_string(odm_string)[source]

Parse an ODM JSON string and store its contents internally.

Parameters:

odm_string (str) – JSON string containing the ODM document.

Returns:

The parsed JSON as a Python dictionary.

Return type:

dict

load_odm()[source]

Return the root ODM object loaded from the stored document.

Must be called after create_document() or create_document_from_string().

Returns:

An odmlib ODM root object.

Raises:

OdmlibLoaderStateError – If no document has been loaded yet.

Return type:

Any

load_metadataversion(idx=0)[source]

Return the MetaDataVersion at index idx from the stored document.

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Zero-based index of the MetaDataVersion (default: 0).

Returns:

A MetaDataVersion odmlib object.

Raises:

OdmlibLoaderStateError – If no document has been loaded yet.

Return type:

Any

load_study(idx=0)[source]

Return the Study at index idx from the stored document.

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Zero-based index of the Study (default: 0).

Returns:

A Study odmlib object.

Raises:

OdmlibLoaderStateError – If no document has been loaded yet.

Return type:

Any

class odm_loader.DictODMLoader(model_package='odm_1_3_2')[source]

Bases: JSONODMLoader

Loads odmlib objects from Python dictionaries (same as JSONODMLoader).

Parameters:

model_package (str)

class odm_loader.XMLODMLoader(model_package='odm_1_3_2', ns_uri=None, local_model=False, nsr=None)[source]

Bases: DocumentLoader

Loads ODM-XML documents into the odmlib object model.

Parses XML using ElementTree and dynamically instantiates odmlib model classes based on element tag names.

Parameters:
  • model_package (str) – Package name (default: "odm_1_3_2"). Options: "odm_1_3_2", "odm_2_0", "dataset_1_0_1", "ct_1_1_1".

  • ns_uri (Optional[str]) – ODM default namespace URI. When None (the default), the URI is derived from model_package (odm_1_3_2 -> .../v1.3, odm_2_0 -> .../v2.0; other packages fall back to .../v1.3). Passing an explicit value overrides the derived default and is preserved as-is, which is useful for custom extensions or non-standard schema variants.

  • local_model (bool) – If True, model_package is a full module path.

  • nsr (Optional[Any]) – Optional pre-configured NamespaceRegistry instance.

load_document(elem, *args)[source]

Recursively load an XML element into an odmlib object tree.

Strips the namespace URI from the tag name, instantiates the matching odmlib class, and recurses into child elements.

Parameters:
  • elem (ET.Element) – The XML element to load.

  • *args – Unused; present for interface compatibility.

Returns:

An odmlib element object populated from elem.

Return type:

Any

create_document(filename, namespace_registry=None)[source]

Parse an ODM XML file and store the parsed tree internally.

Parameters:
  • filename (str) – Path to the ODM XML file.

  • namespace_registry (Any | None) – Optional pre-configured NamespaceRegistry instance.

Returns:

The root XML element of the parsed document.

Return type:

ET.Element

create_document_from_string(odm_string, namespace_registry=None)[source]

Parse an ODM XML string and store the parsed tree internally.

Parameters:
  • odm_string (str) – XML string containing the ODM document.

  • namespace_registry (Any | None) – Optional pre-configured NamespaceRegistry instance.

Returns:

The root XML element of the parsed document.

Return type:

ET.Element

load_odm()[source]

Return the root ODM object loaded from the stored document.

Must be called after create_document() or create_document_from_string().

Returns:

An odmlib ODM root object.

Return type:

Any

load_metadataversion(idx=0)[source]

Return the MetaDataVersion at index idx from the stored document.

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Zero-based index of the MetaDataVersion (default: 0).

Returns:

A MetaDataVersion odmlib object.

Return type:

Any

load_study(idx=0)[source]

Return the Study at index idx from the stored document.

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Zero-based index of the Study (default: 0).

Returns:

A Study odmlib object.

Return type:

Any

define_loader module

Concrete loaders for Define-XML documents (XML and JSON formats).

This module provides:

These loaders handle Define-XML extensions including the def: namespace and additional elements like ValueListDef, WhereClauseDef, and CommentDef. They are typically used via the ODMLoader facade.

class define_loader.XMLDefineLoader(model_package='define_2_0', ns_uri=None, local_model=False)[source]

Bases: DocumentLoader

Loads Define-XML documents into the odmlib object model.

Handles Define-XML extensions including the def: namespace and additional elements like ValueListDef, WhereClauseDef, and CommentDef.

Parameters:
  • model_package (str) – Package name (default: "define_2_0"). Options: "define_2_0", "define_2_1".

  • ns_uri (Optional[str]) – Define-XML def: namespace URI. When None (the default), the URI is derived from model_package (define_2_0 -> .../v2.0, define_2_1 -> .../v2.1). Passing an explicit value overrides the derived default and is preserved as-is, which is useful for custom extensions or non-standard schema variants.

  • local_model (bool) – If True, model_package is a full module path.

load_document(elem, *args)[source]

Recursively load an XML element into an odmlib object tree.

Strips the namespace URI from the tag name, instantiates the matching odmlib class from the Define-XML model package, and recurses into child elements.

Parameters:
  • elem (ET.Element) – The XML element to load.

  • *args – Unused; present for interface compatibility.

Returns:

An odmlib element object populated from elem.

Return type:

Any

create_document(filename, namespace_registry=None)[source]

Parse a Define-XML file and store the parsed tree internally.

Parameters:
  • filename (str) – Path to the Define-XML file.

  • namespace_registry (Any | None) – Optional pre-configured NamespaceRegistry instance.

Returns:

The root XML element of the parsed document.

Return type:

ET.Element

create_document_from_string(odm_string, namespace_registry=None)[source]

Parse a Define-XML string and store the parsed tree internally.

Parameters:
  • odm_string (str) – XML string containing the Define-XML document.

  • namespace_registry (Any | None) – Optional pre-configured NamespaceRegistry instance.

Returns:

The root XML element of the parsed document.

Return type:

ET.Element

load_odm()[source]

Return the root ODM object loaded from the stored document.

Must be called after create_document() or create_document_from_string().

Returns:

An odmlib ODM root object.

Return type:

Any

load_metadataversion(idx=0)[source]

Return the MetaDataVersion at index idx from the stored document.

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Zero-based index of the MetaDataVersion (default: 0).

Returns:

A MetaDataVersion odmlib object.

Return type:

Any

load_study(idx=0)[source]

Return the Study from the stored document.

Must be called after create_document() or create_document_from_string().

Note

Define-XML documents contain a single Study element; the idx parameter is accepted for interface compatibility but always returns index 0.

Parameters:

idx (int) – Unused; always returns the first Study (default: 0).

Returns:

A Study odmlib object.

Return type:

Any

class define_loader.JSONDefineLoader(model_package='define_2_0')[source]

Bases: DocumentLoader

Loads Define-XML JSON documents into the odmlib object model.

Parameters:

model_package (str) – Package name (default: “define_2_0”). Options: “define_2_0”, “define_2_1”.

load_document(odm_dict, key)[source]

Recursively load a dict into an odmlib object tree.

Instantiates the odmlib class named key with scalar attributes from odm_dict, then recurses into nested dicts (ODMObject) and lists of dicts (ODMListObject).

Parameters:
  • odm_dict (dict) – Dictionary containing the element’s data.

  • key (str) – Class name to instantiate from the Define-XML model package.

Returns:

An odmlib element object populated from odm_dict.

Return type:

Any

create_document(filename)[source]

Parse a Define-XML JSON file and store its contents internally.

Parameters:

filename (str) – Path to the Define-XML JSON file.

Returns:

The parsed JSON as a Python dictionary.

Return type:

dict

create_document_from_string(odm_string)[source]

Parse a Define-XML JSON string and store its contents internally.

Parameters:

odm_string (str) – JSON string containing the Define-XML document.

Returns:

The parsed JSON as a Python dictionary.

Return type:

dict

load_odm()[source]

Return the root ODM object loaded from the stored document.

Must be called after create_document() or create_document_from_string().

Returns:

An odmlib ODM root object.

Raises:

OdmlibLoaderStateError – If no document has been loaded yet.

Return type:

Any

load_metadataversion(idx=0)[source]

Return the MetaDataVersion from the stored document.

Handles both flat Define-XML JSON (MetaDataVersion at root level) and nested formats (MetaDataVersion inside Study).

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Unused; present for interface compatibility.

Returns:

A MetaDataVersion odmlib object.

Raises:

OdmlibLoaderStateError – If no document has been loaded yet, or if MetaDataVersion is not found in the document.

Return type:

Any

load_study(idx=0)[source]

Return the Study from the stored document.

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Unused; present for interface compatibility.

Returns:

A Study odmlib object.

Raises:

OdmlibLoaderStateError – If no document has been loaded yet, or if Study is not found in the document.

Return type:

Any

arm_loader module

Concrete loaders for ARM (Analysis Results Metadata) documents (XML and JSON).

This module provides:

  • XMLArmLoader – loads ARM-extended Define-XML files into the odmlib object model

  • JSONArmLoader – loads ARM-extended Define-XML JSON files into the odmlib object model

ARM extends Define-XML 2.1 with elements for describing analysis results, displays, programming code, and analysis datasets. The loaders handle the ARM-specific elements and are typically used via the ODMLoader facade.

Example (XML):

import odmlib.arm_loader as AL
import odmlib.loader as LD

loader = LD.ODMLoader(AL.XMLArmLoader())
loader.open_odm_document("define-adam.xml")
odm = loader.root()

Example (JSON):

import odmlib.arm_loader as AL
import odmlib.loader as LD

loader = LD.ODMLoader(AL.JSONArmLoader())
loader.open_odm_document("define-adam.json")
odm = loader.root()
class arm_loader.XMLArmLoader(model_package='arm_1_0', ns_uri='http://www.cdisc.org/ns/arm/v1.0', local_model=False)[source]

Bases: DocumentLoader

Loads ARM-extended Define-XML documents into the odmlib object model.

Handles the arm: namespace and ARM-specific elements such as AnalysisResultDisplays, ResultDisplay, AnalysisResult, ProgrammingCode, and AnalysisDatasets.

Parameters:
  • model_package (str) – Package name (default: “arm_1_0”).

  • ns_uri (str) – ARM namespace URI (default: “http://www.cdisc.org/ns/arm/v1.0”).

  • local_model (bool) – If True, model_package is a full module path.

load_document(elem, *args)[source]

Recursively load an XML element into an odmlib object tree.

Strips the namespace URI from the tag name, instantiates the matching odmlib class from the ARM model package, and recurses into child elements.

Parameters:
  • elem (ET.Element) – The XML element to load.

  • *args – Unused; present for interface compatibility.

Returns:

An odmlib element object populated from elem.

Return type:

Any

create_document(filename, namespace_registry=None)[source]

Parse an ARM-extended Define-XML file and store the parsed tree.

Parameters:
  • filename (str) – Path to the ARM Define-XML file.

  • namespace_registry (Any | None) – Optional pre-configured NamespaceRegistry instance.

Returns:

The root XML element of the parsed document.

Return type:

ET.Element

create_document_from_string(odm_string, namespace_registry=None)[source]

Parse an ARM-extended Define-XML string and store the parsed tree.

Parameters:
  • odm_string (str) – XML string containing the ARM document.

  • namespace_registry (Any | None) – Optional pre-configured NamespaceRegistry instance.

Returns:

The root XML element of the parsed document.

Return type:

ET.Element

load_odm()[source]

Return the root ODM object loaded from the stored document.

Must be called after create_document() or create_document_from_string().

Returns:

An odmlib ODM root object.

Return type:

Any

load_metadataversion(idx=0)[source]

Return the MetaDataVersion at index idx from the stored document.

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Zero-based index of the MetaDataVersion (default: 0).

Returns:

A MetaDataVersion odmlib object.

Return type:

Any

load_study(idx=0)[source]

Return the Study from the stored document.

Must be called after create_document() or create_document_from_string().

Note

ARM documents (like Define-XML) contain a single Study element; the idx parameter is accepted for interface compatibility.

Parameters:

idx (int) – Zero-based index of the Study (default: 0).

Returns:

A Study odmlib object.

Return type:

Any

class arm_loader.JSONArmLoader(model_package='arm_1_0')[source]

Bases: DocumentLoader

Loads ARM-extended Define-XML JSON documents into the odmlib object model.

Handles ARM-specific elements such as AnalysisResultDisplays, ResultDisplay, AnalysisResult, ProgrammingCode, and AnalysisDatasets when loading from JSON format.

Parameters:

model_package (str) – Package name (default: “arm_1_0”).

load_document(odm_dict, key)[source]

Recursively load a dict into an odmlib object tree.

Instantiates the odmlib class named key with scalar attributes from odm_dict, then recurses into nested dicts (ODMObject) and lists of dicts (ODMListObject).

Parameters:
  • odm_dict (dict) – Dictionary containing the element’s data.

  • key (str) – Class name to instantiate from the ARM model package.

Returns:

An odmlib element object populated from odm_dict.

Return type:

Any

create_document(filename)[source]

Parse an ARM JSON file and store its contents internally.

Parameters:

filename (str) – Path to the ARM JSON file.

Returns:

The parsed JSON as a Python dictionary.

Return type:

dict

create_document_from_string(odm_string)[source]

Parse an ARM JSON string and store its contents internally.

Parameters:

odm_string (str) – JSON string containing the ARM document.

Returns:

The parsed JSON as a Python dictionary.

Return type:

dict

load_odm()[source]

Return the root ODM object loaded from the stored document.

Must be called after create_document() or create_document_from_string().

Returns:

An odmlib ODM root object.

Raises:

OdmlibLoaderStateError – If no document has been loaded yet.

Return type:

Any

load_metadataversion(idx=0)[source]

Return the MetaDataVersion from the stored document.

Handles both flat JSON (MetaDataVersion at root level) and nested formats (MetaDataVersion inside Study).

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Unused; present for interface compatibility.

Returns:

A MetaDataVersion odmlib object.

Raises:

OdmlibLoaderStateError – If no document has been loaded yet, or if MetaDataVersion is not found in the document.

Return type:

Any

load_study(idx=0)[source]

Return the Study from the stored document.

Must be called after create_document() or create_document_from_string().

Parameters:

idx (int) – Unused; present for interface compatibility.

Returns:

A Study odmlib object.

Raises:

OdmlibLoaderStateError – If no document has been loaded yet, or if Study is not found in the document.

Return type:

Any

odm_parser module

oid_generator module

Dynamic OID ref/def check generation from model introspection.

Inspects odmlib model class definitions to automatically derive:

  • Which classes are OID definitions (have an attribute named OID).

  • Which attributes are OID references (any attribute whose name contains "OID" but is not the definition attribute itself).

  • The mapping from reference attribute names to definition element classes.

  • The reverse mapping from definition element classes to reference attributes.

This replaces the manually-coded dictionaries in the rules/oid_ref.py files for each model package. The dynamic system is derived directly from the model classes, so it stays in sync automatically when the models evolve.

Usage:

from odmlib.oid_generator import create_oid_checker

checker = create_oid_checker("odm_1_3_2")
odm.verify_oids(checker)

Added in version 0.2.0.

oid_generator.discover_model_classes(model_module)[source]

Return all ODMElement subclasses found in model_module.

Parameters:

model_module (Any) – An imported odmlib model module, e.g. odmlib.odm_1_3_2.model.

Returns:

Mapping of class name → class object for every ODMElement subclass exposed at module level.

Return type:

dict[str, type]

oid_generator.discover_oid_definitions(model_classes)[source]

Find all model classes that define an OID.

A class is a “definition” element when it has an attribute literally named "OID" in its _attrs dict (regardless of descriptor type). This mirrors the runtime behaviour in _init_oid_check(), which registers any attr == "OID" as a definition.

Parameters:

model_classes (dict[str, type]) – Mapping returned by discover_model_classes().

Returns:

List of class names that have an OID attribute.

Return type:

list[str]

oid_generator.discover_oid_references(model_classes)[source]

Find all OID reference attributes across all model classes.

An attribute is an OID reference when its name contains the substring "OID" but is NOT itself named exactly "OID" (which would be a definition). This mirrors the runtime check elif "OID" in attr: oid_checker.add_oid_ref(obj, attr) in _init_oid_check().

Parameters:

model_classes (dict[str, type]) – Mapping returned by discover_model_classes().

Returns:

Mapping of ref_attr_name → [class_names …] listing every class that carries each reference attribute.

Return type:

dict[str, list[str]]

oid_generator.build_ref_def_mapping(ref_attrs, oid_defs, model_classes)[source]

Build the mapping from reference attribute names to definition classes.

Resolution order for each reference attribute:

  1. Check _REF_DEF_OVERRIDES; only apply the override if its target class exists in model_classes (so the same override dict works for all model packages).

  2. Strip the "OID" suffix from the attribute name and try <base>Def first, then <base> as the definition class name.

If neither step resolves to a known definition class, the attribute is not added to the mapping and will not be validated.

Parameters:
Returns:

Mapping of ref_attr_name → definition_class_name.

Return type:

dict[str, str]

oid_generator.build_def_ref_mapping(ref_def)[source]

Build the reverse mapping from definition classes to reference attributes.

This is used by DynamicOIDRef.check_unreferenced_oids() to find which reference attributes (if any) point to each definition class.

Parameters:

ref_def (dict[str, str]) – Mapping returned by build_ref_def_mapping().

Returns:

Mapping of definition_class_name → [ref_attr_names …].

Return type:

dict[str, list[str]]

class oid_generator.DynamicOIDRef(model_package, skip_attrs=None, skip_elems=None)[source]

Bases: ErrorReporting

Dynamically-generated OID ref/def checker.

Drop-in replacement for the manually-coded OIDRef classes in the rules/oid_ref.py files. Introspects the model module at instantiation time to build the ref/def mappings automatically.

Implements the same interface as the manual OIDRef classes so it can be passed directly to verify_oids().

Via the ErrorReporting mixin it also supports the collecting-checker protocol, so validate() with collect_errors=True reports every duplicate and every bad reference rather than only the first. The deprecated manual OIDRef classes do not, and contribute at most one error.

Note

A checker accumulates every OID it sees, so it is single-use per document. Call reset() before validating a second document (or re-validating the same one), otherwise every OID is reported as a duplicate.

Parameters:
  • model_package (str) – Model package name, e.g. "odm_1_3_2" or "define_2_1".

  • skip_attrs (Optional[list[str]]) – Attribute names to ignore during validation (e.g. ["FileOID", "PriorFileOID"]).

  • skip_elems (Optional[list[str]]) – Element class names to ignore when registering OID definitions (e.g. ["ODM"]).

Example:

from odmlib.oid_generator import create_oid_checker

checker = create_oid_checker("odm_1_3_2")
odm.verify_oids(checker)

Added in version 0.2.0.

add_oid(oid, element)[source]

Register an OID definition.

Called automatically during _init_oid_check() traversal.

Parameters:
  • oid (str) – The OID value to register.

  • element (str) – The class name of the element that owns this OID.

Raises:

OdmlibOIDError – If oid is already registered (duplicate OID). Inside a collecting() block the error goes to the sink instead and the traversal continues, so the reference checks that follow still run.

Return type:

None

Note

On a duplicate, the first definition is kept — that is what the error message asserts, and it keeps oid stable so the reference pass gives a deterministic verdict. A corner case worth knowing: if the first definition is a skip_elem type it lands only in unique_oids, so the reference pass then reports “not found” for everything pointing at it.

add_oid_ref(oid, attr)[source]

Register an OID reference.

Called automatically during _init_oid_check() traversal.

Parameters:
  • oid (str) – The referenced OID value.

  • attr (str) – The attribute name (e.g. "FormOID", "ItemOID").

Return type:

None

is_oids_verified()[source]

Return True if check_oid_refs() has been called.

Return type:

bool

check_oid_refs()[source]

Validate that all OID references point to valid definitions.

For each collected OID reference, checks:

  1. The referenced OID exists in the OID definitions.

  2. The definition element type matches what the reference attribute expects (per ref_def).

Returns:

True if all references are valid.

Raises:

OdmlibOIDError – On the first invalid or mismatched reference. Inside a collecting() block every bad reference is reported to the sink instead.

Return type:

bool

reset()[source]

Clear all state collected by a previous verification pass.

A checker accumulates every OID it sees, so reusing one instance for a second document — or a second validate() call on the same document — reports every OID as a duplicate. Call reset() between runs, or build a fresh checker with create_oid_checker().

Return type:

None

check_unreferenced_oids()[source]

Find OID definitions that are not referenced anywhere.

Returns:

Mapping of oid_value → ref_attr_name for each OID that is defined but never referenced. An empty dict means every definition is used.

Return type:

dict[str, str]

oid_generator.create_oid_checker(model_package, extra_skip_attrs=None, extra_skip_elems=None)[source]

Create a DynamicOIDRef checker for a model package.

This is the primary public API for creating OID checkers. It loads the per-model skip configuration from odmlib.oid_generator_config and merges any caller-provided extras before constructing the checker.

Parameters:
  • model_package (str) – Model package name, e.g. "odm_1_3_2", "define_2_1", "odm_2_0".

  • extra_skip_attrs (list[str] | None) – Additional attribute names to skip during validation (merged with model-level defaults).

  • extra_skip_elems (list[str] | None) – Additional element class names to skip (merged with model-level defaults).

Returns:

A configured DynamicOIDRef instance ready for use with verify_oids().

Return type:

DynamicOIDRef

Example:

from odmlib.oid_generator import create_oid_checker

# Replace manual OIDRef usage:
#   from odmlib.odm_1_3_2.rules.oid_ref import OIDRef
#   checker = OIDRef()
# With:
checker = create_oid_checker("odm_1_3_2")
odm.verify_oids(checker)

Added in version 0.2.0.

oid_generator_config module

Per-model skip configuration for dynamic OID ref/def generation.

Defines the skip_attr and skip_elem lists for each supported model package. These lists mirror the hard-coded skip behaviour in the legacy manual OIDRef classes:

  • skip_attr: OID-containing attribute names that should not be validated as OID references (e.g. FileOID, which is a top-level file identifier, not a reference to another element).

  • skip_elem: Element class names whose OID attributes should not be registered as definitions (e.g. ODM, whose FileOID is a file-level identifier rather than a metadata OID).

Usage (via odmlib.oid_generator.create_oid_checker()):

checker = create_oid_checker("define_2_1")
odm.verify_oids(checker)

Added in version 0.2.0.

oid_generator_config.get_skip_config(model_package)[source]

Return (skip_attrs, skip_elems) for model_package.

Falls back to empty lists for unknown packages so that a DynamicOIDRef can still be created for custom model packages.

Parameters:

model_package (str) – Model package name, e.g. "odm_1_3_2".

Returns:

A (skip_attrs, skip_elems) tuple of lists.

Return type:

tuple[list[str], list[str]]

oid_index module

class oid_index.OIDIndex[source]

Bases: object

add_oid(oid, element)[source]

odmlib expects all OIDs to be unique within the scope of an ODM document

find_all(oid)[source]

schema_manager module

schema_manager.get_schema_dir(standard, version)[source]

Return absolute path to the directory that contains the schema files.

This uses importlib.resources to resolve packaged data paths so that relative imports inside the XSD can be resolved by the schema processor.

Parameters:
  • standard (str)

  • version (str)

Return type:

str

schema_manager.get_schema_path(standard, version, filename=None)[source]

Return absolute path to the requested schema file.

If filename is None, use the main/root schema file for the standard/version.

Parameters:
  • standard (str)

  • version (str)

  • filename (str | None)

Return type:

str

typed module

Type-validated descriptors for ODM element attributes.

This module defines descriptor classes that enforce type validation on ODM element attributes at assignment time. Each descriptor validates the type, format, or enumerated values of the assigned value.

The descriptor hierarchy:
class typed.Typed(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Base descriptor that validates value type on assignment.

Subclasses set odm_type to the expected Python type. Raises OdmlibTypeError on type mismatch.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

odm_type

The expected Python type (default: object).

Type:

type

odm_type

alias of object

class typed.Integer(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for integer-valued ODM attributes.

Accepts int values and strings that convert to int (e.g., “42”). Used for OrderNumber, KeySequence, and similar integer attributes.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.Float(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for float-valued ODM attributes.

Accepts float, int, and string values that convert to float. Used for Rank attributes in CodeListItem and EnumeratedItem.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.String(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Typed

Descriptor for string-valued ODM attributes.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

odm_type

alias of str

class typed.OID(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Typed

Descriptor for OID (Object Identifier) attributes.

OIDs uniquely identify definition elements within a MetaDataVersion. Used on the OID attribute of *Def elements (e.g., ItemDef.OID).

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

odm_type

alias of str

class typed.OIDRef(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Typed

Descriptor for OID reference attributes.

References the OID of a definition element. Used on reference elements (e.g., ItemRef.ItemOID references ItemDef.OID).

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

odm_type

alias of str

class typed.Name(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Typed

Descriptor for the Name attribute of ODM elements.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

odm_type

alias of str

class typed.ID(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Typed

Descriptor for XML ID attributes.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

odm_type

alias of str

class typed.IDRef(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Typed

Descriptor for XML IDREF attributes.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

odm_type

alias of str

class typed.List(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Typed

Descriptor for list-valued attributes.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

odm_type

alias of list

class typed.Dictionary(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Typed

Descriptor for dictionary-valued attributes.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

odm_type

alias of dict

class typed.Positive(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Mixin descriptor that enforces value > 0.

Raises OdmlibTypeError if value is <= 0.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.NonNegative(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Mixin descriptor that enforces value >= 0.

Raises OdmlibTypeError if value is < 0.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.PositiveInteger(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Integer, Positive

Descriptor for positive integer attributes (value > 0).

Combines integer type validation with positive value constraint. Used for Length, ItemGroupDataSeq, and similar attributes.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.NonNegativeInteger(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Integer, NonNegative

Descriptor for non-negative integer attributes (value >= 0).

Combines integer type validation with non-negative constraint. Used for SignificantDigits and similar attributes.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.PositiveFloat(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Float, Positive

Descriptor for positive float attributes (value > 0).

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.NonNegativeFloat(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Float, NonNegative

Descriptor for non-negative float attributes (value >= 0).

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.Sized(*args, max_length, **kwargs)[source]

Bases: Descriptor

Mixin descriptor that enforces a maximum string length.

Parameters:

max_length (int) – Maximum allowed length of the string value.

class typed.SizedString(*args, max_length, **kwargs)[source]

Bases: String, Sized

String descriptor with a maximum length constraint.

class typed.Regex(*args, pat, **kwargs)[source]

Bases: Descriptor

Descriptor that validates a string against a regular expression pattern.

Parameters:

pat (str) – Regular expression pattern the value must match.

class typed.SizedRegexString(*args, max_length, **kwargs)[source]

Bases: SizedString, Regex

String descriptor with both size and regex constraints.

class typed.DateTimeString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for ISO 8601 datetime strings.

Validates format: YYYY-MM-DDTHH:MM:SS[.sss][Z|±HH:MM]

Used for CreationDateTime, AsOfDateTime, and similar attributes.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.PartialDateTimeString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for partial ISO 8601 datetime strings.

Allows partial date/time values where some components may be omitted.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.PartialDateString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for partial ISO 8601 date strings.

Accepts full YYYY-MM-DD, year-month YYYY-MM, or year-only YYYY.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.PartialTimeString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for partial ISO 8601 time strings.

Accepts full HH:MM:SS or partial time values.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.IncompleteDateTimeString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for incomplete ISO 8601 datetime strings.

Allows - as a placeholder for unknown date/time components.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.IncompleteDateString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for incomplete ISO 8601 date strings.

Allows - as a placeholder for unknown date components.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.IncompleteTimeString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for incomplete ISO 8601 time strings.

Allows - as a placeholder for unknown time components.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.DurationDateTimeString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for ISO 8601 duration strings.

Accepts the whole durationDatetime union the ODM 2.0 XSD defines: the week form (P2W, -P1W), the general ISO 8601 form (P3D, P1Y2M, PT1H30M, P1DT2H3M4.5S), and the empty tag. Used for timing window attributes in ODM 2.0.

Changed in version 0.2.1: Previously only [+-]P{n}W was accepted, rejecting XSD-valid values such as P3D and PT1H.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.DateString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for ISO 8601 date strings (YYYY-MM-DD).

Used for EffectiveDate in MetaDataVersionRef.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.SASName(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for SAS variable/dataset name attributes.

Validates that the value starts with a letter or underscore, contains only alphanumerics and underscores, and is at most 8 characters long. Used for SASFieldName, SASDatasetName, etc.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.SASFormat(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for SAS format name attributes.

Validates that the value starts with a letter, underscore, or $, contains only alphanumerics, underscores, and dots, and is at most 8 characters long. Used for SASFormatName.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.Email(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for email address attributes.

Validates using the validators.email function.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.Url(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for URL attributes.

Validates using the validators.url function.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.FileName(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor for file name attributes.

Validates using pathvalidate.is_valid_filename. Used for PdfFileName, PictureFileName, etc.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.ValidValues(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor that validates against a dynamically-loaded set of permitted values.

Looks up valid values from the ValueSet registry using the element class name and attribute name (e.g., ODM.FileType).

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.ExtendedValidValues(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: Descriptor

Descriptor that validates against an explicit list of permitted values.

Unlike ValidValues, the allowed values are provided at class definition time in the valid_values list argument.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.ValueSetString(name=None, required=False, element_class=None, valid_values=None, namespace='odm')[source]

Bases: ValidValues, String

String descriptor that validates against a dynamically-loaded set of permitted values.

Combines string type validation with value set membership checking. Used for attributes like FileType, Repeating, Mandatory, DataType, etc.

Parameters:
  • name (Optional[str])

  • required (bool)

  • element_class (Optional[type])

  • valid_values (Optional[list])

  • namespace (str)

class typed.ODMObject(*args, element_class, **kwargs)[source]

Bases: Descriptor

Descriptor for a single child ODM element.

Stores a reference to the element class and validates type on assignment. When accessed on an unset optional attribute, instantiates the element class with no arguments.

Parameters:
  • element_class (type) – The expected child element class.

  • namespace (str) – XML namespace prefix (default: “odm”).

class typed.ODMListObject(*args, element_class, **kwargs)[source]

Bases: ODMObject, list

Descriptor for a list of child ODM elements.

Validates that all items assigned to the list are instances of the expected element class. Stored as a plain list in the instance.

Parameters:
  • element_class (type) – The class that all list items must be.

  • namespace (str) – XML namespace prefix (default: “odm”).

valueset module

class valueset.ValueSetLoader[source]

Bases: object

Loads and caches valueset data from JSON file

classmethod load_valuesets()[source]

Load JSON once, cache in memory (lazy loading)

classmethod get_version_for_module(module_path, instance_class=None)[source]

Determine the valueset version for a module path.

Resolution order: 1. Exact match in the version map. 2. Substring marker match against the module path. 3. If instance_class is provided, walk its MRO and recurse on each

base class’s module — this lets local models that subclass a shipped odmlib model inherit the correct version.

  1. Fallback: odm_1_3_2 for backward compatibility.

class valueset.ValueSet[source]

Bases: object

Backward-compatible interface to valueset data.

Supports three entry types in valuesets.json: - list: A closed set of allowed string values (e.g., ["Yes", "No"]) - regex dict: A dict with _regex key and optional _description

(e.g., {"_regex": "^2\\.[01](\\.\\d+)?$", "_description": "..."}).

  • open dict: A dict with _values and _open: true for an extensible vocabulary — the XSD models these as a union of an enumeration with bare xs:string, so any value is permitted while the listed terms remain the documented ones (e.g., {"_values": ["Form", "Dataset"], "_open": true}).

Added in version 0.2.1: The open-dict form. ODM 2.0 uses extensible unions for ItemGroupDef/@Type, TrialPhase/@Value and Standard/@Status; enforcing them as closed lists rejected schema-valid documents.

UNKNOWN_ATTRIBUTE = <ValueSet.UNKNOWN_ATTRIBUTE>

Sentinel returned by value_set() for an unknown attribute key (see _UnknownAttribute). Exposed for callers/tests.

classmethod value_set(attribute, version=None, instance=None)[source]

Get the raw valueset entry for an attribute.

Parameters:
  • attribute – “ClassName.AttributeName” format

  • version – Explicit version (e.g., “odm_2_0”) - optional

  • instance – ODMElement instance for automatic version detection - optional

Returns:

A list of valid values, or a dict with _regex key for pattern-based validation. Returns the UNKNOWN_ATTRIBUTE sentinel if the attribute key is not registered for the resolved version or any fallback version.

Return type:

list or dict

Raises:
  • OdmlibValidationError – If the version is not found in the

  • valueset. An unknown attribute no longer raises — it returns –

:raises the UNKNOWN_ATTRIBUTE sentinel instead.:

classmethod validate(attribute, value, version=None, instance=None)[source]

Check whether a value is valid for the given attribute.

Parameters:
  • attribute – “ClassName.AttributeName” format

  • value – The string value to validate

  • version – Explicit version (e.g., “odm_2_0”) - optional

  • instance – ODMElement instance for automatic version detection - optional

Returns:

True if the value is valid, False otherwise.

Return type:

bool

classmethod describe(attribute, version=None, instance=None)[source]

Return a human-readable description of valid values for an attribute.

Parameters:
  • attribute – “ClassName.AttributeName” format

  • version – Explicit version (e.g., “odm_2_0”) - optional

  • instance – ODMElement instance for automatic version detection - optional

Returns:

Description suitable for error messages.

Return type:

str

mode module

Validation mode control for odmlib.

This module provides a context-managed permissive loading mode that allows non-conformant ODM/Define-XML files to be loaded into the object model for inspection and repair. The mode uses contextvars.ContextVar with a Flag enum for graduated control over which validations to bypass.

Quick start:

from odmlib import permissive

with permissive():
    loader.open_odm_document("broken.xml")
    odm = loader.root()

# Mode is back to strict — fix issues, then validate
errors = odm.validate(collect_errors=True)
class odmlib.mode.ValidationMode(*values)[source]

Bases: Flag

Flag enum controlling which validation categories are enforced.

Members can be combined with | to create targeted relaxation:

ValidationMode.SKIP_REQUIRED | ValidationMode.SKIP_VALUESET
STRICT = 0

All validation enforced (default, preserves existing behavior).

SKIP_REQUIRED = 1

Omit required-attribute checks during construction and access.

SKIP_VALUESET = 2

Omit ValidValues and ExtendedValidValues enforcement.

SKIP_TYPE = 4

Omit type checks and unknown-attribute rejection.

SKIP_FORMAT = 8

Omit format validators (datetime, SAS name, email, URL, etc.).

PERMISSIVE = 15

Composite flag that skips all validation categories.

odmlib.mode.get_mode()[source]

Return the current validation mode from the ContextVar.

Return type:

ValidationMode

odmlib.mode.set_mode(mode)[source]

Set the validation mode and return a reset token.

Parameters:

mode (ValidationMode) – The validation mode to activate.

Returns:

A contextvars.Token that can be passed to _validation_mode.reset() to restore the previous mode.

Return type:

Token

odmlib.mode.is_permissive(flag)[source]

Check whether a specific validation flag is active.

Parameters:

flag (ValidationMode) – The validation category to check (e.g., ValidationMode.SKIP_REQUIRED).

Returns:

True if the flag is active in the current mode.

Return type:

bool

odmlib.mode.permissive(mode=<ValidationMode.PERMISSIVE: 15>)[source]

Context manager that activates a permissive validation mode.

On entry, sets the validation mode to mode. On exit (including on exception), restores the previous mode via token reset.

Parameters:

mode (ValidationMode) – The validation mode to activate. Defaults to ValidationMode.PERMISSIVE (skip all validation).

Example:

from odmlib import permissive, ValidationMode

# Skip everything
with permissive():
    ...

# Skip only required checks
with permissive(ValidationMode.SKIP_REQUIRED):
    ...