diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index 00c1018a0b..acd90f6c4d 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -17,6 +17,7 @@ # along with IfcOpenShell. If not, see . +from __future__ import annotations import os import sys import operator @@ -27,7 +28,7 @@ from ..entity_instance import entity_instance from . import has_occ -from typing import TypeVar, Union, Optional, Generator, Any, Literal +from typing import TypeVar, Union, Optional, Generator, Any, Literal, overload T = TypeVar("T") ShapeElementType = Union[ @@ -95,6 +96,16 @@ SETTING = Literal[ "piecewise-step-param", "use-python-opencascade", ] +SERIALIZER_SETTING = Literal[ + "use-element-names", + "use-element-guids", + "use-element-step-ids", + "use-material-names", + "use-element-types", + "y-up", + "ecef", + "digits", +] class missing_setting: @@ -109,8 +120,6 @@ class settings_mixin: when available """ - use_python_opencascade = False - def __init__(self, **kwargs): super(settings_mixin, self).__init__() for k, v in kwargs.items(): @@ -127,13 +136,17 @@ class settings_mixin: return "%s(%s)" % (type(self).__name__, ", ".join(map(fmt_pair, self.setting_names()))) @staticmethod - def name(k: str) -> SETTING: + def name(k: str) -> Union[SETTING, SERIALIZER_SETTING]: return k.lower().replace("_", "-") @staticmethod - def rname(k: SETTING) -> str: + def rname(k: Union[SETTING, SERIALIZER_SETTING]) -> str: return k.upper().replace("-", "_") + @overload + def set(self: settings, k: SETTING, v: Any) -> None: ... + @overload + def set(self: serializer_settings, k: SERIALIZER_SETTING, v: Any) -> None: ... def set(self, k: SETTING, v: Any) -> None: """ Set value of the setting named `k` to `v`. @@ -141,7 +154,7 @@ class settings_mixin: :raises RuntimeError: If there is no setting with name `k`. """ k = self.name(k) - if k == "use-python-opencascade": + if isinstance(self, settings) and k == "use-python-opencascade": if not has_occ: raise AttributeError("Python OpenCASCADE is not installed") if v: @@ -151,21 +164,36 @@ class settings_mixin: else: self.set_(self.name(k), v) - def get(self, k: SETTING) -> Any: + @overload + def get(self: settings, k: SETTING) -> Any: ... + @overload + def get(self: serializer_settings, k: SERIALIZER_SETTING) -> Any: ... + def get(self, k: str) -> Any: """ Return value of the setting named `k`. :raises RuntimeError: If there is no setting with name `k`. """ k = self.name(k) - if k == "use-python-opencascade": + if isinstance(self, settings) and k == "use-python-opencascade": return self.use_python_opencascade return self.get_(k) - def setting_names(self) -> tuple[SETTING, ...]: - return super().setting_names() + ("use-python-opencascade",) + @overload + def setting_names(self: settings) -> tuple[SETTING, ...]: ... + @overload + def setting_names(self: serializer_settings) -> tuple[SERIALIZER_SETTING, ...]: ... + def setting_names(self) -> tuple[str, ...]: + setting_names = super().setting_names() + if isinstance(self, settings): + setting_names += ("use-python-opencascade",) + return setting_names - def __getattr__(self, k: str) -> SETTING: + @overload + def __getattr__(self: settings, k: str) -> SETTING: ... + @overload + def __getattr__(self: serializer_settings, k: str) -> SERIALIZER_SETTING: ... + def __getattr__(self, k: str) -> str: # Swig wrapper will try to access "this", # ensure we won't accidentally call any c-extension methods # like .setting_names() until wrapper is not completely initialized. @@ -183,7 +211,7 @@ class serializer_settings(settings_mixin, ifcopenshell_wrapper.SerializerSetting class settings(settings_mixin, ifcopenshell_wrapper.Settings): - pass + use_python_opencascade = False class iterator(ifcopenshell_wrapper.Iterator): diff --git a/src/ifcopenshell-python/test/test_create_shape.py b/src/ifcopenshell-python/test/test_create_shape.py index 3267d86de7..639c3b3541 100644 --- a/src/ifcopenshell-python/test/test_create_shape.py +++ b/src/ifcopenshell-python/test/test_create_shape.py @@ -7,10 +7,11 @@ from typing import get_args class TestGeomSettings: - def test_run(self): + def test_settings(self): settings = ifcopenshell.geom.settings() assert set(get_args(ifcopenshell.geom.SETTING)) == set(settings.setting_names()) + assert "use-python-opencascade" in settings.setting_names() assert settings.get(settings.USE_PYTHON_OPENCASCADE) is False assert settings.get("use-python-opencascade") is False assert "USE_PYTHON_OPENCASCADE = False" in repr(settings) @@ -27,6 +28,18 @@ class TestGeomSettings: settings.set(settings.USE_PYTHON_OPENCASCADE, True) assert "USE_PYTHON_OPENCASCADE = False" in repr(settings) + def test_serializer_settings(self): + settings = ifcopenshell.geom.serializer_settings() + assert set(get_args(ifcopenshell.geom.SERIALIZER_SETTING)) == set(settings.setting_names()) + + # Only for settings. + assert "use-python-opencascade" not in settings.setting_names() + with pytest.raises(AttributeError): + settings.get(settings.USE_PYTHON_OPENCASCADE) + with pytest.raises(RuntimeError): + settings.get("use-python-opencascade") + assert "USE_PYTHON_OPENCASCADE" not in repr(settings) + class TestAssignObject: def test_no_welding_on_distinct_items(self):