Measurement.to_xml() in pygdtf/__init__.py writes the LuminousIntensity
attribute unconditionally whenever self.luminous_intensity is not None
(default 0.0 on read):
def to_xml(self):
attrs = {}
if self.physical is not None:
attrs["Physical"] = f"{self.physical:.6f}"
if self.luminous_intensity is not None:
attrs["LuminousIntensity"] = f"{self.luminous_intensity:.6f}"
if self.transmission is not None and (
"Transmission" in self._attr_keys or self.transmission != 0
):
attrs["Transmission"] = f"{self.transmission:.6f}"
...
This is correct for an EmitterMeasurement, which requires
LuminousIntensity. But the same Measurement class is also used for
FilterMeasurement, where the GDTF XSD explicitly forbids
LuminousIntensity and requires Transmission instead. Since
luminous_intensity defaults to 0.0 on read regardless of whether the
source XML had the attribute, every Filter/Measurement round-tripped
through pygdtf gains an invalid LuminousIntensity="0.000000".
Reproduction
Round-trip any GDTF file whose PhysicalDescriptions/Filters contain
Measurement elements (e.g. the Robe Robin Esprite / iSpiider / MegaPointe
files published on gdtf-share.com) through:
with pygdtf.FixtureType(path) as fixture:
pygdtf.FixtureTypeWriter(fixture).write_gdtf(out_path)
then validate description.xml against gdtf.xsd. Result, once per
Filter/Measurement:
/GDTF/FixtureType/PhysicalDescriptions/Filters/Filter[N]/Measurement
'LuminousIntensity' attribute not allowed for element
13, 3, and 17 such errors respectively on the three Robe files mentioned
above, on otherwise-valid, XSD-passing originals.
Root cause
Transmission already handles this correctly one line below, via
self._attr_keys — the set of attribute names actually present on the
source element, captured in _read_xml:
if self.transmission is not None and (
"Transmission" in self._attr_keys or self.transmission != 0
):
LuminousIntensity should use the identical condition:
if self.luminous_intensity is not None and (
"LuminousIntensity" in self._attr_keys or self.luminous_intensity != 0
):
attrs["LuminousIntensity"] = f"{self.luminous_intensity:.6f}"
With this change, an EmitterMeasurement (where the source XML always has
LuminousIntensity) keeps writing it as before, while a
FilterMeasurement (where it's never in the source) correctly omits it.
Version
pygdtf 1.4.5 (PyPI).
Note
A related, lower-severity issue: Wheel/Slot round-trips gain a spurious
MediaFileName="" attribute even when the source slot had no media
(Resource(name="") created unconditionally on read, __init__.py
~r794-795, then written back unconditionally). XSD-valid but noisy — happy
to file that separately if useful.
Measurement.to_xml()inpygdtf/__init__.pywrites theLuminousIntensityattribute unconditionally whenever
self.luminous_intensity is not None(default
0.0on read):This is correct for an
EmitterMeasurement, which requiresLuminousIntensity. But the sameMeasurementclass is also used forFilterMeasurement, where the GDTF XSD explicitly forbidsLuminousIntensityand requiresTransmissioninstead. Sinceluminous_intensitydefaults to0.0on read regardless of whether thesource XML had the attribute, every
Filter/Measurementround-trippedthrough pygdtf gains an invalid
LuminousIntensity="0.000000".Reproduction
Round-trip any GDTF file whose
PhysicalDescriptions/FilterscontainMeasurementelements (e.g. the Robe Robin Esprite / iSpiider / MegaPointefiles published on gdtf-share.com) through:
then validate
description.xmlagainstgdtf.xsd. Result, once perFilter/Measurement:13, 3, and 17 such errors respectively on the three Robe files mentioned
above, on otherwise-valid, XSD-passing originals.
Root cause
Transmissionalready handles this correctly one line below, viaself._attr_keys— the set of attribute names actually present on thesource element, captured in
_read_xml:LuminousIntensityshould use the identical condition:With this change, an
EmitterMeasurement(where the source XML always hasLuminousIntensity) keeps writing it as before, while aFilterMeasurement(where it's never in the source) correctly omits it.Version
pygdtf 1.4.5 (PyPI).
Note
A related, lower-severity issue:
Wheel/Slotround-trips gain a spuriousMediaFileName=""attribute even when the source slot had no media(
Resource(name="")created unconditionally on read,__init__.py~r794-795, then written back unconditionally). XSD-valid but noisy — happy
to file that separately if useful.