diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index a82dd77b2d..3b5c9e159a 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -243,6 +243,7 @@ SET(FreeCADBase_CPP_SRCS ProgressIndicatorPy.cpp PyExport.cpp PyObjectBase.cpp + PythonTypeExt.cpp QtTools.cpp Reader.cpp Rotation.cpp @@ -308,6 +309,7 @@ SET(FreeCADBase_HPP_SRCS ProgressIndicatorPy.h PyExport.h PyObjectBase.h + PythonTypeExt.h QtTools.h Reader.h Rotation.h diff --git a/src/Base/PythonTypeExt.cpp b/src/Base/PythonTypeExt.cpp new file mode 100644 index 0000000000..280717e421 --- /dev/null +++ b/src/Base/PythonTypeExt.cpp @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2023 Werner Mayer * + * Copyright (c) 2023 Mario Passaglia * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include + +#include + + +using namespace Base; + +PythonTypeExt::PythonTypeExt(Py::PythonType& type) + : pytype(type) +{ +} + +Py::PythonType& PythonTypeExt::set_tp_descr_get(PyObject* (*tp_descr_get)(PyObject* self, PyObject* obj, PyObject* type)) +{ + pytype.type_object()->tp_descr_get = tp_descr_get; + + return pytype; +} + +Py::PythonType& PythonTypeExt::set_tp_descr_set(int (*tp_descr_set)(PyObject* self, PyObject* obj, PyObject* value)) +{ + pytype.type_object()->tp_descr_set = tp_descr_set; + + return pytype; +} diff --git a/src/Base/PythonTypeExt.h b/src/Base/PythonTypeExt.h new file mode 100644 index 0000000000..a1f49cec81 --- /dev/null +++ b/src/Base/PythonTypeExt.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2023 Werner Mayer * + * Copyright (c) 2023 Mario Passaglia * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef BASE_PYTHONTYPEEXT_H +#define BASE_PYTHONTYPEEXT_H + +#include + +namespace Py +{ + class PythonType; +} + +using PyObject = struct _object; + +namespace Base +{ + +/// class to extend PyCXX PythonType class +class BaseExport PythonTypeExt +{ +public: + PythonTypeExt(Py::PythonType& type); + + Py::PythonType& set_tp_descr_get(PyObject* (*tp_descr_get)(PyObject* self, PyObject* obj, PyObject* type)); + Py::PythonType& set_tp_descr_set(int (*tp_descr_set)(PyObject* self, PyObject* obj, PyObject* value)); + +private: + Py::PythonType& pytype; +}; + +} // namespace Base + +#endif // BASE_PYTHONTYPEEXT_H diff --git a/src/CXX/Python3/ExtensionType.hxx b/src/CXX/Python3/ExtensionType.hxx index 74dd2e17fb..2f8fad57a7 100644 --- a/src/CXX/Python3/ExtensionType.hxx +++ b/src/CXX/Python3/ExtensionType.hxx @@ -96,7 +96,7 @@ // need to support METH_STATIC and METH_CLASS #define PYCXX_ADD_NOARGS_METHOD( PYNAME, NAME, docs ) \ - add_method( #PYNAME, (PyCFunction)PYCXX_NOARGS_METHOD_NAME( NAME ), METH_NOARGS, docs ) + add_method( #PYNAME, (PyCFunction)(void (*) (void))PYCXX_NOARGS_METHOD_NAME( NAME ), METH_NOARGS, docs ) #define PYCXX_ADD_VARARGS_METHOD( PYNAME, NAME, docs ) \ add_method( #PYNAME, (PyCFunction)(void (*) (void))PYCXX_VARARGS_METHOD_NAME( NAME ), METH_VARARGS, docs ) #define PYCXX_ADD_KEYWORDS_METHOD( PYNAME, NAME, docs ) \ diff --git a/src/Tools/generateBase/generateMetaModel_Module.xsd b/src/Tools/generateBase/generateMetaModel_Module.xsd index eb641cfbb5..725b1a151f 100644 --- a/src/Tools/generateBase/generateMetaModel_Module.xsd +++ b/src/Tools/generateBase/generateMetaModel_Module.xsd @@ -66,6 +66,8 @@ + + diff --git a/src/Tools/generateBase/generateModel_Module.py b/src/Tools/generateBase/generateModel_Module.py index bcb469c073..217916845c 100644 --- a/src/Tools/generateBase/generateModel_Module.py +++ b/src/Tools/generateBase/generateModel_Module.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -# Generated Mon Mar 6 17:37:05 2023 by generateDS.py. +# Generated Fri Mar 31 16:59:53 2023 by generateDS.py. # Update it with: python generateDS.py -o generateModel_Module.py generateMetaModel_Module.xsd # # WARNING! All changes made in this file will be lost! @@ -220,7 +220,7 @@ class GenerateModel: class PythonExport: subclass = None - def __init__(self, Name='', PythonName='', Include='', Father='', Twin='', Namespace='', FatherInclude='', FatherNamespace='', Constructor=0, NumberProtocol=0, RichCompare=0, TwinPointer='', Delete=0, Reference=0, Initialization=0, DisableNotify=0, Documentation=None, Methode=None, Attribute=None, Sequence=None, CustomAttributes='', ClassDeclarations='', ForwardDeclarations=''): + def __init__(self, Name='', PythonName='', Include='', Father='', Twin='', Namespace='', FatherInclude='', FatherNamespace='', Constructor=0, NumberProtocol=0, RichCompare=0, TwinPointer='', Delete=0, Reference=0, Initialization=0, DisableNotify=0, DescriptorGetter=0, DescriptorSetter=0, Documentation=None, Methode=None, Attribute=None, Sequence=None, CustomAttributes='', ClassDeclarations='', ForwardDeclarations=''): self.Name = Name self.PythonName = PythonName self.Include = Include @@ -237,6 +237,8 @@ class PythonExport: self.Reference = Reference self.Initialization = Initialization self.DisableNotify = DisableNotify + self.DescriptorGetter = DescriptorGetter + self.DescriptorSetter = DescriptorSetter self.Documentation = Documentation if Methode is None: self.Methode = [] @@ -306,6 +308,10 @@ class PythonExport: def setInitialization(self, Initialization): self.Initialization = Initialization def getDisablenotify(self): return self.DisableNotify def setDisablenotify(self, DisableNotify): self.DisableNotify = DisableNotify + def getDescriptorgetter(self): return self.DescriptorGetter + def setDescriptorgetter(self, DescriptorGetter): self.DescriptorGetter = DescriptorGetter + def getDescriptorsetter(self): return self.DescriptorSetter + def setDescriptorsetter(self, DescriptorSetter): self.DescriptorSetter = DescriptorSetter def export(self, outfile, level, name_='PythonExport'): showIndent(outfile, level) outfile.write('<%s' % (name_, )) @@ -339,6 +345,10 @@ class PythonExport: outfile.write(' Initialization="%s"' % (self.getInitialization(), )) if self.getDisablenotify() is not None: outfile.write(' DisableNotify="%s"' % (self.getDisablenotify(), )) + if self.getDescriptorgetter() is not None: + outfile.write(' DescriptorGetter="%s"' % (self.getDescriptorgetter(), )) + if self.getDescriptorsetter() is not None: + outfile.write(' DescriptorSetter="%s"' % (self.getDescriptorsetter(), )) def exportChildren(self, outfile, level, name_='PythonExport'): if self.Documentation: self.Documentation.export(outfile, level) @@ -391,6 +401,10 @@ class PythonExport: outfile.write('Initialization = "%s",\n' % (self.getInitialization(),)) showIndent(outfile, level) outfile.write('DisableNotify = "%s",\n' % (self.getDisablenotify(),)) + showIndent(outfile, level) + outfile.write('DescriptorGetter = "%s",\n' % (self.getDescriptorgetter(),)) + showIndent(outfile, level) + outfile.write('DescriptorSetter = "%s",\n' % (self.getDescriptorsetter(),)) def exportLiteralChildren(self, outfile, level, name_): if self.Documentation: showIndent(outfile, level) @@ -508,6 +522,20 @@ class PythonExport: self.DisableNotify = 0 else: raise ValueError('Bad boolean attribute (DisableNotify)') + if attrs.get('DescriptorGetter'): + if attrs.get('DescriptorGetter').value in ('true', '1'): + self.DescriptorGetter = 1 + elif attrs.get('DescriptorGetter').value in ('false', '0'): + self.DescriptorGetter = 0 + else: + raise ValueError('Bad boolean attribute (DescriptorGetter)') + if attrs.get('DescriptorSetter'): + if attrs.get('DescriptorSetter').value in ('true', '1'): + self.DescriptorSetter = 1 + elif attrs.get('DescriptorSetter').value in ('false', '0'): + self.DescriptorSetter = 0 + else: + raise ValueError('Bad boolean attribute (DescriptorSetter)') def buildChildren(self, child_, nodeName_): if child_.nodeType == Node.ELEMENT_NODE and \ nodeName_ == 'Documentation': @@ -1950,6 +1978,22 @@ class SaxGeneratemodelHandler(handler.ContentHandler): obj.setDisablenotify(0) else: self.reportError('"DisableNotify" attribute must be boolean ("true", "1", "false", "0")') + val = attrs.get('DescriptorGetter', None) + if val is not None: + if val in ('true', '1'): + obj.setDescriptorgetter(1) + elif val in ('false', '0'): + obj.setDescriptorgetter(0) + else: + self.reportError('"DescriptorGetter" attribute must be boolean ("true", "1", "false", "0")') + val = attrs.get('DescriptorSetter', None) + if val is not None: + if val in ('true', '1'): + obj.setDescriptorsetter(1) + elif val in ('false', '0'): + obj.setDescriptorsetter(0) + else: + self.reportError('"DescriptorSetter" attribute must be boolean ("true", "1", "false", "0")') stackObj = SaxStackElement('PythonExport', obj) self.stack.append(stackObj) done = 1 diff --git a/src/Tools/generateTemplates/templateClassPyExport.py b/src/Tools/generateTemplates/templateClassPyExport.py index 810cfa75e2..2666555d73 100644 --- a/src/Tools/generateTemplates/templateClassPyExport.py +++ b/src/Tools/generateTemplates/templateClassPyExport.py @@ -70,6 +70,12 @@ public: - + if (self.export.RichCompare): static PyObject * richCompare(PyObject *v, PyObject *w, int op); +- ++ if (self.export.DescriptorGetter): + static PyObject* descriptorGetter(PyObject* self, PyObject* obj, PyObject* type); +- ++ if (self.export.DescriptorSetter): + static int descriptorSetter(PyObject* self, PyObject* obj, PyObject* value); - static PyGetSetDef GetterSetter[]; virtual PyTypeObject *GetType() {return &Type;} @@ -325,8 +331,16 @@ PyTypeObject @self.export.Name@::Type = { @self.export.Namespace@::@self.export.Name@::GetterSetter, /*tp_getset */ &@self.export.FatherNamespace@::@self.export.Father@::Type, /*tp_base */ nullptr, /*tp_dict */ ++ if (self.export.DescriptorGetter): + @self.export.Namespace@::@self.export.Name@::descriptorGetter, /*tp_descr_get */ += else: nullptr, /*tp_descr_get */ +- ++ if (self.export.DescriptorSetter): + @self.export.Namespace@::@self.export.Name@::descriptorSetter, /*tp_descr_set */ += else: nullptr, /*tp_descr_set */ +- 0, /*tp_dictoffset */ __PyInit, /*tp_init */ nullptr, /*tp_alloc */ @@ -1125,6 +1139,22 @@ int @self.export.Name@::setCustomAttributes(const char* /*attr*/, PyObject* /*ob return 0; } - + ++ if (self.export.DescriptorGetter): +PyObject* @self.export.Name@::descriptorGetter(PyObject* self, PyObject* obj, PyObject* type) +{ + PyErr_SetString(PyExc_NotImplementedError, "Not yet implemented"); + return nullptr; +} +- + ++ if (self.export.DescriptorSetter): +int @self.export.Name@::descriptorSetter(PyObject* self, PyObject* obj, PyObject* value) +{ + PyErr_SetString(PyExc_NotImplementedError, "Not yet implemented"); + return -1; +} +- #endif @@ -1453,4 +1483,20 @@ int @self.export.Name@::setCustomAttributes(const char* /*attr*/, PyObject* /*ob } - ++ if (self.export.DescriptorGetter): +PyObject* @self.export.Name@::descriptorGetter(PyObject* self, PyObject* obj, PyObject* type) +{ + PyErr_SetString(PyExc_NotImplementedError, "Not yet implemented"); + return nullptr; +} +- + ++ if (self.export.DescriptorSetter): +int @self.export.Name@::descriptorSetter(PyObject* self, PyObject* obj, PyObject* value) +{ + PyErr_SetString(PyExc_NotImplementedError, "Not yet implemented"); + return -1; +} +- + """