1
/***************************************************************************
2
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
24
#include "PreCompiled.h"
30
#include "PyObjectBase.h"
32
#include "Interpreter.h"
40
PyObject* Base::PyExc_FC_GeneralError = nullptr;
41
PyObject* Base::PyExc_FC_FreeCADAbort = nullptr;
42
PyObject* Base::PyExc_FC_XMLBaseException = nullptr;
43
PyObject* Base::PyExc_FC_XMLParseException = nullptr;
44
PyObject* Base::PyExc_FC_XMLAttributeError = nullptr;
45
PyObject* Base::PyExc_FC_UnknownProgramOption = nullptr;
46
PyObject* Base::PyExc_FC_BadFormatError = nullptr;
47
PyObject* Base::PyExc_FC_BadGraphError = nullptr;
48
PyObject* Base::PyExc_FC_ExpressionError = nullptr;
49
PyObject* Base::PyExc_FC_ParserError = nullptr;
50
PyObject* Base::PyExc_FC_CADKernelError = nullptr;
51
PyObject* Base::PyExc_FC_PropertyError = nullptr;
53
typedef struct { //NOLINT
56
PyObject* weakreflist; /* List of weak references */
60
PyObjectBase::PyObjectBase(void* voidp, PyTypeObject *T)
61
: _pcTwinPointer(voidp)
63
#if PY_VERSION_HEX < 0x030b0000
68
_Py_NewReference(this);
70
Base::Console().Log("PyO+: %s (%p)\n",T->tp_name, this);
72
StatusBits.set(Valid); // valid, the second bit is NOT set, i.e. it's mutable
73
StatusBits.set(Notify);
77
PyObjectBase::~PyObjectBase()
79
PyGILStateLocker lock;
81
Base::Console().Log("PyO-: %s (%p)\n",Py_TYPE(this)->tp_name, this);
83
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
84
if (baseProxy && reinterpret_cast<PyBaseProxy*>(baseProxy)->baseobject == this) {
90
/*------------------------------
91
* PyObjectBase Type -- Every class, even the abstract one should have a Type
92
------------------------------*/
95
* To prevent subclasses of PyTypeObject to be subclassed in Python we should remove
96
* the Py_TPFLAGS_BASETYPE flag. For example, the classes App::VectorPy and App::MatrixPy
97
* have removed this flag and its Python proxies App.Vector and App.Matrix cannot be subclassed.
98
* In case we want to allow to derive from subclasses of PyTypeObject in Python
99
* we must either reimplement tp_new, tp_dealloc, tp_getattr, tp_setattr, tp_repr or set them to
100
* 0 and define tp_base as 0.
103
#if defined(__clang__)
104
# pragma clang diagnostic push
105
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
109
PyBaseProxy_dealloc(PyObject* self)
111
/* Clear weakrefs first before calling any destructors */
112
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
113
if (reinterpret_cast<PyBaseProxy*>(self)->weakreflist) {
114
PyObject_ClearWeakRefs(self);
116
Py_TYPE(self)->tp_free(self);
119
static PyTypeObject PyBaseProxyType = {
120
PyVarObject_HEAD_INIT(nullptr, 0)
121
"PyBaseProxy", /*tp_name*/
122
sizeof(PyBaseProxy), /*tp_basicsize*/
124
PyBaseProxy_dealloc, /*tp_dealloc*/
125
#if PY_VERSION_HEX >= 0x03080000
126
0, /*tp_vectorcall_offset*/
128
nullptr, /*tp_print*/
130
nullptr, /*tp_getattr*/
131
nullptr, /*tp_setattr*/
132
nullptr, /*tp_compare*/
134
nullptr, /*tp_as_number*/
135
nullptr, /*tp_as_sequence*/
136
nullptr, /*tp_as_mapping*/
138
nullptr, /*tp_call */
140
nullptr, /*tp_getattro*/
141
nullptr, /*tp_setattro*/
142
nullptr, /*tp_as_buffer*/
143
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DEFAULT, /*tp_flags */
144
"Proxy class", /*tp_doc */
145
nullptr, /*tp_traverse */
146
nullptr, /*tp_clear */
147
nullptr, /*tp_richcompare */
148
offsetof(PyBaseProxy, weakreflist), /*tp_weaklistoffset */
149
nullptr, /*tp_iter */
150
nullptr, /*tp_iternext */
151
nullptr, /*tp_methods */
152
nullptr, /*tp_members */
153
nullptr, /*tp_getset */
154
nullptr, /*tp_base */
155
nullptr, /*tp_dict */
156
nullptr, /*tp_descr_get */
157
nullptr, /*tp_descr_set */
158
0, /*tp_dictoffset */
159
nullptr, /*tp_init */
160
nullptr, /*tp_alloc */
162
nullptr, /*tp_free Low-level free-memory routine */
163
nullptr, /*tp_is_gc For PyObject_IS_GC */
164
nullptr, /*tp_bases */
165
nullptr, /*tp_mro method resolution order */
166
nullptr, /*tp_cache */
167
nullptr, /*tp_subclasses */
168
nullptr, /*tp_weaklist */
170
0, /*tp_version_tag */
171
nullptr /*tp_finalize */
172
#if PY_VERSION_HEX >= 0x03090000
173
,0 //NOLINT /*tp_vectorcall */
174
#if PY_VERSION_HEX >= 0x030c0000
177
#elif PY_VERSION_HEX >= 0x03080000
178
,0 /*tp_vectorcall */
179
/* bpo-37250: kept for backwards compatibility in CPython 3.8 only */
184
PyTypeObject PyObjectBase::Type = {
185
PyVarObject_HEAD_INIT(&PyType_Type,0)
186
"PyObjectBase", /*tp_name*/
187
sizeof(PyObjectBase), /*tp_basicsize*/
189
/* --- methods ---------------------------------------------- */
190
PyDestructor, /*tp_dealloc*/
191
#if PY_VERSION_HEX >= 0x03080000
192
0, /*tp_vectorcall_offset*/
194
nullptr, /*tp_print*/
196
nullptr, /*tp_getattr*/
197
nullptr, /*tp_setattr*/
198
nullptr, /*tp_compare*/
200
nullptr, /*tp_as_number*/
201
nullptr, /*tp_as_sequence*/
202
nullptr, /*tp_as_mapping*/
204
nullptr, /*tp_call */
206
__getattro, /*tp_getattro*/
207
__setattro, /*tp_setattro*/
208
/* --- Functions to access object as input/output buffer ---------*/
209
nullptr, /* tp_as_buffer */
210
/* --- Flags to define presence of optional/expanded features */
211
Py_TPFLAGS_BASETYPE|Py_TPFLAGS_DEFAULT, /*tp_flags */
212
"The most base class for Python binding", /*tp_doc */
213
nullptr, /*tp_traverse */
214
nullptr, /*tp_clear */
215
nullptr, /*tp_richcompare */
216
0, /*tp_weaklistoffset */
217
nullptr, /*tp_iter */
218
nullptr, /*tp_iternext */
219
nullptr, /*tp_methods */
220
nullptr, /*tp_members */
221
nullptr, /*tp_getset */
222
nullptr, /*tp_base */
223
nullptr, /*tp_dict */
224
nullptr, /*tp_descr_get */
225
nullptr, /*tp_descr_set */
226
0, /*tp_dictoffset */
227
nullptr, /*tp_init */
228
nullptr, /*tp_alloc */
230
nullptr, /*tp_free Low-level free-memory routine */
231
nullptr, /*tp_is_gc For PyObject_IS_GC */
232
nullptr, /*tp_bases */
233
nullptr, /*tp_mro method resolution order */
234
nullptr, /*tp_cache */
235
nullptr, /*tp_subclasses */
236
nullptr, /*tp_weaklist */
238
0, /*tp_version_tag */
239
nullptr /*tp_finalize */
240
#if PY_VERSION_HEX >= 0x03090000
241
,0 //NOLINT /*tp_vectorcall */
242
#if PY_VERSION_HEX >= 0x030c0000
245
#elif PY_VERSION_HEX >= 0x03080000
246
,0 /*tp_vectorcall */
247
/* bpo-37250: kept for backwards compatibility in CPython 3.8 only */
252
#if defined(__clang__)
253
# pragma clang diagnostic pop
256
PyObject* createWeakRef(PyObjectBase* ptr)
258
static bool init = false;
261
if (PyType_Ready(&PyBaseProxyType) < 0) {
266
PyObject* proxy = ptr->baseProxy;
268
proxy = PyType_GenericAlloc(&PyBaseProxyType, 0);
269
ptr->baseProxy = proxy;
270
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
271
reinterpret_cast<PyBaseProxy*>(proxy)->baseobject = ptr;
274
PyObject* ref = PyWeakref_NewRef(proxy, nullptr);
278
PyObjectBase* getFromWeakRef(PyObject* ref)
281
PyObject* proxy = PyWeakref_GetObject(ref);
282
if (proxy && PyObject_TypeCheck(proxy, &PyBaseProxyType)) {
283
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
284
return static_cast<PyObjectBase*>(reinterpret_cast<PyBaseProxy*>(proxy)->baseobject);
291
/*------------------------------
292
* PyObjectBase Methods -- Every class, even the abstract one should have a Methods
293
------------------------------*/
294
PyMethodDef PyObjectBase::Methods[] = {
295
{nullptr, nullptr, 0, nullptr} /* Sentinel */
298
PyObject* PyObjectBase::__getattro(PyObject * obj, PyObject *attro)
301
attr = PyUnicode_AsUTF8(attro);
303
// For the __class__ attribute get it directly as with
304
// ExtensionContainerPy::getCustomAttributes we may get
305
// the wrong type object (#0003311)
306
if (streq(attr, "__class__")) {
307
PyObject* res = PyObject_GenericGetAttr(obj, attro);
313
// This should be the entry in Type
314
PyObjectBase* pyObj = static_cast<PyObjectBase*>(obj);
315
if (!pyObj->isValid()){
316
PyErr_Format(PyExc_ReferenceError, "Cannot access attribute '%s' of deleted object", attr);
321
// If an attribute references this as parent then reset it (bug #0002902)
322
PyObject* cur = pyObj->getTrackedAttribute(attr);
324
if (PyObject_TypeCheck(cur, &(PyObjectBase::Type))) {
325
PyObjectBase* base = static_cast<PyObjectBase*>(cur);
326
base->resetAttribute();
327
pyObj->untrackAttribute(attr);
332
PyObject* value = pyObj->_getattr(attr);
334
if (value && PyObject_TypeCheck(value, &(PyObjectBase::Type))) {
335
if (!static_cast<PyObjectBase*>(value)->isConst() &&
336
!static_cast<PyObjectBase*>(value)->isNotTracking()) {
337
static_cast<PyObjectBase*>(value)->setAttributeOf(attr, pyObj);
338
pyObj->trackAttribute(attr, value);
343
if (value && PyCFunction_Check(value)) {
344
// ExtensionContainerPy::initialization() transfers the methods of an
345
// extension object by creating PyCFunction objects.
346
// At this point no 'self' object is passed but is handled and determined
347
// in ExtensionContainerPy::getCustomAttributes().
348
// So, if we come through this section then it's an indication that
349
// something is wrong with the Python types. For example, a C++ class
350
// that adds an extension uses the same Python type as a wrapper than
351
// another C++ class without this extension.
352
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
353
PyCFunctionObject* cfunc = reinterpret_cast<PyCFunctionObject*>(value);
354
if (!cfunc->m_self) {
357
PyErr_Format(PyExc_AttributeError, "<no object bound to built-in method %s>", attr);
364
int PyObjectBase::__setattro(PyObject *obj, PyObject *attro, PyObject *value)
367
attr = PyUnicode_AsUTF8(attro);
369
//Hint: In general we don't allow to delete attributes (i.e. value=0). However, if we want to allow
370
//we must check then in _setattr() of all subclasses whether value is 0.
372
PyErr_Format(PyExc_AttributeError, "Cannot delete attribute: '%s'", attr);
375
if (!static_cast<PyObjectBase*>(obj)->isValid()){
376
PyErr_Format(PyExc_ReferenceError, "Cannot access attribute '%s' of deleted object", attr);
381
// If an attribute references this as parent then reset it
382
// before setting the new attribute
383
PyObject* cur = static_cast<PyObjectBase*>(obj)->getTrackedAttribute(attr);
385
if (PyObject_TypeCheck(cur, &(PyObjectBase::Type))) {
386
PyObjectBase* base = static_cast<PyObjectBase*>(cur);
387
base->resetAttribute();
388
static_cast<PyObjectBase*>(obj)->untrackAttribute(attr);
393
int ret = static_cast<PyObjectBase*>(obj)->_setattr(attr, value);
396
static_cast<PyObjectBase*>(obj)->startNotify();
402
/*------------------------------
403
* PyObjectBase attributes -- attributes
404
------------------------------*/
405
PyObject *PyObjectBase::_getattr(const char *attr)
407
if (streq(attr, "__class__")) {
408
// Note: We must return the type object here,
409
// so that our own types feel as really Python objects
410
Py_INCREF(Py_TYPE(this));
411
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
412
return reinterpret_cast<PyObject *>(Py_TYPE(this));
414
if (streq(attr, "__members__")) {
415
// Use __dict__ instead as __members__ is deprecated
418
if (streq(attr,"__dict__")) {
419
// Return the default dict
420
PyTypeObject *tp = Py_TYPE(this);
421
Py_XINCREF(tp->tp_dict);
424
if (streq(attr,"softspace")) {
425
// Internal Python stuff
428
// As fallback solution use Python's default method to get generic attributes
429
PyObject *w{}, *res{};
430
w = PyUnicode_InternFromString(attr);
432
res = PyObject_GenericGetAttr(this, w);
436
// Throw an exception for unknown attributes
437
PyTypeObject *tp = Py_TYPE(this);
438
PyErr_Format(PyExc_AttributeError, "%.50s instance has no attribute '%.400s'", tp->tp_name, attr);
442
int PyObjectBase::_setattr(const char *attr, PyObject *value)
444
if (streq(attr,"softspace")) {
445
return -1; // filter out softspace
448
// As fallback solution use Python's default method to get generic attributes
449
w = PyUnicode_InternFromString(attr); // new reference
451
// call methods from tp_getset if defined
452
int res = PyObject_GenericSetAttr(this, w, value);
456
// Throw an exception for unknown attributes
457
PyTypeObject *tp = Py_TYPE(this);
458
PyErr_Format(PyExc_AttributeError, "%.50s instance has no attribute '%.400s'", tp->tp_name, attr);
462
/*------------------------------
463
* PyObjectBase repr representations
464
------------------------------*/
465
PyObject *PyObjectBase::_repr()
468
a << "<base object at " << _pcTwinPointer << ">";
470
Console().Log("PyObjectBase::_repr() not overwritten representation!");
472
return Py_BuildValue("s", a.str().c_str());
477
void PyObjectBase::resetAttribute()
480
// This is the attribute name to the parent structure
481
// which we search for in the dict
482
PyObject* key1 = PyBytes_FromString("__attribute_of_parent__");
483
PyObject* key2 = PyBytes_FromString("__instance_of_parent__");
484
PyObject* attr = PyDict_GetItem(attrDict, key1);
485
PyObject* inst = PyDict_GetItem(attrDict, key2);
487
PyDict_DelItem(attrDict, key1);
490
PyDict_DelItem(attrDict, key2);
497
void PyObjectBase::setAttributeOf(const char* attr, PyObject* par)
500
attrDict = PyDict_New();
503
PyObject* key1 = PyBytes_FromString("__attribute_of_parent__");
504
PyObject* key2 = PyBytes_FromString("__instance_of_parent__");
505
PyObject* attro = PyUnicode_FromString(attr);
506
PyDict_SetItem(attrDict, key1, attro);
507
PyDict_SetItem(attrDict, key2, par);
513
void PyObjectBase::startNotify()
515
if (!shouldNotify()) {
520
// This is the attribute name to the parent structure
521
// which we search for in the dict
522
PyObject* key1 = PyBytes_FromString("__attribute_of_parent__");
523
PyObject* key2 = PyBytes_FromString("__instance_of_parent__");
524
PyObject* attr = PyDict_GetItem(attrDict, key1);
525
PyObject* parent = PyDict_GetItem(attrDict, key2);
526
if (attr && parent) {
527
// Inside __setattr of the parent structure the 'attr'
528
// is being removed from the dict and thus its reference
529
// counter will be decremented. To avoid to be deleted we
530
// must tmp. increment it and afterwards decrement it again.
535
__setattro(parent, attr, this);
537
Py_DECREF(parent); // might be destroyed now
538
Py_DECREF(attr); // might be destroyed now
539
Py_DECREF(this); // might be destroyed now
541
if (PyErr_Occurred()) {
550
PyObject* PyObjectBase::getTrackedAttribute(const char* attr)
552
PyObject* obj = nullptr;
554
obj = PyDict_GetItemString(attrDict, attr);
555
obj = getFromWeakRef(obj);
560
void PyObjectBase::trackAttribute(const char* attr, PyObject* obj)
563
attrDict = PyDict_New();
566
PyObject* obj_ref = createWeakRef(static_cast<PyObjectBase*>(obj));
568
PyDict_SetItemString(attrDict, attr, obj_ref);
572
void PyObjectBase::untrackAttribute(const char* attr)
575
PyDict_DelItemString(attrDict, attr);
579
void PyObjectBase::clearAttributes()
582
PyDict_Clear(attrDict);