@xmlify
@dataclass
class Config:
date: str
number_of_cores: int
codes: list[int]
show_logs: bool
write_xsd(THIS_DIR / "config.xsd", Config)
write_xml_template(THIS_DIR / "config_xml_template.xml", Config)
original = Config(
date="31/02/2023",
number_of_cores=48,
codes=[101, 345, 42, 67],
show_logs=False,
)
write_xml_value(THIS_DIR / "config_xml_example.xml", original)
read_config: Config = parse_file(Config, THIS_DIR / "config_xml_example.xml")
assert read_config == original
See more in examples
Currently supports the types:
int, float, str, dict, tuple, set, list, None
# as well as unions!
int | float | None
And dataclasses that have been @xmlify
-ed.
These can be combined for types such as:
@xmlify
@dataclass
class Complex:
a: dict[tuple[int, str], list[tuple[dict[int, float | str], set[bool]]]]
c1 = Complex(
a={(3, "hello"): [({3: 0.4}, {True, False}), ({2: "str"}, {False})]}
)
The xmlify interface can be implemented by adding methods described in xmlify
Once the class is_xmlified
it can be used just as if generated by @xmlify
from xmlable._xobject import XObject
from xmlable._user import IXmlify
from xmlable._manual import manual_xmlify
@manual_xmlify
class MyClass(IXmlify):
def get_xobject() -> XObject:
class XMyClass(XObject):
def xsd_out(self, name: str, attribs: dict[str, str] = {}, add_ns: dict[str, str] = {}) -> _Element:
pass
def xml_temp(self, name: str) -> _Element:
pass
def xml_out(self, name: str, val: Any, ctx: XErrorCtx) -> _Element:
pass
def xml_in(self, obj: ObjectifiedElement, ctx: XErrorCtx) -> Any:
pass
return XMyClass() # must be an instance of XMyClass, not the class
def xsd_forward(add_ns: dict[str, str]) -> _Element:
pass
def xsd_dependencies() -> set[type]:
return {MyClass}
See the user define example for implementation.
Generating xsd works, parsing works, however generating an xml template can fail if they type is not determinable at runtime.
- Values do not have type arguments carried with them
- Many types are indistinguishable in python
For example:
@xmlify
@dataclass
class GenericUnion:
u: dict[int, float] | dict[int, str]
GenericUnion(u={}) # which variant in the xml should {} have??named_
In this case an error is raised
git clone # this project
# Can use hatch to build, run
hatch run check:test # run tests/
hatch run check:lint # check formatting
hatch run check:typecheck # mypy for src/ and all examples
hatch run auto:examplegen # regenerate the example code
hatch run auto:lint # format code
# Alternatively can just create a normal env
python3.11 -m venv .venv
source .venv/bin/activate # activate virtual environment
pip install -e . # install this project in the venv
pip install -e .[dev] # install optional dev dependencies (mypy, black and pytest)
black . # to reformat
mypy # type check
pytest # to run tests
Hatch is used for build, test and pypi publish.
(As a fun weekend project) generate arbitrary python data types with values, and dataclasses.
Then @xmlify
all and validate as in the current tests
Currently using objectify for parsing and etree for construction, I want to move parsing to use etree
- previously used objectify for quick prototype.