src: Add descriptor protocol support for Python C++ extension modules

This commit is contained in:
marioalexis
2023-04-03 00:56:38 -03:00
committed by wwmayer
parent b02b879dd5
commit 6cb9bdd23a
7 changed files with 201 additions and 3 deletions

View File

@@ -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

View 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
View 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

View File

@@ -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 ) \

View File

@@ -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>

View File

@@ -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

View File

@@ -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;
}
-
"""