From 7120f704e06fb1fca0520746d82b6a9d5939ef1a Mon Sep 17 00:00:00 2001 From: vanous Date: Mon, 21 Jul 2025 20:31:47 +0200 Subject: [PATCH 1/2] Add check and a workaround for missing channels on incorrect GDTF files --- pygdtf/__init__.py | 58 +++++-- tests/missing_channels.xml | 266 +++++++++++++++++++++++++++++ tests/test_for_missing_channels.py | 34 ++++ 3 files changed, 347 insertions(+), 11 deletions(-) create mode 100644 tests/missing_channels.xml create mode 100644 tests/test_for_missing_channels.py diff --git a/pygdtf/__init__.py b/pygdtf/__init__.py index d69a45d..16dcc1d 100644 --- a/pygdtf/__init__.py +++ b/pygdtf/__init__.py @@ -1361,6 +1361,23 @@ def _read_xml(self, xml_node: "Element", xml_parent: Optional["Element"] = None) for i in dmx_channels_collect.findall("DMXChannel") ] + self._process_dmx_channels() + if self._check_for_missing_channels(): + self._process_dmx_channels() + + relations_node = xml_node.find("Relations") + if relations_node is not None: + self.relations = [ + Relation(xml_node=i) for i in relations_node.findall("Relation") + ] + + ftmacros_node = xml_node.find("FTMacros") + if ftmacros_node is not None: + self.ft_macros = [ + Macro(xml_node=i) for i in ftmacros_node.findall("FTMacro") + ] + + def _process_dmx_channels(self): dmx_channels = get_dmx_channels( gdtf_profile=self.fixture_type, dmx_mode=self, @@ -1398,17 +1415,35 @@ def _read_xml(self, xml_node: "Element", xml_parent: Optional["Element"] = None) self.virtual_channels_count = len(self.virtual_channels) - relations_node = xml_node.find("Relations") - if relations_node is not None: - self.relations = [ - Relation(xml_node=i) for i in relations_node.findall("Relation") - ] - - ftmacros_node = xml_node.find("FTMacros") - if ftmacros_node is not None: - self.ft_macros = [ - Macro(xml_node=i) for i in ftmacros_node.findall("FTMacro") - ] + def _check_for_missing_channels(self): + # check if channels are missing and add them + result = False + highest_offset = 0 + for channel in self.dmx_channels: + if channel.offset is not None: + if channel.offset[0] > highest_offset: + highest_offset = max(channel.offset) + + if len(self.dmx_channels) != highest_offset: + # we have missing channels + for i in range(1, highest_offset + 1): + found = False + for channel in self.dmx_channels: + if channel.offset is not None: + if i in channel.offset: + found = True + break + if not found: + result = True + self._dmx_channels.append( + DmxChannel( + name="NoFeature", + offset=[i], + geometry=self.geometry, + attribute=NodeLink("Attributes", "NoFeature"), + ) + ) + return result def as_dict(self): return { @@ -1653,6 +1688,7 @@ def __init__( # make this invalid GDTF file valid self.channel_functions = [ ChannelFunction( + name="NoFeature", attribute=NodeLink("Attributes", "NoFeature"), default=DmxValue("0/1"), ) diff --git a/tests/missing_channels.xml b/tests/missing_channels.xml new file mode 100644 index 0000000..67835cf --- /dev/null +++ b/tests/missing_channels.xml @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_for_missing_channels.py b/tests/test_for_missing_channels.py new file mode 100644 index 0000000..026bfa5 --- /dev/null +++ b/tests/test_for_missing_channels.py @@ -0,0 +1,34 @@ +# MIT License +# +# Copyright (C) 2025 vanous +# +# This file is part of pygdtf. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import pytest +import os +from pathlib import Path + + +def test_missing_channels(pygdtf_module): + path = Path("tests/missing_channels.xml") + with pygdtf_module.FixtureType(dsc_file=path) as fixture: + mode = fixture.dmx_modes[0] + assert mode.dmx_channels_count == 56 From 746ab55e34592f3197c8a9a6858aac1c1a945434 Mon Sep 17 00:00:00 2001 From: vanous Date: Mon, 21 Jul 2025 22:13:09 +0200 Subject: [PATCH 2/2] Add 3.14 to tested pythons in CI/CD --- .github/workflows/run-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 40177e7..f08988e 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: szenius/set-timezone@v1.1