diff --git a/convert.py b/convert.py deleted file mode 100644 index 3dac8949..00000000 --- a/convert.py +++ /dev/null @@ -1,18 +0,0 @@ -from pathlib import Path - -import ome_types2 -from xsdata.formats.dataclass.parsers.config import ParserConfig - - -def factory(clazz, params): - return params - # return clazz(**params) - - -# from xsdata.formats.dataclass.parsers.nodes.element -source = Path(__file__).parent / "tests" / "data" / "example.ome.xml" -result = ome_types2.from_xml( - source, parser_kwargs={"config": ParserConfig(class_factory=factory)} -) - -x =1 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3e23bd8e..d0d4ce2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,6 +117,7 @@ ignore = [ "C901", # Function is too complex "RUF009", # Do not perform function calls in default arguments ] +exclude = ['src/_ome_autogen.py'] [tool.ruff.per-file-ignores] "tests/*.py" = ["D", "S"] diff --git a/src/ome_autogen/_class_type.py b/src/ome_autogen/_class_type.py index bd9b4819..f02823dd 100644 --- a/src/ome_autogen/_class_type.py +++ b/src/ome_autogen/_class_type.py @@ -12,6 +12,3 @@ def is_model(self, obj: Any) -> bool: return True return False - - - diff --git a/src/ome_autogen/_config.py b/src/ome_autogen/_config.py index a791c796..ace4ccc4 100644 --- a/src/ome_autogen/_config.py +++ b/src/ome_autogen/_config.py @@ -68,7 +68,8 @@ def get_config( # 'Experimenters', 'ExperimenterGroups', 'Instruments', 'Images', # 'StructuredAnnotations', 'ROIs', 'BinaryOnly'] # Pixels ['BinDataBlocks', 'TiffDataBlocks', 'MetadataOnly'] -# Instrument ['GenericExcitationSource', 'LightEmittingDiode', 'Filament', 'Arc', 'Laser'] +# Instrument ['GenericExcitationSource', 'LightEmittingDiode', 'Filament', 'Arc', +# 'Laser'] # BinaryFile ['External', 'BinData'] # StructuredAnnotations ['XMLAnnotation', 'FileAnnotation', 'ListAnnotation', # 'LongAnnotation', 'DoubleAnnotation', 'CommentAnnotation', diff --git a/src/ome_autogen/_util.py b/src/ome_autogen/_util.py index e7466f36..d3591776 100644 --- a/src/ome_autogen/_util.py +++ b/src/ome_autogen/_util.py @@ -38,7 +38,7 @@ def get_plural_names(schema: Path | str = SCHEMA_FILE) -> dict[str, str]: camel_snake_registry: dict[str, str] = {} -def camel_to_snake(name: str, **kwargs) -> str: +def camel_to_snake(name: str) -> str: name = name.lstrip("@") # remove leading @ from "@any_element" result = CAMEL_SNAKE_OVERRIDES.get(name) if not result: diff --git a/src/ome_types/_convenience.py b/src/ome_types/_convenience.py index 7a981a79..90abc1ec 100644 --- a/src/ome_types/_convenience.py +++ b/src/ome_types/_convenience.py @@ -1,10 +1,8 @@ import os from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, Optional, Union, cast +from typing import TYPE_CHECKING, Any, Dict, Optional, Protocol, Union, cast from warnings import warn -from typing_extensions import Protocol - from .model import OME if TYPE_CHECKING: diff --git a/src/ome_types/util.py b/src/ome_types/util.py index 7b418480..d0984e89 100644 --- a/src/ome_types/util.py +++ b/src/ome_types/util.py @@ -74,13 +74,13 @@ def collect_ids(value: Any) -> dict[LSID, OMEType]: CAMEL_REGEX = re.compile(r"(? str: """Return a snake_case version of a camelCase string.""" return model._camel_to_snake.get(name, CAMEL_REGEX.sub("_", name).lower()) -@lru_cache() +@lru_cache def norm_key(key: str) -> str: """Return a normalized key.""" return key.split("}")[-1] diff --git a/src/ome_types2/__init__.py b/src/ome_types2/__init__.py index ea98408b..90f5a2d0 100644 --- a/src/ome_types2/__init__.py +++ b/src/ome_types2/__init__.py @@ -5,7 +5,7 @@ except PackageNotFoundError: __version__ = "unknown" -from ome_types2._conversion import from_tiff, from_xml, to_dict +from ome_types2._conversion import from_tiff, from_xml, to_dict, to_xml from ome_types2.model import OME -__all__ = ["__version__", "OME", "from_xml", "from_tiff", "to_dict"] +__all__ = ["__version__", "OME", "from_xml", "from_tiff", "to_dict", "to_xml"] diff --git a/src/ome_types2/_conversion.py b/src/ome_types2/_conversion.py index 91ac7afb..b6e852ca 100644 --- a/src/ome_types2/_conversion.py +++ b/src/ome_types2/_conversion.py @@ -8,7 +8,8 @@ from xml.etree import ElementTree as ET from xsdata.formats.dataclass.parsers.config import ParserConfig -from xsdata_pydantic_basemodel.bindings import XmlParser +from xsdata.formats.dataclass.serializers.config import SerializerConfig +from xsdata_pydantic_basemodel.bindings import XmlParser, XmlSerializer if TYPE_CHECKING: from typing import TypedDict @@ -24,7 +25,8 @@ class ParserKwargs(TypedDict, total=False): handler: type[XmlHandler] -OME_2016_06 = r"{http://www.openmicroscopy.org/Schemas/OME/2016-06}OME" +OME_2016_06_URI = "http://www.openmicroscopy.org/Schemas/OME/2016-06" +OME_2016_06_NS = f"{{{OME_2016_06_URI}}}OME" def _get_ome(xml: str | bytes) -> type[OME]: @@ -33,7 +35,7 @@ def _get_ome(xml: str | bytes) -> type[OME]: else: root = ET.fromstring(xml) # noqa: S314 - if root.tag == OME_2016_06: + if root.tag == OME_2016_06_NS: from ome_types2.model import OME return OME @@ -70,6 +72,24 @@ def from_xml( return parser.parse(xml, OME_type) +def to_xml( + ome: OME, + ignore_defaults: bool = True, + indent: int = 2, + include_schema_location: bool = True, +) -> str: + config = SerializerConfig( + pretty_print=indent > 0, + pretty_print_indent=" " * indent, + ignore_default_attributes=ignore_defaults, + ) + if include_schema_location: + config.schema_location = f"{OME_2016_06_URI} {OME_2016_06_URI}/ome.xsd" + + serializer = XmlSerializer(config=config) + return serializer.render(ome, ns_map={None: OME_2016_06_URI}) + + def from_tiff( path: Path | str, *, diff --git a/src/ome_types2/_mixins/_base_type.py b/src/ome_types2/_mixins/_base_type.py index 14303ec9..0c4921aa 100644 --- a/src/ome_types2/_mixins/_base_type.py +++ b/src/ome_types2/_mixins/_base_type.py @@ -1,9 +1,8 @@ import contextlib -import weakref from datetime import datetime from enum import Enum from textwrap import indent -from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Sequence, Set +from typing import TYPE_CHECKING, Any, ClassVar, Optional, Sequence, Set, Type, cast from pydantic import BaseModel, validator @@ -18,22 +17,14 @@ def __init__(self, name: str) -> None: self.name = name def __repr__(self) -> str: - return f"{__name__}.{self.name}.{id(self)}" + return f"{__name__}.{self.name}.{id(self):x}" -def quantity_property(field: str) -> property: - """Create property that returns a ``pint.Quantity`` combining value and unit.""" - - def quantity(self: Any) -> Optional["pint.Quantity"]: - from ome_types._units import ureg - - value = getattr(self, field) - if value is None: - return None - unit = getattr(self, f"{field}_unit").value.replace(" ", "_") - return ureg.Quantity(value, unit) - - return property(quantity) +# Default value to support automatic numbering for id field values. +_AUTO_SEQUENCE = Sentinel("AUTO_SEQUENCE") +_COUNTERS: dict[Type["OMEType"], int] = {} +_UNIT_FIELD = "{}_unit" +_QUANTITY_FIELD = "{}_quantity" class OMEType(BaseModel): @@ -47,35 +38,31 @@ class OMEType(BaseModel): support. """ - # Default value to support automatic numbering for id field values. - _AUTO_SEQUENCE = Sentinel("AUTO_SEQUENCE") + # pydantic BaseModel configuration. + # see: https://pydantic-docs.helpmanual.io/usage/model_config/ + class Config: + arbitrary_types_allowed = False + validate_assignment = True + underscore_attrs_are_private = True + use_enum_values = False + validate_all = True + # allow use with weakref __slots__: ClassVar[Set[str]] = {"__weakref__"} # type: ignore def __init__(__pydantic_self__, **data: Any) -> None: if "id" in __pydantic_self__.__fields__: - data.setdefault("id", OMEType._AUTO_SEQUENCE) + data.setdefault("id", _AUTO_SEQUENCE) super().__init__(**data) def __init_subclass__(cls) -> None: - """Add some properties to subclasses with units. + """Add `*_quantity` property for fields that have both a value and a unit. - It adds ``*_quantity`` property for fields that have both a value and a - unit, where ``*_quantity`` is a pint ``Quantity`` + where `*_quantity` is a pint `Quantity`. """ - _clsdir = set(cls.__fields__) - for field in _clsdir: - if f"{field}_unit" in _clsdir: - setattr(cls, f"{field}_quantity", quantity_property(field)) - - # pydantic BaseModel configuration. - # see: https://pydantic-docs.helpmanual.io/usage/model_config/ - class Config: - arbitrary_types_allowed = False - validate_assignment = True - underscore_attrs_are_private = True - use_enum_values = False - validate_all = True + for field in cls.__fields__: + if _UNIT_FIELD.format(field) in cls.__fields__: + setattr(cls, _QUANTITY_FIELD.format(field), _quantity_property(field)) def __repr__(self) -> str: name = self.__class__.__qualname__ @@ -107,43 +94,49 @@ def __repr__(self) -> str: return f"{name}({body})" @validator("id", pre=True, always=True, check_fields=False) - def validate_id(cls, value: Any) -> str: + @classmethod + def validate_id(cls, value: Any) -> Any: """Pydantic validator for ID fields in OME models. If no value is provided, this validator provides and integer ID, and stores the maximum previously-seen value on the class. """ # get the required LSID field from the annotation - id_field = cls.__fields__.get("id") - if not id_field: + current_count = _COUNTERS.setdefault(cls, 0) + if isinstance(value, str): + # parse the id and update the counter + v_id = value.rsplit(":", 1)[-1] + with contextlib.suppress(ValueError): + _COUNTERS[cls] = max(current_count, int(v_id)) return value - - # Store the highest seen value on the class._max_id attribute. - if not hasattr(cls, "_max_id"): - cls._max_id = 0 # type: ignore [misc] - cls.__annotations__["_max_id"] = ClassVar[int] - if value is OMEType._AUTO_SEQUENCE: - value = cls._max_id + 1 if isinstance(value, int): - v_id = value - id_string = id_field.type_.__name__[:-2] - value = f"{id_string}:{value}" - else: - value = str(value) - v_id = value.rsplit(":", 1)[-1] - with contextlib.suppress(ValueError): - v_id = int(v_id) - cls._max_id = max(cls._max_id, v_id) - return id_field.type_(value) + _COUNTERS[cls] = max(current_count, value) + return f"{cls.__name__}:{value}" - def __getstate__(self: Any) -> Dict[str, Any]: - """Support pickle of our weakref references.""" - state = super().__getstate__() - state["__private_attribute_values__"].pop("_ref", None) - return state + if value is _AUTO_SEQUENCE: + # just increment the counter + _COUNTERS[cls] += 1 + return f"{cls.__name__}:{_COUNTERS[cls]}" - @classmethod - def snake_name(cls) -> str: - from .model import _camel_to_snake + raise ValueError(f"Invalid ID value: {value!r}") + + # @classmethod + # def snake_name(cls) -> str: + # from .model import _camel_to_snake - return _camel_to_snake[cls.__name__] + # return _camel_to_snake[cls.__name__] + + +def _quantity_property(field_name: str) -> property: + """Create property that returns a ``pint.Quantity`` combining value and unit.""" + from ome_types2._units import ureg + + def quantity(self: Any) -> Optional["pint.Quantity"]: + value = getattr(self, field_name) + if value is None: + return None + + unit = cast("Enum", getattr(self, _UNIT_FIELD.format(field_name))) + return ureg.Quantity(value, unit.value.replace(" ", "_")) + + return property(quantity) diff --git a/src/ome_types2/_mixins/_reference.py b/src/ome_types2/_mixins/_reference.py index 20d30190..db679a07 100644 --- a/src/ome_types2/_mixins/_reference.py +++ b/src/ome_types2/_mixins/_reference.py @@ -1,5 +1,5 @@ import weakref -from typing import Optional +from typing import Any, Dict, Optional from ._base_type import OMEType @@ -12,3 +12,9 @@ def ref(self) -> "OMEType | None": if self._ref is None: raise ValueError("references not yet resolved on root OME object") return self._ref() + + def __getstate__(self: Any) -> Dict[str, Any]: + """Support pickle of our weakref references.""" + state = super().__getstate__() + state["__private_attribute_values__"].pop("_ref", None) + return state diff --git a/src/ome_types2/_units.py b/src/ome_types2/_units.py new file mode 100644 index 00000000..088ca164 --- /dev/null +++ b/src/ome_types2/_units.py @@ -0,0 +1,9 @@ +import pint + +ureg: pint.UnitRegistry = pint.UnitRegistry(auto_reduce_dimensions=True) +ureg.define("reference_frame = [_reference_frame]") +ureg.define("@alias grade = gradian") +ureg.define("@alias astronomical_unit = ua") +ureg.define("line = inch / 12") +ureg.define("millitorr = torr / 1000 = mTorr") +ureg.define("@alias torr = Torr") diff --git a/src/ome_types2/model/simple_types.py b/src/ome_types2/model/simple_types.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/v1/test_model.py b/tests/v1/test_model.py index 6dd32789..2c8e157d 100644 --- a/tests/v1/test_model.py +++ b/tests/v1/test_model.py @@ -6,10 +6,10 @@ from xml.etree import ElementTree import pytest +import util from pydantic import ValidationError from xmlschema.validators.exceptions import XMLSchemaValidationError -import util from ome_types import from_tiff, from_xml, model, to_xml from ome_types._xmlschema import NS_OME, URI_OME, get_schema, to_xml_element diff --git a/tests/v2/test_from_xml.py b/tests/v2/test_from_xml.py deleted file mode 100644 index ef85a9a7..00000000 --- a/tests/v2/test_from_xml.py +++ /dev/null @@ -1,24 +0,0 @@ -from pathlib import Path - -import pydantic -import pytest - -from ome_types2 import OME, from_xml - -TESTS = Path(__file__).parent.parent -ALL_XML = list((TESTS / "data").glob("*.ome.xml")) -INVALID = {"bad", "invalid_xml_annotation"} - - -def true_stem(p: Path) -> str: - return p.name.partition(".")[0] - - -@pytest.mark.filterwarnings("ignore::ResourceWarning") # FIXME -@pytest.mark.parametrize("xml", ALL_XML, ids=true_stem) -def test_from_xml(xml: Path) -> None: - if true_stem(xml) in INVALID: - with pytest.raises(pydantic.ValidationError): - from_xml(xml) - else: - assert isinstance(from_xml(xml), OME) diff --git a/tests/v2/test_model.py b/tests/v2/test_model.py new file mode 100644 index 00000000..ec1ec85c --- /dev/null +++ b/tests/v2/test_model.py @@ -0,0 +1,127 @@ +import pickle +from pathlib import Path + +import pydantic +import pytest + +from ome_types2 import OME, from_tiff, from_xml, to_xml +from ome_types2.model import ome_2016_06 as model + +DATA = Path(__file__).parent.parent / "data" +ALL_XML = set(DATA.glob("*.ome.xml")) +INVALID = {DATA / "invalid_xml_annotation.ome.xml", DATA / "bad.ome.xml"} + + +def _true_stem(p: Path) -> str: + return p.name.partition(".")[0] + + +@pytest.fixture(params=sorted(ALL_XML), ids=_true_stem) +def any_xml(request: pytest.FixtureRequest) -> Path: + return request.param + + +@pytest.fixture(params=sorted(ALL_XML - INVALID), ids=_true_stem) +def valid_xml(request: pytest.FixtureRequest) -> Path: + return request.param + + +@pytest.fixture(params=INVALID, ids=_true_stem) +def invalid_xml(request: pytest.FixtureRequest) -> Path: + return request.param + + +@pytest.mark.filterwarnings("ignore::ResourceWarning") # FIXME +def test_from_xml(any_xml: Path) -> None: + if any_xml in INVALID: + with pytest.raises(pydantic.ValidationError): + from_xml(any_xml) + else: + assert isinstance(from_xml(any_xml), OME) + + +def test_from_tiff() -> None: + """Test that OME metadata extractions from Tiff headers works.""" + ome = from_tiff(DATA / "ome.tiff") + assert len(ome.images) == 1 + assert ome.images[0].id == "Image:0" + assert ome.images[0].pixels.size_x == 6 + assert ome.images[0].pixels.channels[0].samples_per_pixel == 1 + + +@pytest.mark.filterwarnings("ignore::ResourceWarning") # FIXME +def test_serialization(valid_xml: Path) -> None: + """Test pickle serialization and reserialization.""" + ome = from_xml(valid_xml) + serialized = pickle.dumps(ome) + deserialized = pickle.loads(serialized) + assert ome == deserialized + + +def test_no_id(): + """Test that ids are optional, and auto-increment.""" + i = model.Instrument(id=20) + assert i.id == "Instrument:20" + i2 = model.Instrument() + assert i2.id == "Instrument:21" + + # but validation still works + with pytest.raises(ValueError): + model.Instrument(id="nonsense") + + +def test_required_missing() -> None: + """Test subclasses with non-default arguments still work.""" + with pytest.raises(pydantic.ValidationError, match="value\n field required"): + model.BooleanAnnotation() + + with pytest.raises(pydantic.ValidationError, match="x\n field required"): + model.Label() + + +def test_refs() -> None: + xml = DATA / "two-screens-two-plates-four-wells.ome.xml" + ome = from_xml(xml) + assert ome.screens[0].plate_refs[0].ref is ome.plates[0] + + +def test_with_ome_ns() -> None: + assert from_xml(DATA / "ome_ns.ome.xml").experimenters + + +def test_roundtrip_inverse(valid_xml, tmp_path: Path): + """both variants have been touched by the model, here...""" + ome1 = from_xml(valid_xml) + + xml = to_xml(ome1) + out = tmp_path / "test.xml" + out.write_bytes(xml.encode()) + ome2 = from_xml(out) + + assert ome1 == ome2 + + +# def test_roundtrip(): +# """Ensure we can losslessly round-trip XML through the model and back.""" +# valid_xml = Path(DATA / "example.ome.xml") +# raw_bytes = valid_xml.read_bytes() +# node1 = etree.fromstring(raw_bytes) +# canonical_input = etree.tostring(node1, method="c14n2") + +# ome = from_xml(valid_xml) +# rexml = to_xml(ome) + +# node2 = etree.fromstring(rexml.encode()) +# canonical_output = etree.tostring(node2, method="c14n2") + +# assert canonical_input == canonical_output +# try: +# assert canonicalize(rexml, False) == original +# except AssertionError: +# # Special xfail catch since two files fail only with xml2dict +# if true_stem(Path(xml)) in SHOULD_FAIL_ROUNDTRIP_LXML and parser == "lxml": +# pytest.xfail( +# f"Expected failure on roundtrip using xml2dict on file: {stem}" +# ) +# else: +# raise diff --git a/tests/v2/test_units.py b/tests/v2/test_units.py new file mode 100644 index 00000000..bc1b2249 --- /dev/null +++ b/tests/v2/test_units.py @@ -0,0 +1,75 @@ +import pytest +from pint import DimensionalityError +from pydantic import ValidationError + +from ome_types2._units import ureg +from ome_types2.model import simple_types +from ome_types2.model.ome_2016_06 import Channel, Laser, Plane + + +def test_quantity_math(): + """Validate math on quantities with different but compatible units.""" + channel = Channel( + excitation_wavelength=475, + excitation_wavelength_unit="nm", + emission_wavelength=530000, + emission_wavelength_unit="pm", + ) + shift = ( + channel.emission_wavelength_quantity - channel.excitation_wavelength_quantity + ) + # Compare to a tolerance due to Pint internal factor representation. + assert abs(shift.to("nm").m - 55) < 1e-12 + + +def test_invalid_unit(): + """Ensure incompatible units in constructor raises ValidationError.""" + with pytest.raises(ValidationError): + Channel( + excitation_wavelength=475, + excitation_wavelength_unit="kg", + ) + + +def test_dimensionality_error(): + """Ensure math on incompatible units raises DimensionalityError.""" + laser = Laser( + id="LightSource:1", + repetition_rate=10, + repetition_rate_unit="MHz", + wavelength=640, + ) + with pytest.raises(DimensionalityError): + laser.repetition_rate_quantity + laser.wavelength_quantity + + +def test_reference_frame(): + """Validate reference_frame behavior.""" + plane = Plane( + the_c=0, + the_t=0, + the_z=0, + position_x=1, + position_x_unit="reference frame", + position_y=2, + position_y_unit="mm", + ) + # Verify two different ways that reference_frame and length are incompatible. + with pytest.raises(DimensionalityError): + plane.position_x_quantity + plane.position_y_quantity + product = plane.position_x_quantity * plane.position_y_quantity + assert not product.check("[area]") + # Verify that we can obtain a usable length if we know the conversion factor. + conversion_factor = ureg.Quantity(1, "micron/reference_frame") + position_x = plane.position_x_quantity * conversion_factor + assert position_x.check("[length]") + + +def test_all_units(): + """Test that all Unit* enums are in the registry.""" + for t in dir(simple_types): + if not t.startswith("Unit"): + continue + e = getattr(simple_types, t) + for v in e.__members__.values(): + assert v.value.replace(" ", "_") in ureg diff --git a/xsdata_test/.xsdata.xml b/xsdata_test/.xsdata.xml deleted file mode 100644 index 06b4e850..00000000 --- a/xsdata_test/.xsdata.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - dataclasses - - diff --git a/xsdata_test/gen.py b/xsdata_test/gen.py deleted file mode 100644 index e3fc4116..00000000 --- a/xsdata_test/gen.py +++ /dev/null @@ -1,16 +0,0 @@ -import os -import subprocess -from pathlib import Path - -from xsdata.formats.dataclass.parsers import XmlParser - -os.chdir(Path(__file__).parent) - -subprocess.run(["xsdata", "schema.xsd"]) -# subprocess.run(["black", "generated"]) -# subprocess.run(["ruff", "generated", "--fix", "--ignore=D1"]) - -from generated.schema_mod import Root # noqa: E402 - -result = XmlParser().parse("instance.xml", Root) -print(result) diff --git a/xsdata_test/generated/__init__.py b/xsdata_test/generated/__init__.py deleted file mode 100644 index 66577824..00000000 --- a/xsdata_test/generated/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from generated.schema_mod import ( - Foo, - Root, -) - -__all__ = [ - "Foo", - "Root", -] diff --git a/xsdata_test/generated/_base.py b/xsdata_test/generated/_base.py deleted file mode 100644 index 688f7957..00000000 --- a/xsdata_test/generated/_base.py +++ /dev/null @@ -1,12 +0,0 @@ -from dataclasses import fields - - -class MyMixin: - def __repr__(self) -> str: - name = self.__class__.__qualname__ - reprs = [] - for f in fields(self): - val = getattr(self, f.name) - if val is not None: - reprs.append(f"{f.name}={val!r}") - return f"{name}({', '.join(reprs)})" diff --git a/xsdata_test/generated/schema_mod.py b/xsdata_test/generated/schema_mod.py deleted file mode 100644 index e6961aab..00000000 --- a/xsdata_test/generated/schema_mod.py +++ /dev/null @@ -1,28 +0,0 @@ -from dataclasses import dataclass, field -from typing import Optional - -from generated._base import MyMixin - -__NAMESPACE__ = "http://xsdata" - - -@dataclass(repr=False) -class Foo(MyMixin): - id: Optional[str] = field( - default=None, - metadata={ - "type": "Attribute", - "pattern": r"(urn:lsid:([\w\-\.]+\.[\w\-\.]+)+:\S+:\S+)|(\S+:\S+)", - }, - ) - - -@dataclass(repr=False) -class Root(MyMixin): - foo: Optional[Foo] = field( - default=None, - metadata={ - "type": "Element", - "namespace": "http://xsdata", - }, - ) diff --git a/xsdata_test/instance.xml b/xsdata_test/instance.xml deleted file mode 100644 index b949f41a..00000000 --- a/xsdata_test/instance.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/xsdata_test/schema.xsd b/xsdata_test/schema.xsd deleted file mode 100644 index 49a1794e..00000000 --- a/xsdata_test/schema.xsd +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - Either LSID or internal consistent IDs for the file - - - - - - -