-
-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AttributeError: 'NoneType' object has no attribute 'start' #586
Comments
I've reviewed my previous example and found some errors. Here is updated code that addresses these errors in my previous example. The issue is still present despite fixing these issues. The following was corrected:
Despite these corrections, the error Corrected code: from PySide6.QtCore import QThread, QObject, Signal, QMetaObject, Qt, Slot
import traceback
class MyRelay(QObject):
sig_to_relay = Signal()
def remote_emit_relay_signal(self):
QMetaObject.invokeMethod(self, "emit_relay_signal", Qt.QueuedConnection)
@Slot()
def emit_relay_signal(self):
self.sig_to_relay.emit()
class MyWorker(QObject):
my_signal = Signal()
def __init__(self, relay):
super().__init__()
self._relay = relay
self._relay.setParent(self)
self._relay.sig_to_relay.connect(self.my_signal)
class MyThread(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._workers = None
self._thread = QThread(parent=self)
def add_worker(self, worker):
self._workers = worker
def start(self):
self._workers.moveToThread(self._thread)
self._thread.start()
class TestThread:
def test_error_1(self, qtbot):
relay = MyRelay()
mw = MyWorker(relay)
my_thread = MyThread()
my_thread.add_worker(mw)
my_thread.start()
try:
with qtbot.waitSignal(mw.my_signal, timeout=500) as blocker:
relay.remote_emit_relay_signal()
except Exception as e:
traceback.print_exc()
raise e
finally:
my_thread._thread.quit()
my_thread._thread.wait() (edited by @The-Compiler to add syntax highlighting) |
Reproduced by installing I added some debugging prints to pytest-qt: diff --git i/src/pytestqt/wait_signal.py w/src/pytestqt/wait_signal.py
index 8da3836..f559168 100644
--- i/src/pytestqt/wait_signal.py
+++ w/src/pytestqt/wait_signal.py
@@ -1,3 +1,4 @@
+import threading
import functools
from pytestqt.exceptions import TimeoutError
@@ -25,8 +26,10 @@ class _AbstractSignalBlocker:
self._signals = None # will be initialized by inheriting implementations
self._timeout_message = ""
if timeout is None or timeout == 0:
+ print("INIT NONE")
self._timer = None
else:
+ print(f"\n{threading.get_ident():x} INIT QTIMER")
self._timer = qt_api.QtCore.QTimer(self._loop)
self._timer.setSingleShot(True)
self._timer.setInterval(timeout)
@@ -44,6 +47,7 @@ class _AbstractSignalBlocker:
if self.timeout is None and not self._signals:
raise ValueError("No signals or timeout specified.")
if self._timer is not None:
+ print(f"{threading.get_ident():x} WAIT {self._timer}")
self._timer.timeout.connect(self._quit_loop_by_timeout)
self._timer.start()
@@ -63,6 +67,7 @@ class _AbstractSignalBlocker:
# store timeout message before the data to construct it is lost
self._timeout_message = self._get_timeout_error_message()
if self._timer is not None:
+ print(f"{threading.get_ident():x} CLEANUP")
_silent_disconnect(self._timer.timeout, self._quit_loop_by_timeout)
self._timer.stop()
self._timer = None which reveals: test.py::TestThread::test_error_1[151-500]
70582b03c740 INIT QTIMER
70582b03c740 WAIT <PySide6.QtCore.QTimer(0x632b10e78ca0) at 0x70582206b4c0>
705820ffd6c0 CLEANUP
QObject::killTimer: Timers cannot be stopped from another thread
PASSED
test.py::TestThread::test_error_1[152-500]
70582b03c740 INIT QTIMER
70582b03c740 WAIT <PySide6.QtCore.QTimer(0x632b10e7a2e0) at 0x705822069a80>
705820ffd6c0 CLEANUP
Traceback (most recent call last):
File ".../test.py", line 48, in test_error_1
with qtbot.waitSignal(mw.my_signal, timeout=500) as blocker:
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../pytestqt/wait_signal.py", line 156, in __exit__
self.wait()
~~~~~~~~~^^
File ".../pytestqt/wait_signal.py", line 52, in wait
self._timer.start()
^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'start'
FAILED So what happens is that pytest-qt/src/pytestqt/wait_signal.py Lines 46 to 48 in 9ccd999
We use: pytest-qt/src/pytestqt/wait_signal.py Line 205 in 9ccd999
which by default is supposed to use a
though then that page says:
So I believe this can be fixed simply by making Unfortunately, the obvious thing doesn't work: diff --git c/src/pytestqt/wait_signal.py i/src/pytestqt/wait_signal.py
index 8da3836..5a5e361 100644
--- c/src/pytestqt/wait_signal.py
+++ i/src/pytestqt/wait_signal.py
@@ -4,7 +4,7 @@ from pytestqt.exceptions import TimeoutError
from pytestqt.qt_compat import qt_api
-class _AbstractSignalBlocker:
+class _AbstractSignalBlocker(qt_api.QtCore.QObject):
"""
Base class for :class:`SignalBlocker` and :class:`MultiSignalBlocker`.
@@ -18,6 +18,7 @@ class _AbstractSignalBlocker:
"""
def __init__(self, timeout=5000, raising=True):
+ super().__init__()
self._loop = qt_api.QtCore.QEventLoop()
self.timeout = timeout
self.signal_triggered = False as the Traceback (most recent call last):
[...]
File ".../pluggy/_manager.py", line 421, in load_setuptools_entrypoints
plugin = ep.load()
[...]
File ".../pytestqt/plugin.py", line 11, in <module>
from pytestqt.qtbot import QtBot, _close_widgets
[...]
File ".../pytestqt/qtbot.py", line 7, in <module>
from pytestqt.wait_signal import (
...<6 lines>...
)
[...]
File ".../pytestqt/wait_signal.py", line 8, in <module>
class _AbstractSignalBlocker(qt_api.QtCore.QObject):
^^^^^^^^^^^^^
AttributeError: '_QtApi' object has no attribute 'QtCore' From a quick try with a hardcoded |
Thanks for looking into this issue and identifying the cause. Do you think this will be fixed in future versions on pytest-qt? |
I'm seeing an issue with pytest-qt (4.4.0) and I am not sure if it is a thread issue on my end or an issue with pytest-qt. Most of the time, the test will pass, but this issue will cause the test to fail about 10% to 20% of the time. Below is code that reproduces the issue:
In this example, the
try
block is to catch the exception before it fails the test so that I can check the traceback. During failures, the traceback is:It appears that the a timer in pytest-qt is getting through the
timer is None
check but then isNone
whenself._timer.start()
is called a few lines later.Also, on every run of the test (in debug mode), the following message is reported
QObject::killTimer: Timers cannot be stopped from another thread
, which may be related.Any idea on what may be wrong here?
Thanks.
(edited by @The-Compiler to add syntax highlighting)
The text was updated successfully, but these errors were encountered: