RobotFramework Monitoring Thread to Trigger Teardown - multithreading

I am trying to have a monitoring thread in robotframework to continuously monitor a signal and force a teardown if a certain signal is read. I have the following code.
import signal
from datetime import datetime
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
import logging
import thread
import os
import sys
import time
def do_error_log_monitoring_new():
def _monitor_log():
time.sleep(5)
# Monitor_Some_Signal
builtin_lib.fatal_error(msg='Force Teardown')
thread_logger = logging.getLogger('root')
thread_logger.info("Started Fault Listener")
builtin_lib = BuiltIn().get_library_instance('BuiltIn', all=True)
thread.start_new_thread(_monitor_log, ())
I have builtin_lib = BuiltIn().get_library_instance('BuiltIn', all=True) with the all=True argument to return dictionary mapping of all library names to instances. However I see the following error:
AttributeError: 'dict' object has no attribute 'fatal_error'
Furthermore, removing the all=True argument allows the function to go through and fatal_error to trigger, however I notice that it does not trigger a teardown in the main thread, which is what I intend to do. Any advice on how I can trigger teardown in the main thread from the fatal_error() function in the secondary thread?

You're setting builtin_lib to a dictionary (the result of BuiltIn().get_library_instances(..., all=True). As the error message states, this dictionary does not have a fatal_error method.
Change this:
builtin_lib.fatal_error(msg='Force Teardown')
... to this:
BuiltIn().fatal_error(msg='Force Teardown')
If you only need a reference to the BuiltIn library (versus a dictionary of all libraries), you don't need to call get_library_instance at all since BuiltIn() already returns an instance of the library.

Related

Class wide mock in pytest (for all methods in the whole TestClass)

I am unittesting my new librabry, which is basically database interface. Our apps use it to access our database. that means, I want to test all methods, but I do not want DB commands to be called for real. I only check if they are called with correct arguemnts.
For that purpose, I am mocking the database library. this is the actual code that DOES work:
import pytest
from unittests.conftest import PyTestConfig as Tconf
from my_lib.influx_interface import InfluxInterface
class TestInfluxInterface:
def test_connect(self, mocker):
"""
Create InfluxConnector object and call connect()
check if __init__() arguments are passed / used correctly
"""
influx_client = mocker.patch('my_lib.influx_interface.InfluxDBClient', autospec=True)
test_connector = InfluxInterface(Tconf.test_id)
# Call connect with no input (influx_client - should be called with no arguemnts
test_connector.connect()
influx_client.assert_called_once()
influx_client.reset_mock()
# Call connect with custom correct input (influx_client - should be called with custom values
test_connector.connect(Tconf.custom_conf)
influx_client.assert_called_once_with(url=Tconf.custom_conf["url"], token=Tconf.custom_conf["token"],
org=Tconf.custom_conf["org"], timeout=Tconf.custom_conf["timeout"],
debug=Tconf.custom_conf["debug"])
influx_client.reset_mock()
# Call connect with incorrect input (influx_client - should be called with default values
test_connector.connect(Tconf.default_conf)
influx_client.assert_called_once_with(url=Tconf.default_conf["url"], token=Tconf.default_conf["token"],
org=Tconf.default_conf["org"], timeout=Tconf.default_conf["timeout"],
debug=Tconf.default_conf["debug"])
Now, what I do next, is to add more methods into TestInfluxInterface class, which will be testing rest of the code. One test method for each method in my library. Thats how I usually do it.
The problem is, that there is a part of the code:
influx_client = mocker.patch('my_lib.influx_interface.InfluxDBClient', autospec=True)
test_connector = InfluxInterface(Tconf.test_id)
That will be same for every method. Thus I will be copy-pasting it over and over. As you can already see, thats not good solution.
In unittest, I would do this:
import unittest
import unittest.mock as mock
from unittests.conftest import PyTestConfig as Tconf
from my_lib.influx_interface import InfluxInterface
#mock.patch('my_lib.influx_interface.InfluxDBClient', autospec=True)
class TestInfluxInterface:
def setUp(self):
self.test_connector = InfluxInterface(Tconf.test_id)
def test_connect(self, influx_client):
"""
Create InfluxConnector object and call connect()
check if __init__() arguments are passed / used correctly
"""
# Call connect with no input (influx_client - should be called with no arguemnts
self.test_connector.connect()
influx_client.assert_called_once()
Than in each method, I would use self.test_connector to call whatever method I want to test, and check if it called correct influx_client method, with correct parameters.
However, we are moving from unittest to pytest. And while I am wrapping my head around pytest docs, reading stuff here and there, fixtures, mockers, etc..I cannot find out how to do this correctly.

