See More

/* -*- mode: c++; indent-tabs-mode: nil -*- */ /* QorePythonClass.h Qore Programming Language python Module Copyright (C) 2020 - 2026 Qore Technologies, s.r.o. This library 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. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "QorePythonClass.h" #include "QorePythonProgram.h" #include type_vec_t QorePythonClass::gateParamTypeInfo = { stringTypeInfo }; static constexpr const char* PYOBJ_KEY = "__$PYCLS__"; QorePythonClass::QorePythonClass(const char* name, const char* path) : QoreBuiltinClass(name, path, QDOM_UNCONTROLLED_API), pypgm(nullptr) { } QorePythonClass::QorePythonClass(QorePythonProgram* pypgm, const char* name, const char* path) : QoreBuiltinClass(name, path, QDOM_UNCONTROLLED_API), pypgm(pypgm) { pypgm->weakRef(); addMethod(nullptr, "memberGate", (q_external_method_t)memberGate, Public, QCF_NO_FLAGS, QDOM_UNCONTROLLED_API, autoTypeInfo, gateParamTypeInfo); addMethod(nullptr, "methodGate", (q_external_method_t)methodGate, Public, QCF_USES_EXTRA_ARGS, QDOM_UNCONTROLLED_API, autoTypeInfo, gateParamTypeInfo); addMember(PYOBJ_KEY, Internal, autoTypeInfo); setPublicMemberFlag(); } QorePythonClass::QorePythonClass(const QorePythonClass& old) : QoreBuiltinClass(old), pypgm(old.pypgm) { if (pypgm) { pypgm->weakRef(); } } QorePythonClass::~QorePythonClass() { if (pypgm) { pypgm->weakDeref(); } } void QorePythonClass::addObj(PyObject* obj) { pypgm->addObj(obj); } PyObject* QorePythonClass::getPyObject(QoreObject* self, ExceptionSink* xsink) const { ValueHolder v(self->getReferencedMemberNoMethod(PYOBJ_KEY, this, xsink), xsink); if (*xsink) { assert(!v); return nullptr; } if (!v) { return nullptr; } if (v->getType() != NT_INT) { xsink->raiseException("PYTHON-OBJECT-ERROR", "invalid type '%s' saved to internal data key '%s'", v->getFullTypeName(), PYOBJ_KEY); return nullptr; } return (PyObject*)v->getAsBigInt(); } int QorePythonClass::setPyObject(QoreObject* self, PyObject* pyself, ExceptionSink* xsink) const { return self->setMemberValue(PYOBJ_KEY, this, (int64)pyself, xsink); } // static method QoreValue QorePythonClass::methodGate(const QoreMethod& meth, void* m, QoreObject* self, QorePythonPrivateData* pd, const QoreListNode* args, q_rt_flags_t rtflags, ExceptionSink* xsink) { assert(args && args->size() >= 1); assert(args->retrieveEntry(0).getType() == NT_STRING); QoreStringValueHelper mname(args->retrieveEntry(0)); //printd(5, "QorePythonClass::methodGate() %s()\n", mname); QorePythonProgram* pypgm = QorePythonProgram::getPythonProgramFromMethod(meth, xsink); if (!pypgm) { assert(*xsink); return QoreValue(); } const QorePythonClass* cls = static_cast(meth.getClass()); return cls->callPythonMethod(xsink, pypgm, mname->c_str(), args, pd, 2); } // static method QoreValue QorePythonClass::memberGate(const QoreMethod& meth, void* m, QoreObject* self, QorePythonPrivateData* pd, const QoreListNode* args, q_rt_flags_t rtflags, ExceptionSink* xsink) { assert(args && args->size() == 1); assert(args->retrieveEntry(0).getType() == NT_STRING); QoreStringValueHelper mname(args->retrieveEntry(0)); QorePythonProgram* pypgm = QorePythonProgram::getPythonProgramFromMethod(meth, xsink); if (!pypgm) { assert(*xsink); return QoreValue(); } const QorePythonClass* cls = static_cast(meth.getClass()); return cls->getPythonMember(pypgm, mname->c_str(), pd, xsink); } QoreValue QorePythonClass::callPythonMethod(ExceptionSink* xsink, QorePythonProgram* pypgm, const char* mname, const QoreListNode* args, QorePythonPrivateData* pd, size_t arg_offset) const { //printd(5, "QorePythonClass::callPythonMethod() %s::%s()\n", pname.c_str(), mname); PyObject* pyobj = pd->get(); PyTypeObject* mtype = Py_TYPE(pyobj); QorePythonHelper qph(pypgm, xsink); if (*xsink || pypgm->checkValid(xsink)) { return QoreValue(); } // Use PyObject_GetAttrString to properly traverse the MRO for inherited methods // (PyDict_GetItemString only looks in the immediate type's dict, missing inherited methods like __sizeof__) // Note: PyObject_GetAttrString returns a new reference and may return a bound method QorePythonReferenceHolder attr(PyObject_GetAttrString((PyObject*)mtype, mname)); if (!*attr) { PyErr_Clear(); xsink->raiseException("METHOD-DOES-NOT-EXIST", "Python value of type '%s' has no method or member '%s'", mtype->tp_name, mname); return QoreValue(); } // PyObject_GetAttrString can return either: // 1. Unbound descriptors (function, method_descriptor, etc.) - need self prepended, use arg_offset // 2. Bound methods (builtin_function_or_method for classmethods) - self already bound, use arg_offset - 1 // Check if it's a bound builtin method by checking if it's a PyCFunction that's already bound PyTypeObject* attr_type = Py_TYPE(*attr); if (PyCFunction_Check(*attr) && attr_type != &PyMethodDescr_Type && attr_type != &PyClassMethodDescr_Type) { // Bound method - only skip the method name, not the implicit self return pypgm->callPythonMethod(xsink, *attr, pyobj, args, arg_offset - 1); } return pypgm->callPythonMethod(xsink, *attr, pyobj, args, arg_offset); } QoreValue QorePythonClass::getPythonMember(QorePythonProgram* pypgm, const char* mname, QorePythonPrivateData* pd, ExceptionSink* xsink) const { QorePythonHelper qph(pypgm, xsink); if (*xsink || pypgm->checkValid(xsink)) { return QoreValue(); } { PyMemberDef* m = getPythonMember(mname); if (m) { return pypgm->getQoreValue(xsink, PyMember_GetOne((const char*)pd->get(), m)); } } return pypgm->getQoreAttr(pd->get(), mname, xsink); }