Skip to content

Releases: swhume/odmlib

v0.2.0

Choose a tag to compare

@swhume swhume released this 23 Jun 23:49

[0.2.0]- 2026-06-23

Added

Context Manager write_on_exit Opt-Out

  • odmlib/context.py: added write_on_exit: bool = True parameter to
    ODMContext, DefineContext, open_odm, and open_define. Passing
    write_on_exit=False suppresses the auto-save on clean exit, enabling
    read-only inspection through the context managers without modifying or
    creating any file. The default (True) preserves the documented
    in-place save behaviour ? additive change, no compat impact.
  • tests/test_context_managers.py: 10 new tests covering the opt-out
    (XML, JSON, open_odm, open_define, and the input-preservation
    regression guard for the default-output-file footgun) plus explicit
    default-still-writes guards.

ODM v2.0 Model/XSD Alignment (safe subset)

  • odmlib/data/valuesets.json: added 12 missing odm_2_0 value-set keys
    (ReturnValue.DataType, ItemRef.Core/Repeat/Other/IsNonStandard/HasNoData,
    CodeListItem.Other, Telecom.TelecomType, ItemGroupDef.IsNonStandard/HasNoData,
    CodeList.IsNonStandard, ODM.Context) bound to the ODM 2.0
    ODM-enumerations.xsd value lists.
  • tests/test_odm_2_0_model.py: new construction + XML/JSON round-trip suite
    for the major ODM 2.0 classes.
  • tests/test_odm_2_0_known_gaps.py: new strict-xfail markers pinning the
    five deferred structural ODM 2.0 model/XSD gaps so CI documents the known
    state and fails loudly if a gap is silently fixed or regressed. (Its
    ItemDef test is now a passing regression guard ? see Changed below.)

Changed

  • ODM 2.0 TranslatedText.Type is now required (odmlib/odm_2_0/model.py),
    matching the XSD (use="required", free-text media type). ODMBuilder
    now defaults Type="text/plain" for the ODM 2.0 model shape only via a new
    _translated_text() helper.

  • odmlib/valueset.py: ValueSet.value_set() no longer raises for an
    unknown attribute ? it returns the new ValueSet.UNKNOWN_ATTRIBUTE
    sentinel so validate() returns False and the SKIP_VALUESET permissive
    guard can bypass an unregistered value set. Unknown version still raises.
    Behavioral note: in strict mode an unregistered value-set attribute now
    raises OdmlibTypeError (from ValidValues.__set__) instead of the former
    OdmlibValidationError (from value_set()).

  • ODM v2.0 ItemDef aligned with the ODM 2.0 XSD
    (odmlib/odm_2_0/model.py). Removed the XSD-rejected attributes
    FractionDigits, DatasetVarName, and SDSVarName; added the
    XSD-defined optional attributes DisplayFormat and VariableSet. Code
    that set the removed attributes on an odm_2_0 ItemDef should migrate ?
    those values were schema-invalid and are no longer serialized. (Closes
    the ROADMAP v0.2.1 ItemDef gap / ODM20-MODEL-XSD-DIFFERENCES_PLAN.md
    �3.7; the XSD ItemDef/ValueListRef child element remains deferred.)

Known Limitations

  • ODM v2.0 structural model/XSD gaps deferred to v0.2.1. Five features
    produce schema-invalid output if used under model_package="odm_2_0":
    ConditionDef (no required MethodSignature), text-based
    FormalExpression, Protocol.StudyEventRef (removed in the 2.0 schema),
    MetaDataVersion.StudyTiming placement, and StudyEventGroupDef (missing
    required child group). The affected ODMBuilder helpers carry docstring
    caveats. See ROADMAP "v0.2.1 ? ODM v2.0 Model/XSD Alignment" and
    ODM20-MODEL-XSD-DIFFERENCES_PLAN.md.

