287 lines
12 KiB
C++
287 lines
12 KiB
C++
/***************************************************************************
|
|
* Copyright (c) Jürgen Riegel (juergen.riegel@web.de) 2002 *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library 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 Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
|
|
#include "PreCompiled.h"
|
|
|
|
#ifndef _PreComp_
|
|
# include <sstream>
|
|
# include <stdlib.h>
|
|
#endif
|
|
|
|
#include "PyObjectBase.h"
|
|
#include "Console.h"
|
|
|
|
using namespace Base;
|
|
|
|
PyObject* Base::BaseExceptionFreeCADError = 0;
|
|
|
|
// Constructor
|
|
PyObjectBase::PyObjectBase(void* p,PyTypeObject *T)
|
|
: _pcTwinPointer(p), parent(0), attribute(0)
|
|
{
|
|
this->ob_type = T;
|
|
_Py_NewReference(this);
|
|
#ifdef FC_LOGPYOBJECTS
|
|
Base::Console().Log("PyO+: %s (%p)\n",T->tp_name, this);
|
|
#endif
|
|
StatusBits.set(0); // valid, the second bit is NOT set, i.e. it's mutable
|
|
}
|
|
|
|
/// destructor
|
|
PyObjectBase::~PyObjectBase()
|
|
{
|
|
#ifdef FC_LOGPYOBJECTS
|
|
Base::Console().Log("PyO-: %s (%p)\n",this->ob_type->tp_name, this);
|
|
#endif
|
|
if (this->parent)
|
|
this->parent->DecRef();
|
|
if (this->attribute)
|
|
free(this->attribute); /* it's a strdup */
|
|
}
|
|
|
|
/*------------------------------
|
|
* PyObjectBase Type -- Every class, even the abstract one should have a Type
|
|
------------------------------*/
|
|
|
|
/** \brief
|
|
* To prevent subclasses of PyTypeObject to be subclassed in Python we should remove
|
|
* the Py_TPFLAGS_BASETYPE flag. For example, the classes App::VectorPy and App::MatrixPy
|
|
* have removed this flag and its Python proxies App.Vector and App.Matrix cannot be subclassed.
|
|
* In case we want to allow to derive from subclasses of PyTypeObject in Python
|
|
* we must either reimplment tp_new, tp_dealloc, tp_getattr, tp_setattr, tp_repr or set them to
|
|
* 0 and define tp_base as 0.
|
|
*/
|
|
|
|
PyTypeObject PyObjectBase::Type = {
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
0, /*ob_size*/
|
|
"PyObjectBase", /*tp_name*/
|
|
sizeof(PyObjectBase), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
/* --- methods ---------------------------------------------- */
|
|
PyDestructor, /*tp_dealloc*/
|
|
0, /*tp_print*/
|
|
__getattr, /*tp_getattr*/
|
|
__setattr, /*tp_setattr*/
|
|
0, /*tp_compare*/
|
|
__repr, /*tp_repr*/
|
|
0, /*tp_as_number*/
|
|
0, /*tp_as_sequence*/
|
|
0, /*tp_as_mapping*/
|
|
0, /*tp_hash*/
|
|
0, /*tp_call */
|
|
0, /*tp_str */
|
|
0, /*tp_getattro*/
|
|
0, /*tp_setattro*/
|
|
/* --- Functions to access object as input/output buffer ---------*/
|
|
0, /* tp_as_buffer */
|
|
/* --- Flags to define presence of optional/expanded features */
|
|
Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_CLASS, /*tp_flags */
|
|
"The most base class for Python binding", /*tp_doc */
|
|
0, /*tp_traverse */
|
|
0, /*tp_clear */
|
|
0, /*tp_richcompare */
|
|
0, /*tp_weaklistoffset */
|
|
0, /*tp_iter */
|
|
0, /*tp_iternext */
|
|
0, /*tp_methods */
|
|
0, /*tp_members */
|
|
0, /*tp_getset */
|
|
0, /*tp_base */
|
|
0, /*tp_dict */
|
|
0, /*tp_descr_get */
|
|
0, /*tp_descr_set */
|
|
0, /*tp_dictoffset */
|
|
0, /*tp_init */
|
|
0, /*tp_alloc */
|
|
0, /*tp_new */
|
|
0, /*tp_free Low-level free-memory routine */
|
|
0, /*tp_is_gc For PyObject_IS_GC */
|
|
0, /*tp_bases */
|
|
0, /*tp_mro method resolution order */
|
|
0, /*tp_cache */
|
|
0, /*tp_subclasses */
|
|
0, /*tp_weaklist */
|
|
0, /*tp_del */
|
|
0 /*tp_version_tag */
|
|
};
|
|
|
|
/*------------------------------
|
|
* PyObjectBase Methods -- Every class, even the abstract one should have a Methods
|
|
------------------------------*/
|
|
PyMethodDef PyObjectBase::Methods[] = {
|
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
|
};
|
|
|
|
PyObject* PyObjectBase::__getattr(PyObject * obj, char *attr)
|
|
{
|
|
// This should be the entry in Type
|
|
PyObjectBase* pyObj = static_cast<PyObjectBase*>(obj);
|
|
if (!pyObj->isValid()){
|
|
PyErr_Format(PyExc_ReferenceError, "Cannot access attribute '%s' of deleted object", attr);
|
|
return NULL;
|
|
}
|
|
|
|
PyObject* value = pyObj->_getattr(attr);
|
|
#if 1
|
|
if (value && PyObject_TypeCheck(value, &(PyObjectBase::Type))) {
|
|
if (!static_cast<PyObjectBase*>(value)->isConst())
|
|
static_cast<PyObjectBase*>(value)->setAttributeOf(attr, pyObj);
|
|
}
|
|
#endif
|
|
return value;
|
|
}
|
|
|
|
int PyObjectBase::__setattr(PyObject *obj, char *attr, PyObject *value)
|
|
{
|
|
//FIXME: In general we don't allow to delete attributes (i.e. value=0). However, if we want to allow
|
|
//we must check then in _setattr() of all subclasses whether value is 0.
|
|
if ( value==0 ) {
|
|
PyErr_Format(PyExc_AttributeError, "Cannot delete attribute: '%s'", attr);
|
|
return -1;
|
|
}
|
|
else if (!static_cast<PyObjectBase*>(obj)->isValid()){
|
|
PyErr_Format(PyExc_ReferenceError, "Cannot access attribute '%s' of deleted object", attr);
|
|
return -1;
|
|
}
|
|
|
|
int ret = static_cast<PyObjectBase*>(obj)->_setattr(attr, value);
|
|
#if 1
|
|
if (ret == 0) {
|
|
static_cast<PyObjectBase*>(obj)->startNotify();
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/*------------------------------
|
|
* PyObjectBase attributes -- attributes
|
|
------------------------------*/
|
|
PyObject *PyObjectBase::_getattr(char *attr)
|
|
{
|
|
if (streq(attr, "__class__")) {
|
|
// Note: We must return the type object here,
|
|
// so that our own types feel as really Python objects
|
|
Py_INCREF(this->ob_type);
|
|
return (PyObject *)(this->ob_type);
|
|
}
|
|
else if (streq(attr, "__members__")) {
|
|
// Use __dict__ instead as __members__ is deprecated
|
|
return NULL;
|
|
}
|
|
else if (streq(attr,"__dict__")) {
|
|
// Return the default dict
|
|
PyTypeObject *tp = this->ob_type;
|
|
Py_XINCREF(tp->tp_dict);
|
|
return tp->tp_dict;
|
|
}
|
|
else if (streq(attr,"softspace")) {
|
|
// Internal Python stuff
|
|
return NULL;
|
|
}
|
|
else {
|
|
// As fallback solution use Python's default method to get generic attributes
|
|
PyObject *w, *res;
|
|
w = PyString_InternFromString(attr);
|
|
if (w != NULL) {
|
|
res = PyObject_GenericGetAttr(this, w);
|
|
Py_XDECREF(w);
|
|
return res;
|
|
} else {
|
|
// Throw an exception for unknown attributes
|
|
PyTypeObject *tp = this->ob_type;
|
|
PyErr_Format(PyExc_AttributeError, "%.50s instance has no attribute '%.400s'", tp->tp_name, attr);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int PyObjectBase::_setattr(char *attr, PyObject *value)
|
|
{
|
|
if (streq(attr,"softspace"))
|
|
return -1; // filter out softspace
|
|
PyObject *w;
|
|
// As fallback solution use Python's default method to get generic attributes
|
|
w = PyString_InternFromString(attr); // new reference
|
|
if (w != NULL) {
|
|
// call methods from tp_getset if defined
|
|
int res = PyObject_GenericSetAttr(this, w, value);
|
|
Py_DECREF(w);
|
|
return res;
|
|
} else {
|
|
// Throw an exception for unknown attributes
|
|
PyTypeObject *tp = this->ob_type;
|
|
PyErr_Format(PyExc_AttributeError, "%.50s instance has no attribute '%.400s'", tp->tp_name, attr);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*------------------------------
|
|
* PyObjectBase repr representations
|
|
------------------------------*/
|
|
PyObject *PyObjectBase::_repr(void)
|
|
{
|
|
std::stringstream a;
|
|
a << "<base object at " << _pcTwinPointer << ">";
|
|
# ifdef FCDebug
|
|
Console().Log("PyObjectBase::_repr() not overwritten representation!");
|
|
# endif
|
|
return Py_BuildValue("s", a.str().c_str());
|
|
}
|
|
|
|
void PyObjectBase::setAttributeOf(const char* attr, const PyObjectBase* par)
|
|
{
|
|
if (this->parent != par) {
|
|
Py_XDECREF(this->parent);
|
|
this->parent = const_cast<PyObjectBase*>(par);
|
|
Py_XINCREF(this->parent);
|
|
}
|
|
|
|
if (this->attribute) {
|
|
if (strcmp(this->attribute, attr) != 0) {
|
|
free(this->attribute);
|
|
#if defined (__GNUC__)
|
|
this->attribute = strdup(attr);
|
|
#else
|
|
this->attribute = _strdup(attr);
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
#if defined (__GNUC__)
|
|
this->attribute = strdup(attr);
|
|
#else
|
|
this->attribute = _strdup(attr);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void PyObjectBase::startNotify()
|
|
{
|
|
if (this->attribute && this->parent) {
|
|
__setattr(this->parent, this->attribute, this);
|
|
if (PyErr_Occurred())
|
|
PyErr_Clear();
|
|
}
|
|
}
|