Migrating from pyxsd 0.1 to 1.0¶
pyxsd 1.0 is the first release in twenty years, ported to modern Python and rebuilt for the ecosystem of 2026. It is a new generation of the same idea: schema-compiled Python classes, a validated instance tree, a transform pipeline, and a zero-dependency runtime. The node shape, the CLI flags, and the XSD subset all survive; almost everything else around them was modernized.
This document covers what changed, why, and how to move your code.
Why the architecture changed¶
pyxsd 0.1 generated classes with an imperative type() factory and kept
class metadata (_elementNames_, _attributeNames_) in manually managed
class dictionaries. 1.0 keeps the same generated-class idea but uses the
modern machinery:
types.new_classreplaces thetype()factory, so generated classes behave like ordinary classes underinspect,copy, and pickle probes.__set_name__on theElement/Attributedescriptors wires each descriptor to its owner and attribute name at class creation — no manual bookkeeping lists to keep in sync.__init_subclass__onSchemaBasecollects the element/attribute name lists automatically through the MRO.Descriptive
__getattr__on generated classes turns misspelled attribute access into an error message that names your class and lists its declared elements and attributes.ValidationReportreplaces scatteredprintstatements: all non-fatal issues are structured objects with severity, stable code, message, and optional element name, available atPyXSD.reportand to any generated class through the parser back-reference.loggingreplaces prints for progress/trace output. The library never configures handlers; the CLI maps-v/-qto DEBUG/CRITICAL.pathlib.Path,argparse,abc.ABC, context managers, and lazyimportlibloading replace the Python-2 era equivalents (imp,optparse, ad-hoc file handling,sys.pathhacks).Schema composition (
include/import/redefine) is implemented by splicing the composed documents into the main schema tree before class generation, and identity constraints (key/unique/keyref) run as a post-parse validation pass with an XPath subset.
Breaking changes¶
Python and packaging¶
0.1 |
1.0 |
|---|---|
Python 2.3+, separate ElementTree/cElementTree required |
Python 3.11+ only, standard library only (zero runtime dependencies) |
|
|
|
|
Modules and imports¶
0.1 |
1.0 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
moved to |
|
|
|
|
All internal module names are now snake_case and importable as such; every
layout change was a git mv, so history follows the files.
Crystallography transforms¶
The eight application transforms (CellSizer, SphereCutter,
ExpandCell, BravaisLattice, CoordViewer, FormatForVisit, plus the
Atom/Vector helper libraries) moved from the package to
examples/legacy/. Importing pyxsd.transforms.cellSizer (or any
snake_case variant) now raises ImportError. The installed package keeps
the framework (Transform, Displayer, iter_tree) and the generic
built-ins (PrintData, SendTreeToPyXSD). See
examples/legacy/README.md for how to run them.
CLI¶
Same flag set (
-i,-s,-p,-k,-o,-d,-t,-T,-c,-v,-q), plus new--strict.Transform calls must be calls with parentheses (
-t 'PrintData()'); a bare class name is now a usage error. Calls are parsed withast.literal_eval— only literal arguments are accepted (0.1 usedeval).-tand-Tare mutually exclusive;-vand-qare mutually exclusive (both exit 2).stdin input is read directly (no
stdin.xmltemp file).Exit codes are meaningful:
0success,1fatal error or--strictwith errors,2usage error. Validation issues go to stderr as a rendered report.Default output filenames changed where 0.1 was buggy: the transformed output default is now derived from the input name (0.1 silently reused the input file name); nameless/file-like inputs write
output.xmlin the current directory.
Data types¶
Class names use the true XSD spelling: the class for
xs:stringisString, but its.nameattribute is"string"(0.1 used"String");Doubleno longer reports itself as"Float", and the misspelled"NonPostive"is now"NonPositiveInteger".Lexical validation happens in
__new__and raisesTypeErroron invalid input — previously many invalid values slipped through or crashed with exotic errors.Booleanhas avalattribute,str()→"true"/"false",repr()→"True"/"False"(0.1 raisedNameErroron repr).Empty simple content (
<x/>) yields""— not"True"(a latent 0.1 bug wheredataTypeVal = Truewas stringified).
Instances and descriptors¶
Typed primitive values are stored as
_value_ = [<one string>]— a list contract, consistently (0.1 sometimes stored a bare string, which the writers then char-split).Element-level assignment now works and validates:
item.quantity = Integer(4)stores the value; in 0.1 the descriptor silently discarded it.Attributes remain lexical strings in
_attribs_(so round-trip output is faithful); typed attribute values fromdefault/fixedapplication land in the instance__dict__.Class-level access to element descriptors returns the descriptor (0.1 crashed); instance-level assignment is type-checked.
The quirk where an element named
nameshadows the generated class’s metadatanameis preserved; prefer__name__for the class name.
Errors and reporting¶
printstatements →logging(library default WARNING) plus theValidationReport. Codes are stable strings — see Validation.Schema problems raise or record
PyXSDError(0.1 mixedValueErrorand raw stringraisestatements).Instance validation is more complete and more correct: attribute defaults/prohibitions/fixed, element defaults/fixed/nil, abstract checks, substitution groups,
xsi:type, and identity constraints all report through codes that did not exist in 0.1.The double-processing bug is fixed: 0.1 constructed every grandchild element twice (each child processed by both its parent’s loop and the factory), causing spurious duplicate children and bogus “Order Error”s on inline
complexTypeschemas.
Upgrade guide¶
Command-line users¶
Your existing invocations mostly work as-is. Check:
Transform calls need parentheses:
PrintData()notPrintData.Crystallography transforms must be run from
examples/legacy/(or copied next to your data).Add
--strictwhere a CI pipeline needs a failure signal.Move your log parsing from “grep stdout” to the rendered report on stderr (or the JSON/tuple API on
PyXSD.report).
Library users¶
# 0.1
from pyxsd.pyXSD import PyXSD
parser = PyXSD(
xmlFileInput="data.xml",
xsdFile="data.xsd",
xmlFileOutput="_No_Output_",
transformOutputName="_No_Output_",
)
# ... hope the prints were useful
# 1.0
from pyxsd import PyXSD
parser = PyXSD(xmlFileInput="data.xml", xsdFile="data.xsd", xmlFileOutput=False)
if parser.report.has_errors:
for issue in parser.report.issues:
print(issue.format())
root = parser.schemaRootInstance
Notes:
Construction runs the whole pipeline (parse → validate → write → transforms).
PyXSD.reportholds every issue found.xmlFileOutput=False(or"_No_Output_") suppresses the parsed-tree write; a filename orTrue(default name) writes it.transformOutputNameaccepts a filename or"stdout"; withtransformspresent, output is always written somewhere.Import paths follow the module renames table above; writer APIs (
XmlTreeWriter,XmlTagWriter) are unchanged in behavior.
History¶
The 0.1 codebase’s story — ORNL 2006, the mentors, the design intent — is
preserved in Program origins. Every structural move in the 1.0
modernization was done with git mv so that history remains traceable
through the repository itself.