Python Twisted continuous reconnect attempts

When trying to connect a factory I want it to periodically try to reconnect if it fails to do so. I tried it with a code looking like that:
def reconnect():
print("this sucks")
reactor.connectTCP("localhost", 6667, factory2)
factory2 = pb.PBClientFactory()
factory2.clientConnectionFailed(reconnect(), "reasons")
reactor.connectTCP("localhost", 6667, factory2)
If I run this code it prints "this sucks" just once, although calling the connectTCP method with factory2 as parameter again. How should I go about implementing the desired behavior?
clientConnectionFailed is a method that is called by Twisted on a factory when a connection attempt fails. The usage in your example is nonsensical.
See ReconnectingClientFactory for one solution:
from twisted.internet.protocol import ReconnectingClientFactory
from twisted.internet import reactor
from yourlib import YourProtocol
factory = ReconnectingClientFactory()
factory.protocol = YourProtocol
reactor.connectTCP(host, port, factory)
reactor.run()
However, this does not combine well with PB due to the use of a custom factory class to support PB. This is just one of many reasons to avoid using PB at all.
A more recently introduced solution is ClientService:
from twisted.application.internet import ClientService
from twisted.internet.endpoints import HostnameEndpoint
from yourlib import YourFactory
factory = YourFactory()
service = ClientService(
HostnameEndpoint(reactor, host, port),
YourFactory(),
)
service.startService()
reactor.run()
Note I've skipped over correct "service" usage here for brevity; see the Twisted
service documentation for details on correct usage.

Is it possible to create pyqtSignals on instances at runtime without using class variables?

Is there any possibility to create signals at runtime when needed?
I'm doing something like this in a function:
class WSBaseConnector(QObject)
def __init__(self) -> None:
super(QObject, self).__init__()
self._orderBookListeners: Dict[str, pyqtSignal[OrderBookData]] = {}
def registerOrderBookListener(self, market: str, listener: Callable[[OrderBookData], None], loop: AbstractEventLoop) -> None:
try:
signal = self._orderBookListeners[market]
except KeyError:
signal = pyqtSignal(OrderBookData)
signal.connect(listener)
self._orderBookListeners[market] = signal
else:
signal.connect(listener)
As you can see, I have a dict that stores str, pyqtSignal pairs. When I try to connect the signal to the listener I get the error:
'PyQt5.QtCore.pyqtSignal' object has no attribute 'connect'
Is it not possible to create pyqtSignals at runtime without been class vars?
Cheers.
No, it is not possible. The pyqtSignal object is a factory function that returns a descriptor, so it must be created when the class statement is executed. To quote from the docs:
New signals should only be defined in sub-classes of QObject. They
must be part of the class definition and cannot be dynamically added
as class attributes after the class has been defined.
New signals defined in this way will be automatically added to the
class’s QMetaObject. This means that they will appear in Qt Designer
and can be introspected using the QMetaObject API. [emphasis added]
Your code is creating unbound signal objects, which is why you get the attribute error. The distinction between bound and unbound signals is exactly the same as with the methods of classes. To quote again from the docs:
A signal (specifically an unbound signal) is a class attribute. When a
signal is referenced as an attribute of an instance of the class then
PyQt5 automatically binds the instance to the signal in order to
create a bound signal. This is the same mechanism that Python itself
uses to create bound methods from class functions.
#ekhumoro's accepted answer is, as far as I know, entirely accurate (specifically the statement: "They must be part of the class definition and cannot be dynamically added as class attributes after the class has been defined.").
Unfortunately I've seen this answer mis-interpreted as "It is not possible to generate Qt signals programmatically", which of course, when it comes to Python, is an entirely different proposition.
So I'm afraid this answer does not address the original question, which genuinely wanted to add signals at runtime and instead I thought I'd set the record straight and provide an example of creating signals programmatically, without contradicting the above statement. The solution is to create a dynamically generated class along with some dynamically generated signals. There are several good ways of generating classes dynamically in Python:
Option 1 - using the type function
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtCore import QMetaObject
DynamicSignal = type("DynamicSignal", (QObject, ), {
"my_custom_signal": pyqtSignal([str]),
})
if __name__ == '__main__':
dynamic = DynamicSignal()
dynamic.my_custom_signal.connect(print)
dynamic.my_custom_signal.emit("Hello world")
This prints "Hello world" when executed.
Option 2 - using metaclasses
We can also achieve the above with a metaclass:
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtCore import QMetaObject
class DynamicSignalMeta(type(QObject)):
def __new__(cls, name, bases, dct):
dct['my_custom_signal'] = pyqtSignal([str])
return super().__new__(cls, name, bases, dct)
class DynamicSignal(QObject, metaclass=DynamicSignalMeta):
pass
if __name__ == '__main__':
dynamic = DynamicSignal()
dynamic.my_custom_signal.connect(print)
dynamic.my_custom_signal.emit("Hello world")
In my other answer I focused on the question "Is it possible to programmatically add signals" as opposed to what the OP asked "Is it possible to dynamically add signals at runtime (i.e. after the class has been instantiated)".
Contrary to #ekhumoro's accepted answer, I would claim that it is actually possible to add signals at runtime, despite the PyQT documentation's very clear statement:
They must be part of the class definition and cannot be dynamically added as class attributes after the class has been defined
Whilst I don't doubt the accuracy of the statement, Python is a wonderfully dynamic language and it is in-fact reasonably easy to achieve the desired result. The problem we have to overcome is that in order to add signals at runtime, we must create a new class definition and modify an instance's underlying class. In Python this can be achieved by setting an object's __class__ attribute (which in general has a number of issues to be aware of).
from PyQt5.QtCore import QObject, pyqtSignal
class FunkyDynamicSignals(QObject):
def add_signal(self, name, *args):
# Get the class of this instance.
cls = self.__class__
# Create a new class which is identical to this one,
# but which has a new pyqtSignal class attribute called of the given name.
new_cls = type(
cls.__name__, cls.__bases__,
{**cls.__dict__, name: pyqtSignal(*args)},
)
# Update this instance's class with the newly created one.
self.__class__ = new_cls # noqa
With this class we can create signals after the object has been instantiated:
>>> dynamic = FunkyDynamicSignals()
>>> dynamic.add_signal('example', [str])
>>> dynamic.example.connect(print)
>>> dynamic.add_signal('another_example', [str])
>>> dynamic.another_example.connect(print)
>>> dynamic.example.emit("Hello world")
Hello world
This approach uses modern Python syntax (but could equally have been written for Py2), is careful to expose a sensible class hierarchy and preserves existing connections when new signals are added.

