diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 208232f882..db027239fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -471,10 +471,8 @@ class file: def mvd(self): if not LARK_AVAILABLE: return None - file_description = self.wrapped_data.header.file_description return MvdInfo( - get_description=lambda: file_description.description, - set_description=lambda d: setattr(file_description, "description", tuple(d)) + self.header ) def __getattr__(self, attr) -> Union[Any, Callable[..., ifcopenshell.entity_instance]]: diff --git a/src/ifcopenshell-python/ifcopenshell/util/mvd_info.py b/src/ifcopenshell-python/ifcopenshell/util/mvd_info.py index 573f4945fa..8156f3f20e 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/mvd_info.py +++ b/src/ifcopenshell-python/ifcopenshell/util/mvd_info.py @@ -131,34 +131,46 @@ else: class MvdInfo: - def __init__(self, get_description: Callable[[], list[str]], set_description: Callable[[list[str]], None]): - self._get_description = get_description - self._set_description = set_description + def __init__(self, header): + self._header = header self._parsed = None def _ensure_parsed(self): if not LARK_AVAILABLE: return if self._parsed is None: - self._parsed = parse_mvd(self.description) + self._parsed = parse_mvd(self._header.file_description.description) @property def description(self) -> list[str]: - return self._get_description() + return self._header.file_description.description @description.setter def description(self, new_description: list[str]): - self._set_description(new_description) + self._header.file_description.description = tuple(new_description) self._parsed = None @property def view_definitions(self): self._ensure_parsed() - return ', '.join(' '.join(item.split()) for item in self._parsed.view_definitions) if self._parsed else None + if not self._parsed or self._parsed.view_definitions is None: + return None # + + vd = self._parsed.view_definitions + vd_list = vd if isinstance(vd, list) else [vd] if vd else [] + return AutoCommitList( + vd_list, + callback=lambda val: (self._update_keyword("ViewDefinition", val), setattr(self, "_parsed", None)), + formatter=lambda lst: ",".join(str(i) for i in lst) + ) @view_definitions.setter - def view_definitions(self, new_value: str): - self._update_keyword("ViewDefinition", new_value) + def view_definitions(self, new_value: str | list[str]): + if isinstance(new_value, list): + value = ", ".join(new_value) + else: + value = str(new_value) + self._update_keyword("ViewDefinition", value) @property def comments(self): diff --git a/src/ifcopenshell-python/test/test_mvd_info.py b/src/ifcopenshell-python/test/test_mvd_info.py index 3335d9772e..8c0710767c 100644 --- a/src/ifcopenshell-python/test/test_mvd_info.py +++ b/src/ifcopenshell-python/test/test_mvd_info.py @@ -16,12 +16,18 @@ def load_fixture(): class TestViewDefinition: def test_single_view(self, load_fixture): f = load_fixture("passing_header.ifc") - assert f.mvd.view_definitions == "Alignment-basedView" + assert f.mvd.view_definitions == ["Alignment-basedView"] def test_multiple_views(self, load_fixture): f = load_fixture("two_views.ifc") - assert f.mvd.view_definitions == 'CoordinationView_V2.0, SpaceBoundaryAddonView' - + assert f.mvd.view_definitions == ['CoordinationView_V2.0', 'SpaceBoundaryAddonView'] + + def test_add_view(self): + header = MockHeader(("ViewDefinition [CoordinationView_V2.0]",)) + mvd = mvd_info.MvdInfo(header) + assert mvd.view_definitions == ["CoordinationView_V2.0"] + mvd.view_definitions.append("SpaceBoundaryAddonView") + assert mvd.view_definitions == ['CoordinationView_V2.0', 'SpaceBoundaryAddonView'] class TestExchangeRequirements: def test_parsing(self, load_fixture): @@ -36,8 +42,8 @@ class TestExchangeRequirements: 'ExchangeRequirement [SomethingElse]' ) assert f.mvd.exchange_requirements == 'SomethingElse' - f.mvd.view_definitions = 'CoordinationView_V2.0' - assert f.mvd.view_definitions == 'CoordinationView_V2.0' + f.mvd.view_definitions = ['CoordinationView_V2.0'] + assert f.mvd.view_definitions == ['CoordinationView_V2.0'] class TestComments: @@ -126,9 +132,14 @@ class TestKeywords: class TestFallbackBehavior: def test_parse_mvd_fallback(self, monkeypatch): monkeypatch.setattr(mvd_info, "LARK_AVAILABLE", False) + header = MockHeader(("ViewDefinition [ShouldNotParse]",)) mvd = mvd_info.MvdInfo( - get_description=lambda: ["ViewDefinition [ShouldNotParse]"], - set_description=lambda d: None + header ) assert mvd.view_definitions is None assert mvd.keywords == set() + +class MockHeader: + def __init__(self, description): + self.file_description = type("FileDescription", (), {"description": description}) +