src: Add descriptor protocol support for Python C++ extension modules
This commit is contained in:
@@ -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
|
||||
|
||||
49
src/Base/PythonTypeExt.cpp
Normal file
49
src/Base/PythonTypeExt.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* Copyright (c) 2023 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
* *
|
||||
* 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 *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#include <CXX/Extensions.hxx>
|
||||
|
||||
#include <Base/PythonTypeExt.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
55
src/Base/PythonTypeExt.h
Normal file
55
src/Base/PythonTypeExt.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* Copyright (c) 2023 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
* *
|
||||
* 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 *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef BASE_PYTHONTYPEEXT_H
|
||||
#define BASE_PYTHONTYPEEXT_H
|
||||
|
||||
#include <FCGlobal.h>
|
||||
|
||||
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
|
||||
@@ -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 ) \
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
<xs:attribute name="Reference" type="xs:boolean" use="optional"/>
|
||||
<xs:attribute name="Initialization" type="xs:boolean" use="optional" default="false"/>
|
||||
<xs:attribute name="DisableNotify" type="xs:boolean" use="optional" default="false"/>
|
||||
<xs:attribute name="DescriptorGetter" type="xs:boolean" use="optional" default="false"/>
|
||||
<xs:attribute name="DescriptorSetter" type="xs:boolean" use="optional" default="false"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
-
|
||||
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user