FreeCAD
1/***************************************************************************
2* Copyright (c) 2016 Stefan Tröger <stefantroeger@gmx.net> *
3* *
4* This file is part of the FreeCAD CAx development system. *
5* *
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. *
10* *
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. *
15* *
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 *
20* *
21***************************************************************************/
22
23
24#include "PreCompiled.h"25
26#ifndef _PreComp_27# include <sstream>28#endif29
30#include "Application.h"31
32#include <App/ExtensionContainerPy.h>33#include <App/ExtensionContainerPy.cpp>34#include <App/Extension.h>35
36using namespace App;37
38// returns a string which represent the object e.g. when printed in python
39std::string ExtensionContainerPy::representation() const40{
41return {"<extension>"};42}
43
44int ExtensionContainerPy::initialization() {45
46if (!this->ob_type->tp_dict) {47if (PyType_Ready(this->ob_type) < 0)48return 0;49}50
51ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin();52for(; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) {53
54// The PyTypeObject is shared by all instances of this type and therefore55// we have to add new methods only once.56PyObject* obj = (*it).second->getExtensionPyObject();57PyMethodDef* meth = obj->ob_type->tp_methods;58PyTypeObject *type = this->ob_type;59PyObject *dict = type->tp_dict;60
61// make sure to do the initialization only once62if (meth->ml_name) {63PyObject* item = PyDict_GetItemString(dict, meth->ml_name);64if (!item) {65// Note: this adds the methods to the type object to make sure66// it appears in the call tips. The function will not be bound67// to an instance68Py_INCREF(dict);69while (meth->ml_name) {70PyObject *func;71func = PyCFunction_New(meth, 0);72if (!func)73break;74if (PyDict_SetItemString(dict, meth->ml_name, func) < 0)75break;76Py_DECREF(func);77++meth;78}79
80Py_DECREF(dict);81}82}83
84Py_DECREF(obj);85}86return 1;87}
88
89int ExtensionContainerPy::finalization() {90/*
91//we need to delete all added python extensions, as we are the owner!
92ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin();
93for(; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) {
94if((*it).second->isPythonExtension())
95delete (*it).second;
96}*/
97return 1;98}
99
100PyObject* ExtensionContainerPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper101{
102// create a new instance of @self.export.Name@ and the Twin object103return nullptr;104}
105
106// constructor method
107int ExtensionContainerPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)108{
109return 0;110}
111
112PyObject *ExtensionContainerPy::getCustomAttributes(const char* attr) const113{
114if (Base::streq(attr, "__dict__")) {115PyObject* dict = PyDict_New();116PyObject* props = PropertyContainerPy::getCustomAttributes("__dict__");117if (props && PyDict_Check(props)) {118PyDict_Merge(dict, props, 0);119Py_DECREF(props);120}121
122ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin();123for (; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) {124// The PyTypeObject is shared by all instances of this type and therefore125// we have to add new methods only once.126PyObject* obj = (*it).second->getExtensionPyObject();127PyTypeObject *tp = Py_TYPE(obj);128if (tp && tp->tp_dict) {129Py_XINCREF(tp->tp_dict);130PyDict_Merge(dict, tp->tp_dict, 0);131Py_XDECREF(tp->tp_dict);132}133Py_DECREF(obj);134}135
136return dict;137}138// Search for the method called 'attr' in the extensions. If the search with139// Py_FindMethod is successful then a PyCFunction_New instance is returned140// with the PyObject pointer of the extension to make sure the method will141// be called for the correct instance.142PyObject *func = nullptr;143ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin();144for (; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) {145// The PyTypeObject is shared by all instances of this type and therefore146// we have to add new methods only once.147PyObject* obj = (*it).second->getExtensionPyObject();148PyObject *nameobj = PyUnicode_FromString(attr);149func = PyObject_GenericGetAttr(obj, nameobj);150Py_DECREF(nameobj);151Py_DECREF(obj);152if (func && PyCFunction_Check(func)) {153PyCFunctionObject* cfunc = reinterpret_cast<PyCFunctionObject*>(func);154
155// OK, that's what we wanted156if (cfunc->m_self == obj)157break;158// otherwise cleanup the result again159Py_DECREF(func);160func = nullptr;161}162PyErr_Clear(); // clear the error set inside Py_FindMethod163}164
165return func;166}
167
168int ExtensionContainerPy::setCustomAttributes(const char* /*attr*/, PyObject * /*obj*/)169{
170return 0;171}
172
173PyObject* ExtensionContainerPy::hasExtension(PyObject *args) {174
175char *type;176PyObject *deriv = Py_True;177if (!PyArg_ParseTuple(args, "s|O!", &type, &PyBool_Type, &deriv))178return nullptr;179
180//get the extension type asked for181bool derived = Base::asBoolean(deriv);182Base::Type extension = Base::Type::fromName(type);183if (extension.isBad() || !extension.isDerivedFrom(App::Extension::getExtensionClassTypeId())) {184std::stringstream str;185str << "No extension found of type '" << type << "'" << std::ends;186throw Py::TypeError(str.str());187}188
189bool val = false;190if (getExtensionContainerPtr()->hasExtension(extension, derived)) {191val = true;192}193
194return PyBool_FromLong(val ? 1 : 0);195}
196
197PyObject* ExtensionContainerPy::addExtension(PyObject *args) {198
199char *typeId;200PyObject* proxy = nullptr;201if (!PyArg_ParseTuple(args, "s|O", &typeId, &proxy))202return nullptr;203
204if (proxy) {205PyErr_SetString(PyExc_DeprecationWarning, "Second argument is deprecated. It is ignored and will be removed in future versions. "206"The default Python feature proxy is used for extension method overrides.");207PyErr_Print();208}209
210//get the extension type asked for211Base::Type extension = Base::Type::fromName(typeId);212if (extension.isBad() || !extension.isDerivedFrom(App::Extension::getExtensionClassTypeId())) {213std::stringstream str;214str << "No extension found of type '" << typeId << "'" << std::ends;215throw Py::TypeError(str.str());216}217
218//register the extension219App::Extension* ext = static_cast<App::Extension*>(extension.createInstance());220//check if this really is a python extension!221if (!ext->isPythonExtension()) {222delete ext;223std::stringstream str;224str << "Extension is not a python addable version: '" << typeId << "'" << std::ends;225throw Py::TypeError(str.str());226}227
228GetApplication().signalBeforeAddingDynamicExtension(*getExtensionContainerPtr(), typeId);229ext->initExtension(getExtensionContainerPtr());230
231// The PyTypeObject is shared by all instances of this type and therefore232// we have to add new methods only once.233PyObject* obj = ext->getExtensionPyObject();234PyMethodDef* meth = obj->ob_type->tp_methods;235PyTypeObject *type = this->ob_type;236PyObject *dict = type->tp_dict;237
238// make sure to do the initialization only once239if (meth->ml_name) {240PyObject* item = PyDict_GetItemString(dict, meth->ml_name);241if (!item) {242// Note: this adds the methods to the type object to make sure243// it appears in the call tips. The function will not be bound244// to an instance245Py_INCREF(dict);246while (meth->ml_name) {247PyObject *func;248func = PyCFunction_New(meth, 0);249if (!func)250break;251if (PyDict_SetItemString(dict, meth->ml_name, func) < 0)252break;253Py_DECREF(func);254++meth;255}256
257Py_DECREF(dict);258}259}260
261Py_DECREF(obj);262
263//throw the appropriate event264GetApplication().signalAddedDynamicExtension(*getExtensionContainerPtr(), typeId);265
266Py_Return;267}
268