diff options
author | Shyamnath Premnadh <Shyamnath.Premnadh@qt.io> | 2022-01-10 10:22:09 +0100 |
---|---|---|
committer | Christian Tismer <tismer@stackless.com> | 2022-01-20 08:43:08 +0000 |
commit | 37ac99012c72156342a39644d03a74e714e0ff92 (patch) | |
tree | f57280dfe830878754d990d712a6df8c2bb9abaa | |
parent | 4f34cf03a83f35a9ae5608f631fadf470cdd2cb4 (diff) |
Safe distinction of Nuitka compiled methods
Adds an extra check to see if __code__ is present.
As mentioned in PYSIDE-1755, Mocks are callable objects without __code__
attribute, unlike Python Method or Functions. However, a Mock also has
im_func and im_self attributes. We made the assumption __code__
would be present if im_func and im_self are present, and this makes it
fall under the category of a compiled method.
This patch makes an extra check to see if __code__ is present. If it is
not, then the Slot (here Mock) is considered as a callable method.
Task-number: PYSIDE-1755
Change-Id: If7e8f52dfb2409cd856eec0d0b41891d751d8a69
(cherry picked from commit 56f66f128566bd08f027fee46bb42a6c4f57a26e)
Reviewed-by: Christian Tismer <tismer@stackless.com>
-rw-r--r-- | sources/pyside6/PySide6/glue/qtcore.cpp | 3 | ||||
-rw-r--r-- | sources/pyside6/libpyside/globalreceiverv2.cpp | 7 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pyside.cpp | 7 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pyside_p.h | 2 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pysidesignal.cpp | 7 | ||||
-rw-r--r-- | sources/pyside6/tests/pysidetest/CMakeLists.txt | 1 | ||||
-rw-r--r-- | sources/pyside6/tests/pysidetest/mock_as_slot_test.py | 59 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/bindingmanager.cpp | 3 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/sbkstaticstrings.cpp | 2 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/sbkstaticstrings.h | 1 |
10 files changed, 82 insertions, 10 deletions
diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index 2b5729a69..da2e016ea 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -371,7 +371,8 @@ static bool getReceiver(QObject *source, if (*self && %CHECKTYPE[QObject *](*self)) *receiver = %CONVERTTOCPP[QObject *](*self); } else if (PyObject_HasAttr(callback, Shiboken::PyName::im_func()) - && PyObject_HasAttr(callback, Shiboken::PyName::im_self())) { + && PyObject_HasAttr(callback, Shiboken::PyName::im_self()) + && PyObject_HasAttr(callback, Shiboken::PyMagicName::code())) { *self = PyObject_GetAttr(callback, Shiboken::PyName::im_self()); Py_DECREF(*self); diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp index 0fe67db37..909c2bde6 100644 --- a/sources/pyside6/libpyside/globalreceiverv2.cpp +++ b/sources/pyside6/libpyside/globalreceiverv2.cpp @@ -39,6 +39,7 @@ #include "globalreceiverv2.h" #include "dynamicqmetaobject_p.h" +#include "pyside_p.h" #include "pysideweakref.h" #include "pysidestaticstrings.h" #include "signalmanager.h" @@ -115,8 +116,7 @@ DynamicSlotDataV2::DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *paren //monitor class from method lifetime m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this); - } else if (PyObject_HasAttr(callback, PySide::PyName::im_func()) - && PyObject_HasAttr(callback, PySide::PyName::im_self())) { + } else if (PySide::_isCompiledMethod(callback)) { // PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes. m_isMethod = true; @@ -142,8 +142,7 @@ GlobalReceiverKey DynamicSlotDataV2::key(PyObject *callback) if (PyMethod_Check(callback)) { // PYSIDE-1422: Avoid hash on self which might be unhashable. return {PyMethod_GET_SELF(callback), PyMethod_GET_FUNCTION(callback)}; - } else if (PyObject_HasAttr(callback, PySide::PyName::im_func()) - && PyObject_HasAttr(callback, PySide::PyName::im_self())) { + } else if (PySide::_isCompiledMethod(callback)) { // PYSIDE-1589: Fix for slots in compiled functions Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PyName::im_self())); Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PyName::im_func())); diff --git a/sources/pyside6/libpyside/pyside.cpp b/sources/pyside6/libpyside/pyside.cpp index 4769ea86d..b4a119bf5 100644 --- a/sources/pyside6/libpyside/pyside.cpp +++ b/sources/pyside6/libpyside/pyside.cpp @@ -86,6 +86,13 @@ QT_END_NAMESPACE namespace PySide { +bool _isCompiledMethod(PyObject *callback) +{ + return PyObject_HasAttr(callback, PySide::PyName::im_func()) + && PyObject_HasAttr(callback, PySide::PyName::im_self()) + && PyObject_HasAttr(callback, PySide::PyMagicName::code()); +} + void init(PyObject *module) { qobjectNextAddr = nullptr; diff --git a/sources/pyside6/libpyside/pyside_p.h b/sources/pyside6/libpyside/pyside_p.h index a5183c907..5a1ec30cd 100644 --- a/sources/pyside6/libpyside/pyside_p.h +++ b/sources/pyside6/libpyside/pyside_p.h @@ -47,6 +47,8 @@ namespace PySide { +bool _isCompiledMethod(PyObject *callback); + // Struct associated with QObject's via Shiboken::Object::getTypeUserData() struct TypeUserData { diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp index a30ea35f8..41284feaf 100644 --- a/sources/pyside6/libpyside/pysidesignal.cpp +++ b/sources/pyside6/libpyside/pysidesignal.cpp @@ -40,6 +40,7 @@ #include <sbkpython.h> #include "pysidesignal.h" #include "pysidesignal_p.h" +#include "pyside_p.h" #include "pysidestaticstrings.h" #include "signalmanager.h" @@ -330,7 +331,7 @@ static void extractFunctionArgumentsFromSlot(PyObject *slot, if (functionName != nullptr) { *functionName = Shiboken::String::toCString(PepFunction_GetName(function)); } - } else if (PyObject_HasAttr(slot, PySide::PyName::im_func())) { + } else if (PySide::_isCompiledMethod(slot)) { // PYSIDE-1523: PyFunction_Check and PyMethod_Check are not accepting compiled forms, we // just go by attributes. isMethod = true; @@ -381,6 +382,7 @@ static void extractFunctionArgumentsFromSlot(PyObject *slot, function = nullptr; } } + // any other callback } static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject *kwds) @@ -1165,8 +1167,7 @@ QString codeCallbackName(PyObject *callback, const QString &funcName) return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16); } // PYSIDE-1523: Handle the compiled case. - if (PyObject_HasAttr(callback, PySide::PyName::im_func()) - && PyObject_HasAttr(callback, PySide::PyName::im_self())) { + if (PySide::_isCompiledMethod(callback)) { // Not retaining references inline with what PyMethod_GET_(SELF|FUNC) does. Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PyName::im_self())); Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PyName::im_func())); diff --git a/sources/pyside6/tests/pysidetest/CMakeLists.txt b/sources/pyside6/tests/pysidetest/CMakeLists.txt index 880433ad9..9fec250b5 100644 --- a/sources/pyside6/tests/pysidetest/CMakeLists.txt +++ b/sources/pyside6/tests/pysidetest/CMakeLists.txt @@ -157,3 +157,4 @@ PYSIDE_TEST(signalinstance_equality_test.py) PYSIDE_TEST(signalwithdefaultvalue_test.py) PYSIDE_TEST(typedef_signal_test.py) PYSIDE_TEST(version_test.py) +PYSIDE_TEST(mock_as_slot_test.py) diff --git a/sources/pyside6/tests/pysidetest/mock_as_slot_test.py b/sources/pyside6/tests/pysidetest/mock_as_slot_test.py new file mode 100644 index 000000000..2e332002e --- /dev/null +++ b/sources/pyside6/tests/pysidetest/mock_as_slot_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2022 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +""" PYSIDE-1755: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1755 + Tests that a unittest.mock.MagicMock() can be used as a slot for quick + prototyping. """ + +import os +import sys +import unittest +from unittest.mock import MagicMock + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from init_paths import init_test_paths +init_test_paths(False) + +from PySide6.QtCore import QObject + + +class MockAsSlot(unittest.TestCase): + def testMockAsSlot(self): + obj = QObject() + mock = MagicMock() + obj.objectNameChanged.connect(mock) + + obj.objectNameChanged.emit("test") + mock.assert_called_once() + + +if __name__ == "__main__": + unittest.main() diff --git a/sources/shiboken6/libshiboken/bindingmanager.cpp b/sources/shiboken6/libshiboken/bindingmanager.cpp index 20b331bde..0fc6ab78f 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.cpp +++ b/sources/shiboken6/libshiboken/bindingmanager.cpp @@ -326,7 +326,8 @@ PyObject *BindingManager::getOverride(const void *cptr, method = nullptr; } } else if (PyObject_HasAttr(method, PyName::im_self()) - && PyObject_HasAttr(method, PyName::im_func())) { + && PyObject_HasAttr(method, PyName::im_func()) + && PyObject_HasAttr(method, Shiboken::PyMagicName::code())) { PyObject *im_self = PyObject_GetAttr(method, PyName::im_self()); // Not retaining a reference inline with what PyMethod_GET_SELF does. Py_DECREF(im_self); diff --git a/sources/shiboken6/libshiboken/sbkstaticstrings.cpp b/sources/shiboken6/libshiboken/sbkstaticstrings.cpp index 20aa1b8a0..f85267574 100644 --- a/sources/shiboken6/libshiboken/sbkstaticstrings.cpp +++ b/sources/shiboken6/libshiboken/sbkstaticstrings.cpp @@ -96,12 +96,12 @@ STATIC_STRING_IMPL(property_methods, "__property_methods__") STATIC_STRING_IMPL(qualname, "__qualname__") STATIC_STRING_IMPL(self, "__self__") STATIC_STRING_IMPL(select_i, "__self__") +STATIC_STRING_IMPL(code, "__code__") // Internal: STATIC_STRING_IMPL(base, "__base__") STATIC_STRING_IMPL(bases, "__bases__") STATIC_STRING_IMPL(builtins, "__builtins__") -STATIC_STRING_IMPL(code, "__code__") STATIC_STRING_IMPL(dictoffset, "__dictoffset__") STATIC_STRING_IMPL(func, "__func__") STATIC_STRING_IMPL(func_kind, "__func_kind__") diff --git a/sources/shiboken6/libshiboken/sbkstaticstrings.h b/sources/shiboken6/libshiboken/sbkstaticstrings.h index fd0c06e43..3bf220b6c 100644 --- a/sources/shiboken6/libshiboken/sbkstaticstrings.h +++ b/sources/shiboken6/libshiboken/sbkstaticstrings.h @@ -82,6 +82,7 @@ LIBSHIBOKEN_API PyObject *property_methods(); LIBSHIBOKEN_API PyObject *qualname(); LIBSHIBOKEN_API PyObject *self(); LIBSHIBOKEN_API PyObject *opaque_container(); +LIBSHIBOKEN_API PyObject *code(); } // namespace PyMagicName } // namespace Shiboken |