Permissive Loading Mode
Introduction
odmlib validates attribute values at assignment time, which means that non-conformant ODM or Define-XML files cannot normally be loaded into the object model. The permissive loading mode solves this by selectively bypassing validation during loading, allowing you to inspect, repair, and re-validate documents that contain invalid content.
Quick Start
from odmlib import permissive
import odmlib.loader as LD
import odmlib.odm_loader as OL
loader = LD.ODMLoader(OL.XMLODMLoader())
with permissive():
loader.open_odm_document("incomplete_define.xml")
odm = loader.root()
# odm is now loaded -- mode is back to strict
Graduated Control
The permissive() context manager accepts a ValidationMode flag
that controls exactly which validation categories to bypass:
from odmlib import permissive, ValidationMode
# Skip only required-attribute checks
with permissive(ValidationMode.SKIP_REQUIRED):
...
# Skip required + valueset (but still enforce types and formats)
with permissive(ValidationMode.SKIP_REQUIRED | ValidationMode.SKIP_VALUESET):
...
# Skip everything (the default)
with permissive(): # defaults to ValidationMode.PERMISSIVE
...
Flag |
What it bypasses |
Descriptor classes affected |
|---|---|---|
|
Required-attribute checks at construction and access |
|
|
Type checks and unknown-attribute rejection |
|
|
Format validators |
|
|
Enumerated value set enforcement |
|
|
All of the above (composite flag) |
All descriptor classes |
The Load – Inspect – Fix – Validate Workflow
The recommended workflow for handling non-conformant files:
from odmlib import permissive
import odmlib.loader as LD
import odmlib.odm_loader as OL
# Step 1: Load permissively
loader = LD.ODMLoader(OL.XMLODMLoader())
with permissive():
loader.open_odm_document("broken_define.xml")
odm = loader.root()
# Step 2: Inspect -- mode is strict again, but objects are populated
mdv = odm.Study[0].MetaDataVersion[0]
for item in mdv.ItemDef:
print(f"OID={item.OID}, DataType={item.DataType}")
# Step 3: Fix issues (assignments are validated in strict mode)
for item in mdv.ItemDef:
if item.DataType not in ["text", "integer", "float"]:
item.DataType = "text"
# Step 4: Validate (max_errors caps a badly broken document)
errors = odm.validate(collect_errors=True, max_errors=100)
if not errors:
odm.write_xml("fixed_define.xml")
else:
for err in errors:
print(err)
Context Manager Integration
The open_odm() and open_define() context managers accept a
permissive parameter:
from odmlib.context import open_odm
from odmlib import ValidationMode
with open_odm("broken.xml", output_file="fixed.xml",
permissive=True) as odm:
# Fix issues here
...
# Saves on clean exit -- but note: save runs in strict mode
# Targeted relaxation
with open_odm("broken.xml", output_file="fixed.xml",
permissive=ValidationMode.SKIP_REQUIRED) as odm:
...
API Reference
- class odmlib.mode.ValidationMode(*values)[source]
Bases:
FlagFlag 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
ValidValuesandExtendedValidValuesenforcement.
- 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.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): ...
- odmlib.mode.get_mode()[source]
Return the current validation mode from the ContextVar.
- Return type:
- 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.Tokenthat can be passed to_validation_mode.reset()to restore the previous mode.- Return type:
- 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:
Trueif the flag is active in the current mode.- Return type:
Safety Notes
Permissively loaded objects may contain invalid data that does not conform to the ODM specification.
Always call
validate(collect_errors=True)before serializing for production use. Every layer reports all the problems it finds, so a badly broken document can yield a long list — passmax_errorsto cap it and work through the issues in batches.The permissive mode affects only the current context (thread or coroutine); other threads and coroutines remain in strict mode.
When using
open_odm(permissive=True), the auto-save on exit runs in strict mode. If the object tree has unfixed issues that prevent serialization, the save may fail. Callwrite_xml()orwrite_json()inside thewithblock (while permissive mode is still active) if you need to save an un-fixed document.For read-only inspection — for example, loading a known-broken document to enumerate its issues without risking a strict-mode save failure overwriting the input — pass
write_on_exit=False. No file is created or modified when thewithblock exits, regardless ofoutput_file.with open_odm("broken.xml", permissive=True, write_on_exit=False) as odm: errors = odm.validate(collect_errors=True) for err in errors: print(err) # broken.xml is untouched on exit