ODM v2.0 XSD Schema Validation

  • odmlib/schema_manager.py: registered ("odm", "2.0") ? "ODM.xsd"
    in _MAIN_SCHEMA. ODMSchemaValidator(standard="odm", version="2.0")
    now resolves the bundled odmlib/schemas/odm/2.0/ODM.xsd (target
    namespace http://www.cdisc.org/ns/odm/v2.0) and exposes the same
    validate_tree() / validate_file() API used for ODM 1.3.2 and
    Define-XML.
  • The v2.0 XSD set (ODM.xsd + ODM-foundation.xsd + 7 modular
    includes + xlink/xml/xhtml) was already shipping in
    odmlib/schemas/odm/2.0/ via the schemas/**/*.xsd package-data
    glob; the registry entry is the only missing wiring.
  • tests/test_schema_manager.py: 4 new tests verifying
    get_schema_dir("odm", "2.0"), get_schema_path("odm", "2.0"),
    the resolved filename (ODM.xsd), and the integration
    file-existence check.
  • tests/test_odm_validator.py: new TestODMv20Validator class
    validating tests/data/odmv2_example.xml and
    tests/data/cdash_demo_v20.xml end to end, plus a regression test
    that an ODM 1.3.2 document fails v2.0 validation. New
    test_explicit_odm_v20_works() in
    TestODMValidatorConstructorContract.
  • docs/source/guides/validation.rst: rewrote the "Schema Validation"
    section to document ODMSchemaValidator (the previous text
    referenced a nonexistent SchemaManager class).

Permissive Loading Mode

  • New odmlib/mode.py module with ValidationMode flag enum and
    permissive() context manager for loading non-conformant ODM documents
  • ValidationMode.STRICT (default) ? all validation enforced (existing
    behavior, unchanged)
  • ValidationMode.SKIP_REQUIRED ? omit required-attribute checks during
    construction and access
  • ValidationMode.SKIP_TYPE ? omit type checks (Typed, Integer, Float,
    ODMObject, ODMListObject, Positive, NonNegative, and unknown-attribute
    rejection)
  • ValidationMode.SKIP_FORMAT ? omit format validators (datetime, SAS
    name/format, email, URL, filename, regex, sized string)
  • ValidationMode.SKIP_VALUESET ? omit ValidValues and
    ExtendedValidValues enforcement
  • ValidationMode.PERMISSIVE ? composite flag that skips all validation
    categories
  • permissive() context manager with automatic cleanup via
    contextvars.ContextVar; supports graduated control via flag
    combinations
  • open_odm() and open_define() context managers accept
    permissive=True or a specific ValidationMode combination
  • ValidationMode, permissive, get_mode, set_mode exported from
    odmlib package root
  • New how-to guide: docs/source/guides/permissive_loading.rst
  • tests/test_permissive_mode.py ? 70 tests covering all validation
    categories, context manager safety, integration with loaders, and the
    load-fix-validate workflow

Error Reporting and Diagnostics

  • New odmlib/exceptions.py module with a structured exception hierarchy
  • OdmlibError ? base class for all odmlib exceptions
  • OdmlibValidationError ? replaces bare ValueError for validation failures; includes
    element_path, hint, attribute, element_type, and actual_value attributes
  • OdmlibRequiredAttributeError ? raised when a required attribute is missing at construction
  • OdmlibOIDError ? raised for OID uniqueness or ref/def integrity failures
  • OdmlibConformanceError ? raised when Cerberus conformance validation fails; exposes
    raw cerberus_errors dict for programmatic inspection
  • OdmlibElementOrderError ? raised when child elements violate ODM-spec ordering
  • OdmlibTypeError ? replaces bare TypeError for type/enum validation failures
  • OdmlibParsingError ? raised when an XML or JSON document cannot be parsed
  • OdmlibLoaderStateError ? raised when a loader method is called before the document is opened
  • OdmlibSerializationError ? raised when the model cannot be serialized to XML or JSON
  • OdmlibNamespaceError ? raised for namespace registration or lookup failures
  • OdmlibWarning, OdmlibDeprecationWarning, OdmlibInteroperabilityWarning ? warning hierarchy
  • ErrorCollector ? accumulates validation errors instead of raising on the first failure;
    has_errors, add_error(), add_warning(), raise_if_errors() API
  • ODMElement.validate() method ? unified validation entry point supporting both
    fail-fast (default) and collect-all-errors (collect_errors=True) modes
  • All exceptions and ErrorCollector exported from odmlib package root
  • tests/test_exceptions.py ? 47 tests covering hierarchy, formatting, backward compat, and model integration
  • tests/test_collect_errors.py ? 10 tests covering fail-fast and collect-all-errors validation modes

Dataset-JSON v1.1 ODMElement Model

  • New odmlib/dataset_json_1_1/ package ? spec-conformant Dataset-JSON v1.1 support using
    the ODMElement/descriptor pattern (one dataset per file, matching the v1.1 specification)
    • DatasetJSON ? root element with to_json(), from_json(), write_json(), read_json(),
      write_ndjson(), read_ndjson(), to_dict(), from_dict(), add_row(), add_column(),
      column_names property
    • Column ? column metadata with validated dataType and optional targetDataType,
      length, displayFormat, keySequence
    • SourceSystem ? optional nested source system metadata object
    • DatasetJSONElement base class disables XML serialization (to_xml() raises
      NotImplementedError) and handles mixed list types in to_dict()
  • New odmlib/dataset_json_1_1/define_flattener.py ? converts Define-XML v2.1 metadata into
    11 tabular Dataset-JSON datasets (study, standards, datasets, variables, value_level,
    where_clauses, methods, comments, documents, codelists, codelist_terms)
    • DefineFlattener.flatten_all() returns dict of dataset name ? DatasetJSON
    • DefineFlattener.write_all(output_dir) writes individual JSON files
    • O(1) ItemDef and WhereClauseDef lookups via index
    • _safe_get() utility for traversing deeply nested optional attributes
  • New odmlib/dataset_json_1_1/converter.py ? bidirectional Dataset-XML ? Dataset-JSON v1.1
    • dataset_xml_to_dataset_json(odm_obj) returns dict[str, DatasetJSON] (one per ItemGroupOID)
    • dataset_json_to_dataset_xml(dataset_json, model) converts back to Dataset-XML
  • Updated odmlib/dataframe.py ? Pandas integration for the new model
    • dataset_json_to_dataframe() ? DatasetJSON v1.1 ? DataFrame
    • `define...
Read more

v0.2.0rc1

v0.2.0rc1 Pre-release
Pre-release

Choose a tag to compare

@swhume swhume released this 20 May 22:14

[0.2.0] - 2026-05-20

An rc1 - testing release note. Install for soak with: pip install --pre "odmlib==0.2.0rc1".

Added

Context Manager write_on_exit Opt-Out

  • odmlib/context.py: added write_on_exit: bool = True parameter to
    ODMContext, DefineContext, open_odm, and open_define. Passing
    write_on_exit=False suppresses the auto-save on clean exit, enabling
    read-only inspection through the context managers without modifying or
    creating any file. The default (True) preserves the documented
    in-place save behaviour ? additive change, no compat impact.
  • tests/test_context_managers.py: 10 new tests covering the opt-out
    (XML, JSON, open_odm, open_define, and the input-preservation
    regression guard for the default-output-file footgun) plus explicit
    default-still-writes guards.

ODM v2.0 Model/XSD Alignment (safe subset)

  • odmlib/data/valuesets.json: added 12 missing odm_2_0 value-set keys
    (ReturnValue.DataType, ItemRef.Core/Repeat/Other/IsNonStandard/HasNoData,
    CodeListItem.Other, Telecom.TelecomType, ItemGroupDef.IsNonStandard/HasNoData,
    CodeList.IsNonStandard, ODM.Context) bound to the ODM 2.0
    ODM-enumerations.xsd value lists.
  • tests/test_odm_2_0_model.py: new construction + XML/JSON round-trip suite
    for the major ODM 2.0 classes.
  • tests/test_odm_2_0_known_gaps.py: new strict-xfail markers pinning the
    five deferred structural ODM 2.0 model/XSD gaps so CI documents the known
    state and fails loudly if a gap is silently fixed or regressed. (Its
    ItemDef test is now a passing regression guard ? see Changed below.)

Changed

  • ODM 2.0 TranslatedText.Type is now required (odmlib/odm_2_0/model.py),
    matching the XSD (use="required", free-text media type). ODMBuilder
    now defaults Type="text/plain" for the ODM 2.0 model shape only via a new
    _translated_text() helper.

  • odmlib/valueset.py: ValueSet.value_set() no longer raises for an
    unknown attribute ? it returns the new ValueSet.UNKNOWN_ATTRIBUTE
    sentinel so validate() returns False and the SKIP_VALUESET permissive
    guard can bypass an unregistered value set. Unknown version still raises.
    Behavioral note: in strict mode an unregistered value-set attribute now
    raises OdmlibTypeError (from ValidValues.__set__) instead of the former
    OdmlibValidationError (from value_set()).

  • ODM v2.0 ItemDef aligned with the ODM 2.0 XSD
    (odmlib/odm_2_0/model.py). Removed the XSD-rejected attributes
    FractionDigits, DatasetVarName, and SDSVarName; added the
    XSD-defined optional attributes DisplayFormat and VariableSet. Code
    that set the removed attributes on an odm_2_0 ItemDef should migrate ?
    those values were schema-invalid and are no longer serialized. (Closes
    the ROADMAP v0.2.1 ItemDef gap / ODM20-MODEL-XSD-DIFFERENCES_PLAN.md
    �3.7; the XSD ItemDef/ValueListRef child element remains deferred.)

Known Limitations

  • ODM v2.0 structural model/XSD gaps deferred to v0.2.1. Five features
    produce schema-invalid output if used under model_package="odm_2_0":
    ConditionDef (no required MethodSignature), text-based
    FormalExpression, Protocol.StudyEventRef (removed in the 2.0 schema),
    MetaDataVersion.StudyTiming placement, and StudyEventGroupDef (missing
    required child group). The affected ODMBuilder helpers carry docstring
    caveats. See ROADMAP "v0.2.1 ? ODM v2.0 Model/XSD Alignment" and
    ODM20-MODEL-XSD-DIFFERENCES_PLAN.md.

ODM v2.0 XSD Schema Validation

  • odmlib/schema_manager.py: registered ("odm", "2.0") ? "ODM.xsd"
    in _MAIN_SCHEMA. ODMSchemaValidator(standard="odm", version="2.0")
    now resolves the bundled odmlib/schemas/odm/2.0/ODM.xsd (target
    namespace http://www.cdisc.org/ns/odm/v2.0) and exposes the same
    validate_tree() / validate_file() API used for ODM 1.3.2 and
    Define-XML.
  • The v2.0 XSD set (ODM.xsd + ODM-foundation.xsd + 7 modular
    includes + xlink/xml/xhtml) was already shipping in
    odmlib/schemas/odm/2.0/ via the schemas/**/*.xsd package-data
    glob; the registry entry is the only missing wiring.
  • tests/test_schema_manager.py: 4 new tests verifying
    get_schema_dir("odm", "2.0"), get_schema_path("odm", "2.0"),
    the resolved filename (ODM.xsd), and the integration
    file-existence check.
  • tests/test_odm_validator.py: new TestODMv20Validator class
    validating tests/data/odmv2_example.xml and
    tests/data/cdash_demo_v20.xml end to end, plus a regression test
    that an ODM 1.3.2 document fails v2.0 validation. New
    test_explicit_odm_v20_works() in
    TestODMValidatorConstructorContract.
  • docs/source/guides/validation.rst: rewrote the "Schema Validation"
    section to document ODMSchemaValidator (the previous text
    referenced a nonexistent SchemaManager class).

Permissive Loading Mode

  • New odmlib/mode.py module with ValidationMode flag enum and
    permissive() context manager for loading non-conformant ODM documents
  • ValidationMode.STRICT (default) ? all validation enforced (existing
    behavior, unchanged)
  • ValidationMode.SKIP_REQUIRED ? omit required-attribute checks during
    construction and access
  • ValidationMode.SKIP_TYPE ? omit type checks (Typed, Integer, Float,
    ODMObject, ODMListObject, Positive, NonNegative, and unknown-attribute
    rejection)
  • ValidationMode.SKIP_FORMAT ? omit format validators (datetime, SAS
    name/format, email, URL, filename, regex, sized string)
  • ValidationMode.SKIP_VALUESET ? omit ValidValues and
    ExtendedValidValues enforcement
  • ValidationMode.PERMISSIVE ? composite flag that skips all validation
    categories
  • permissive() context manager with automatic cleanup via
    contextvars.ContextVar; supports graduated control via flag
    combinations
  • open_odm() and open_define() context managers accept
    permissive=True or a specific ValidationMode combination
  • ValidationMode, permissive, get_mode, set_mode exported from
    odmlib package root
  • New how-to guide: docs/source/guides/permissive_loading.rst
  • tests/test_permissive_mode.py ? 70 tests covering all validation
    categories, context manager safety, integration with loaders, and the
    load-fix-validate workflow

Error Reporting and Diagnostics

  • New odmlib/exceptions.py module with a structured exception hierarchy
  • OdmlibError ? base class for all odmlib exceptions
  • OdmlibValidationError ? replaces bare ValueError for validation failures; includes
    element_path, hint, attribute, element_type, and actual_value attributes
  • OdmlibRequiredAttributeError ? raised when a required attribute is missing at construction
  • OdmlibOIDError ? raised for OID uniqueness or ref/def integrity failures
  • OdmlibConformanceError ? raised when Cerberus conformance validation fails; exposes
    raw cerberus_errors dict for programmatic inspection
  • OdmlibElementOrderError ? raised when child elements violate ODM-spec ordering
  • OdmlibTypeError ? replaces bare TypeError for type/enum validation failures
  • OdmlibParsingError ? raised when an XML or JSON document cannot be parsed
  • OdmlibLoaderStateError ? raised when a loader method is called before the document is opened
  • OdmlibSerializationError ? raised when the model cannot be serialized to XML or JSON
  • OdmlibNamespaceError ? raised for namespace registration or lookup failures
  • OdmlibWarning, OdmlibDeprecationWarning, OdmlibInteroperabilityWarning ? warning hierarchy
  • ErrorCollector ? accumulates validation errors instead of raising on the first failure;
    has_errors, add_error(), add_warning(), raise_if_errors() API
  • ODMElement.validate() method ? unified validation entry point supporting both
    fail-fast (default) and collect-all-errors (collect_errors=True) modes
  • All exceptions and ErrorCollector exported from odmlib package root
  • tests/test_exceptions.py ? 47 tests covering hierarchy, formatting, backward compat, and model integration
  • tests/test_collect_errors.py ? 10 tests covering fail-fast and collect-all-errors validation modes

Dataset-JSON v1.1 ODMElement Model

  • New odmlib/dataset_json_1_1/ package ? spec-conformant Dataset-JSON v1.1 support using
    the ODMElement/descriptor pattern (one dataset per file, matching the v1.1 specification)
    • DatasetJSON ? root element with to_json(), from_json(), write_json(), read_json(),
      write_ndjson(), read_ndjson(), to_dict(), from_dict(), add_row(), add_column(),
      column_names property
    • Column ? column metadata with validated dataType and optional targetDataType,
      length, displayFormat, keySequence
    • SourceSystem ? optional nested source system metadata object
    • DatasetJSONElement base class disables XML serialization (to_xml() raises
      NotImplementedError) and handles mixed list types in to_dict()
  • New odmlib/dataset_json_1_1/define_flattener.py ? converts Define-XML v2.1 metadata into
    11 tabular Dataset-JSON datasets (study, standards, datasets, variables, value_level,
    where_clauses, methods, comments, documents, codelists, codelist_terms)
    • DefineFlattener.flatten_all() returns dict of dataset name ? DatasetJSON
    • DefineFlattener.write_all(output_dir) writes individual JSON files
    • O(1) ItemDef and WhereClauseDef lookups via index
    • _safe_get() utility for traversing deeply nested optional attributes
  • New odmlib/dataset_json_1_1/converter.py ? bidirectional Dataset-XML ? Dataset-JSON v1.1
    • dataset_xml_to_dataset_json(odm_obj) returns dict[str, DatasetJSON] (one per ItemGroupOID)
    • dataset_json_to_dataset_xml(dataset_json, model) converts back to Dataset-XML
  • Updated odmlib/dataframe.py ? Pandas integratio...
Read more

Release v0.1.4

Choose a tag to compare

@swhume swhume released this 02 Mar 03:03
edabaf1

Release v0.1.4: This code has been available for a bit, but v0.2.0, with many improvements, is coming soon. So, I'm tagging this release for those who may need to access it. Version 0.1.4 is also available via PyPI.