From 776158adddda3828016962463e12abcf5faa3111 Mon Sep 17 00:00:00 2001 From: marioalexis Date: Thu, 1 Aug 2024 13:01:19 -0300 Subject: [PATCH 1/3] Base: Add PropertyError exception --- src/App/Application.cpp | 5 +++++ src/App/PropertyContainerPyImp.cpp | 2 +- src/Base/Exception.cpp | 17 +++++++++++++++++ src/Base/Exception.h | 20 ++++++++++++++++++++ src/Base/PyObjectBase.cpp | 1 + src/Base/PyObjectBase.h | 1 + 6 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 61291c85bd..624ec2c7c6 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -423,6 +423,10 @@ void Application::setupPythonException(PyObject* module) Base::PyExc_FC_CADKernelError = PyErr_NewException("Base.CADKernelError", Base::PyExc_FC_GeneralError, nullptr); Py_INCREF(Base::PyExc_FC_CADKernelError); PyModule_AddObject(module, "CADKernelError", Base::PyExc_FC_CADKernelError); + + Base::PyExc_FC_PropertyError = PyErr_NewException("Base.PropertyError", PyExc_AttributeError, nullptr); + Py_INCREF(Base::PyExc_FC_PropertyError); + PyModule_AddObject(module, "PropertyError", Base::PyExc_FC_PropertyError); } // clang-format on @@ -2184,6 +2188,7 @@ void Application::initTypes() new Base::ExceptionProducer; new Base::ExceptionProducer; new Base::ExceptionProducer; + new Base::ExceptionProducer; } namespace { diff --git a/src/App/PropertyContainerPyImp.cpp b/src/App/PropertyContainerPyImp.cpp index 28446c0eeb..8e2196dbbf 100644 --- a/src/App/PropertyContainerPyImp.cpp +++ b/src/App/PropertyContainerPyImp.cpp @@ -63,7 +63,7 @@ PyObject* PropertyContainerPy::getPropertyByName(PyObject *args) App::Property* prop = getPropertyContainerPtr()->getPropertyByName(pstr); if (!prop) { - PyErr_Format(PyExc_AttributeError, "Property container has no property '%s'", pstr); + PyErr_Format(Base::PyExc_FC_PropertyError, "Property container has no property '%s'", pstr); return nullptr; } diff --git a/src/Base/Exception.cpp b/src/Base/Exception.cpp index 6de4007eec..acbf6aa16c 100644 --- a/src/Base/Exception.cpp +++ b/src/Base/Exception.cpp @@ -612,6 +612,23 @@ PyObject* AttributeError::getPyExceptionType() const // --------------------------------------------------------- +PropertyError::PropertyError() = default; + +PropertyError::PropertyError(const char* sMessage) + : AttributeError(sMessage) +{} + +PropertyError::PropertyError(const std::string& sMessage) + : AttributeError(sMessage) +{} + +PyObject* PropertyError::getPyExceptionType() const +{ + return PyExc_FC_PropertyError; +} + +// --------------------------------------------------------- + RuntimeError::RuntimeError() = default; RuntimeError::RuntimeError(const char* sMessage) diff --git a/src/Base/Exception.h b/src/Base/Exception.h index 77e9545f84..2ebdd78744 100644 --- a/src/Base/Exception.h +++ b/src/Base/Exception.h @@ -670,6 +670,26 @@ public: PyObject* getPyExceptionType() const override; }; +/** + * The PropertyError can be used to indicate the usage of a wrong property name or value. + * @author Mario Passaglia + */ +class BaseExport PropertyError: public AttributeError +{ +public: + /// Construction + PropertyError(); + explicit PropertyError(const char* sMessage); + explicit PropertyError(const std::string& sMessage); + PropertyError(const PropertyError&) = default; + PropertyError(PropertyError&&) = default; + /// Destruction + ~PropertyError() noexcept override = default; + PropertyError& operator=(const PropertyError&) = default; + PropertyError& operator=(PropertyError&&) = default; + PyObject* getPyExceptionType() const override; +}; + /** * The RuntimeError can be used to indicate an unknown exception at runtime. * @author Werner Mayer diff --git a/src/Base/PyObjectBase.cpp b/src/Base/PyObjectBase.cpp index be96008764..45c62c8f27 100644 --- a/src/Base/PyObjectBase.cpp +++ b/src/Base/PyObjectBase.cpp @@ -48,6 +48,7 @@ PyObject* Base::PyExc_FC_BadGraphError = nullptr; PyObject* Base::PyExc_FC_ExpressionError = nullptr; PyObject* Base::PyExc_FC_ParserError = nullptr; PyObject* Base::PyExc_FC_CADKernelError = nullptr; +PyObject* Base::PyExc_FC_PropertyError = nullptr; typedef struct { //NOLINT PyObject_HEAD diff --git a/src/Base/PyObjectBase.h b/src/Base/PyObjectBase.h index 5f1ac2a7ed..2f40a24a61 100644 --- a/src/Base/PyObjectBase.h +++ b/src/Base/PyObjectBase.h @@ -430,6 +430,7 @@ BaseExport extern PyObject* PyExc_FC_BadGraphError; BaseExport extern PyObject* PyExc_FC_ExpressionError; BaseExport extern PyObject* PyExc_FC_ParserError; BaseExport extern PyObject* PyExc_FC_CADKernelError; +BaseExport extern PyObject* PyExc_FC_PropertyError; /** Exception handling for python callback functions * Is a convenience macro to manage the exception handling of python callback From db3f4f06421aea6a49a242fbb479f74a9c359feb Mon Sep 17 00:00:00 2001 From: marioalexis Date: Sun, 28 Jul 2024 16:11:47 -0300 Subject: [PATCH 2/3] Fem: Fix link scope for Python objects --- src/Mod/Fem/femobjects/base_femelement.py | 16 ++++- src/Mod/Fem/femobjects/base_femmeshelement.py | 15 ++++- .../Fem/femobjects/base_fempythonobject.py | 7 +++ src/Mod/Fem/femobjects/material_common.py | 58 +++++++++++++------ src/Mod/Fem/femobjects/material_reinforced.py | 16 +---- src/Mod/Fem/femobjects/mesh_gmsh.py | 35 +++++------ 6 files changed, 94 insertions(+), 53 deletions(-) diff --git a/src/Mod/Fem/femobjects/base_femelement.py b/src/Mod/Fem/femobjects/base_femelement.py index 0bc959b9d5..950c1e70a9 100644 --- a/src/Mod/Fem/femobjects/base_femelement.py +++ b/src/Mod/Fem/femobjects/base_femelement.py @@ -29,7 +29,7 @@ __url__ = "https://www.freecad.org" # \ingroup FEM # \brief base object for FEM Element Features - +from FreeCAD import Base from . import base_fempythonobject _PropHelper = base_fempythonobject._PropHelper @@ -50,7 +50,7 @@ class BaseFemElement(base_fempythonobject.BaseFemPythonObject): prop.append( _PropHelper( - type="App::PropertyLinkSubList", + type="App::PropertyLinkSubListGlobal", name="References", group="Element", doc="List of element shapes", @@ -59,3 +59,15 @@ class BaseFemElement(base_fempythonobject.BaseFemPythonObject): ) return prop + + def onDocumentRestored(self, obj): + # update old project with new properties + for prop in self._get_properties(): + try: + obj.getPropertyByName(prop.name) + except Base.PropertyError: + prop.add_to_object(obj) + + if prop.name == "References": + # change References to App::PropertyLinkSubListGlobal + prop.handle_change_type(obj, old_type="App::PropertyLinkSubList") diff --git a/src/Mod/Fem/femobjects/base_femmeshelement.py b/src/Mod/Fem/femobjects/base_femmeshelement.py index 6f63797bd4..31fcef9d31 100644 --- a/src/Mod/Fem/femobjects/base_femmeshelement.py +++ b/src/Mod/Fem/femobjects/base_femmeshelement.py @@ -30,6 +30,7 @@ __url__ = "https://www.freecad.org" # \brief base object for FEM Mesh Element Features +from FreeCAD import Base from . import base_fempythonobject _PropHelper = base_fempythonobject._PropHelper @@ -50,7 +51,7 @@ class BaseFemMeshElement(base_fempythonobject.BaseFemPythonObject): prop.append( _PropHelper( - type="App::PropertyLinkSubList", + type="App::PropertyLinkSubListGlobal", name="References", group="Mesh Element", doc="List of reference shapes", @@ -59,3 +60,15 @@ class BaseFemMeshElement(base_fempythonobject.BaseFemPythonObject): ) return prop + + def onDocumentRestored(self, obj): + # update old project with new properties + for prop in self._get_properties(): + try: + obj.getPropertyByName(prop.name) + except Base.PropertyError: + prop.add_to_object(obj) + + if prop.name == "References": + # change References to App::PropertyLinkSubListGlobal + prop.handle_change_type(obj, old_type="App::PropertyLinkSubList") diff --git a/src/Mod/Fem/femobjects/base_fempythonobject.py b/src/Mod/Fem/femobjects/base_fempythonobject.py index 226f37fb8c..65ed19fd43 100644 --- a/src/Mod/Fem/femobjects/base_fempythonobject.py +++ b/src/Mod/Fem/femobjects/base_fempythonobject.py @@ -65,3 +65,10 @@ class _PropHelper: obj.addProperty(**self.info) obj.setPropertyStatus(self.name, "LockDynamic") setattr(obj, self.name, self.value) + + def handle_change_type(self, obj, old_type, convert_old_value=lambda x: x): + if obj.getTypeIdOfProperty(self.name) == old_type: + self.value = convert_old_value(obj.getPropertyByName(self.name)) + obj.setPropertyStatus(self.name, "-LockDynamic") + obj.removeProperty(self.name) + self.add_to_object(obj) diff --git a/src/Mod/Fem/femobjects/material_common.py b/src/Mod/Fem/femobjects/material_common.py index a069318915..f230b843cc 100644 --- a/src/Mod/Fem/femobjects/material_common.py +++ b/src/Mod/Fem/femobjects/material_common.py @@ -30,8 +30,11 @@ __url__ = "https://www.freecad.org" # \ingroup FEM # \brief material common object +from FreeCAD import Base from . import base_fempythonobject +_PropHelper = base_fempythonobject._PropHelper + class MaterialCommon(base_fempythonobject.BaseFemPythonObject): """ @@ -42,27 +45,46 @@ class MaterialCommon(base_fempythonobject.BaseFemPythonObject): def __init__(self, obj): super().__init__(obj) - self.add_properties(obj) + + for prop in self._get_properties(): + prop.add_to_object(obj) + + def _get_properties(self): + prop = [] + + prop.append( + _PropHelper( + type="App::PropertyLinkSubListGlobal", + name="References", + group="Material", + doc="List of material shapes", + value=[], + ) + ) + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="Category", + group="Material", + doc="Material type: fluid or solid", + value=["Solid", "Fluid"], + ) + ) + + return prop def onDocumentRestored(self, obj): - self.add_properties(obj) + # update old project with new properties + for prop in self._get_properties(): + try: + obj.getPropertyByName(prop.name) + except Base.PropertyError: + prop.add_to_object(obj) + + if prop.name == "References": + # change References to App::PropertyLinkSubListGlobal + prop.handle_change_type(obj, old_type="App::PropertyLinkSubList") - def add_properties(self, obj): - # References - if not hasattr(obj, "References"): - obj.addProperty( - "App::PropertyLinkSubList", "References", "Material", "List of material shapes" - ) - obj.setPropertyStatus("References", "LockDynamic") - # Category - # attribute Category was added in commit 61fb3d429a - if not hasattr(obj, "Category"): - obj.addProperty( - "App::PropertyEnumeration", "Category", "Material", "Material type: fluid or solid" - ) - obj.setPropertyStatus("Category", "LockDynamic") - obj.Category = ["Solid", "Fluid"] # used in TaskPanel - obj.Category = "Solid" """ Some remarks to the category. Not finished, thus to be continued. diff --git a/src/Mod/Fem/femobjects/material_reinforced.py b/src/Mod/Fem/femobjects/material_reinforced.py index 13f984ea01..a86c4bf2e8 100644 --- a/src/Mod/Fem/femobjects/material_reinforced.py +++ b/src/Mod/Fem/femobjects/material_reinforced.py @@ -29,10 +29,10 @@ __url__ = "https://www.freecad.org" # \ingroup FEM # \brief reinforced object -from . import base_fempythonobject +from . import material_common -class MaterialReinforced(base_fempythonobject.BaseFemPythonObject): +class MaterialReinforced(material_common.MaterialCommon): """ The MaterialReinforced object """ @@ -42,20 +42,10 @@ class MaterialReinforced(base_fempythonobject.BaseFemPythonObject): def __init__(self, obj): super().__init__(obj) - obj.addProperty( - "App::PropertyLinkSubList", "References", "Material", "List of material shapes" - ) - obj.setPropertyStatus("References", "LockDynamic") - obj.addProperty( "App::PropertyMap", "Reinforcement", "Composites", "Reinforcement material properties" ) obj.setPropertyStatus("Reinforcement", "LockDynamic") - obj.addProperty( - "App::PropertyEnumeration", "Category", "Material", "Matrix material properties" - ) - obj.setPropertyStatus("Category", "LockDynamic") - + # overwrite Category enumeration obj.Category = ["Solid"] - obj.Category = "Solid" diff --git a/src/Mod/Fem/femobjects/mesh_gmsh.py b/src/Mod/Fem/femobjects/mesh_gmsh.py index df1f3cc134..b0727aba8d 100644 --- a/src/Mod/Fem/femobjects/mesh_gmsh.py +++ b/src/Mod/Fem/femobjects/mesh_gmsh.py @@ -29,6 +29,7 @@ __url__ = "https://www.freecad.org" # \ingroup FEM # \brief mesh gmsh object +from FreeCAD import Base from . import base_fempythonobject _PropHelper = base_fempythonobject._PropHelper @@ -41,8 +42,6 @@ class MeshGmsh(base_fempythonobject.BaseFemPythonObject): Type = "Fem::FemMeshGmsh" - # they will be used from the task panel too, thus they need to be outside of the __init__ - def __init__(self, obj): super().__init__(obj) @@ -277,23 +276,21 @@ class MeshGmsh(base_fempythonobject.BaseFemPythonObject): for prop in self._get_properties(): try: obj.getPropertyByName(prop.name) - if prop.name == "Algorithm2D": - # refresh the list of known 2D algorithms - obj.Algorithm2D = prop.value - elif prop.name == "Algorithm3D": - # refresh the list of known 3D algorithms - obj.Algorithm3D = prop.value - elif prop.name == "HighOrderOptimize": - # HighOrderOptimize was once App::PropertyBool, so check this - if type(obj.HighOrderOptimize) is bool: - value = obj.HighOrderOptimize - obj.setPropertyStatus("HighOrderOptimize", "-LockDynamic") - obj.removeProperty("HighOrderOptimize") - prop.add_to_object(obj) - obj.HighOrderOptimize = "Optimization" if value else "None" - except: + except Base.PropertyError: prop.add_to_object(obj) + if prop.name == "Algorithm2D": + # refresh the list of known 2D algorithms for old projects + obj.Algorithm2D = prop.value + elif prop.name == "Algorithm3D": + # refresh the list of known 3D algorithms for old projects + obj.Algorithm3D = prop.value + elif prop.name == "HighOrderOptimize": + # HighOrderOptimize was once App::PropertyBool, so check this + prop.handle_change_type( + obj, "App::PropertyBool", lambda x: "Optimization" if x else "None" + ) + # migrate old Part property to Shape property try: value_part = obj.getPropertyByName("Part") @@ -304,9 +301,9 @@ class MeshGmsh(base_fempythonobject.BaseFemPythonObject): type="App::PropertyLinkGlobal", name="Shape", group="FEM Mesh", - doc="Geometry object, the mesh is made from. The geometry object has to have a Shape", + doc="Geometry object, the mesh is made from. The geometry object has to have a Shape.", value=value_part, ) prop.add_to_object(obj) - except: + except Base.PropertyError: pass From 317a664cbd11b1376a94cb5834a47835060154e4 Mon Sep 17 00:00:00 2001 From: marioalexis Date: Thu, 1 Aug 2024 13:05:52 -0300 Subject: [PATCH 3/3] Fem: Handle PropertyError exception --- src/Mod/Fem/femobjects/constraint_bodyheatsource.py | 4 ++-- src/Mod/Fem/femobjects/constraint_sectionprint.py | 3 ++- src/Mod/Fem/femobjects/constraint_tie.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Mod/Fem/femobjects/constraint_bodyheatsource.py b/src/Mod/Fem/femobjects/constraint_bodyheatsource.py index bf3e9c3957..b7c62ca256 100644 --- a/src/Mod/Fem/femobjects/constraint_bodyheatsource.py +++ b/src/Mod/Fem/femobjects/constraint_bodyheatsource.py @@ -86,7 +86,7 @@ class ConstraintBodyHeatSource(base_fempythonobject.BaseFemPythonObject): for prop in self._get_properties(): try: obj.getPropertyByName(prop.name) - except: + except FreeCAD.Base.PropertyError: prop.add_to_object(obj) # migrate old HeatSource property @@ -96,5 +96,5 @@ class ConstraintBodyHeatSource(base_fempythonobject.BaseFemPythonObject): obj.Mode = "Dissipation Rate" obj.setPropertyStatus("HeatSource", "-LockDynamic") obj.removeProperty("HeatSource") - except: + except FreeCAD.Base.PropertyError: pass diff --git a/src/Mod/Fem/femobjects/constraint_sectionprint.py b/src/Mod/Fem/femobjects/constraint_sectionprint.py index fd59b3c9cf..df83751c3d 100644 --- a/src/Mod/Fem/femobjects/constraint_sectionprint.py +++ b/src/Mod/Fem/femobjects/constraint_sectionprint.py @@ -29,6 +29,7 @@ __url__ = "https://www.freecad.org" # \ingroup FEM # \brief constraint section print object +from FreeCAD import Base from . import base_fempythonobject _PropHelper = base_fempythonobject._PropHelper @@ -67,5 +68,5 @@ class ConstraintSectionPrint(base_fempythonobject.BaseFemPythonObject): for prop in self._get_properties(): try: obj.getPropertyByName(prop.name) - except: + except Base.PropertyError: prop.add_to_object(obj) diff --git a/src/Mod/Fem/femobjects/constraint_tie.py b/src/Mod/Fem/femobjects/constraint_tie.py index e8b1707a88..c8945fce12 100644 --- a/src/Mod/Fem/femobjects/constraint_tie.py +++ b/src/Mod/Fem/femobjects/constraint_tie.py @@ -114,5 +114,5 @@ class ConstraintTie(base_fempythonobject.BaseFemPythonObject): for prop in self._get_properties(): try: obj.getPropertyByName(prop.name) - except: + except FreeCAD.Base.PropertyError: prop.add_to_object(obj)