How do I connect a PyQt5 slot to a signal function in a class?

I'm trying to set up a pyqt signal between a block of UI code and a separate python class that is just for handling event responses. I don't want to give the UI code access to the handler (classic MVC style). Unfortunately, I am having difficulty connecting the slot to the signal. Here is the code:
from PyQt5 import QtCore
from PyQt5.QtCore import QObject
class UiClass(QObject):
mySignal = QtCore.pyqtSignal( str )
def __init__(self):
QObject.__init__(self)
def send_signal(self):
self.mySignal.emit("Hello world!")
class HandlerClass():
currentMessage = "none"
def register(self, mySignal):
mySignal.connect(self.receive_signal)
#QtCore.pyqtSlot(str)
def receive_signal(self, message):
self.currentMessage = message
print(message)
ui = UiClass()
handler = HandlerClass()
handler.register(ui.mySignal)
ui.send_signal()
When I run this code it fails at the handler.register line. Here is the error:
Traceback (most recent call last):
File "C:\git\IonControl\testpyqtslot.py", line 25, in
handler.register(ui.mySignal)
File "C:\git\IonControl\testpyqtslot.py", line 17, in register
mySignal.connect(self.receive_signal)
TypeError: connect() failed between UiClass.mySignal[str] and receive_signal()
I'd like this code to successfully register the signal to the slot and have the handler print "hello world" at the end. What did I do wrong here?
My basic question is this: how do I connect a signal to a slot function that is part of a class?
The error happens becuase you are using pyqtSlot decorator in a class which doesn't inherit from QObject. You can fix the problem by either getting rid of the decorator, or making HandlerClass a subclass of QObject.
The main purpose of pyqtSlot is to allow several different overloads of a slot to be defined, each with a different signature. It may also be needed sometimes when making cross-thread connections. However, these use-cases are relatively rare, and in most PyQt applications it is not necessary to use pyqtSlot at all. Signals can be connected to any python callable object, whether it is decorated as a slot or not.

Call a PyQt class from inside Watchdog event handler class

I have watchdog set up to monitor a folder for new files and I want to try to get back to the main PyQt class after it runs the on_created function.
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class CreatedEvent(FileSystemEventHandler):
def on_created(self, event):
#some code
I need to be able to call someFunction
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class someClass(QMainWindow):
def someFunction(self):
#some code
I have tried using signals but I always get this error
QPixmap: It is not safe to use pixmaps outside the GUI thread
QPixmap: It is not safe to use pixmaps outside the GUI thread

Resources