aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShyamnath Premnadh <Shyamnath.Premnadh@qt.io>2022-01-10 10:22:09 +0100
committerChristian Tismer <tismer@stackless.com>2022-01-20 08:43:08 +0000
commit37ac99012c72156342a39644d03a74e714e0ff92 (patch)
treef57280dfe830878754d990d712a6df8c2bb9abaa
parent4f34cf03a83f35a9ae5608f631fadf470cdd2cb4 (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.cpp3
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.cpp7
-rw-r--r--sources/pyside6/libpyside/pyside.cpp7
-rw-r--r--sources/pyside6/libpyside/pyside_p.h2
-rw-r--r--sources/pyside6/libpyside/pysidesignal.cpp7
-rw-r--r--sources/pyside6/tests/pysidetest/CMakeLists.txt1
-rw-r--r--sources/pyside6/tests/pysidetest/mock_as_slot_test.py59
-rw-r--r--sources/shiboken6/libshiboken/bindingmanager.cpp3
-rw-r--r--sources/shiboken6/libshiboken/sbkstaticstrings.cpp2
-rw-r--r--sources/shiboken6/libshiboken/sbkstaticstrings.h1
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