I am in my keyword running some parallel tasks using concurrent.futures and using robotbackgroundlogger to log the messages. After all threads are finished I run logger.log_background_messages() in the main thread since it only allows running it in main thread.
The problem is that SSHLibrary() automatically tries to call logger.log_background_messages() in it’s close function.
From the source code I can see the following snippet
Which means that closing any SSH connection in a thread would raise an error, since you are not allowed to run the function logger.log_background_messages() unless in the main thread.
How can I disable so that SSHLibrary does not try to run logger.log_background_messages when I close my SSH connection? Or some other workaround to make it work?
It looks to me from that code snippet, that the try block is missing 1 or more except blocks.
Since it’s in the code of SSH Library itself, it’s going to need a new version of that library, so I suggest you go to the SSH Library source (gthub?) and raise and issue for the close function to handle the exception you’re getting,
if you’re comfortable with python you can submit a PR too, which will help get the fix released quicker.
You’re absolutely right in your analysis — the SSHLibrary.close() method calls logger.log_background_messages(), which must run in the main thread, but your parallel execution with concurrent.futures involves calling .close() from background threads, which causes the problem.
Monkey Patch SSHLibrary.close to Skip logger.log_background_messages()
If you’re okay with patching during test execution, you can override the close() method once during initialization:
from SSHLibrary import SSHLibrary
def patched_close(self):
if self.tunnel:
self.tunnel.close()
self._sftp_client = None
self._scp_transfer_client = None
self._scp_all_client = None
self._shell = None
self.client.close()
# Do not call logger.log_background_messages()
Apply the patch
SSHLibrary.close = patched_close
Place this in your test setup (or a library imported before any SSH connections are made). Now SSHLibrary.close() will skip the logger.log_background_messages() call entirely.
Manually Call logger.log_background_messages() Once in Main Thread
You already mentioned you’re doing this, which is good. So you could continue to let threads run .close() but temporarily suppress the logger call inside close() to avoid the threading issue, and call the logger explicitly in main:
To suppress the logger call:
Monkey patch just the logger.log_background_messages method inside the thread:
import threading
from SSHLibrary import SSHLibrary
import robot
def close_without_logging(self):
# Temporarily disable background logging in threads
if threading.current_thread() is not threading.main_thread():
robot.api.logger.log_background_messages = lambda: None
try:
SSHLibrary.original_close(self)
finally:
robot.api.logger.log_background_messages = original_log_background_messages
else:
SSHLibrary.original_close(self)
Save the original close method
SSHLibrary.original_close = SSHLibrary.close
Replace it
SSHLibrary.close = close_without_logging
Then in your main thread after all threads are done, you can safely call:
from robot.api import logger
logger.log_background_messages()
Avoid Closing SSH Connections in Threads (Centralize Cleanup in Main Thread)
If you’re not opening/closing a huge number of connections, an alternate model would be:
Let threads perform the SSH actions.
Pass the connection back to the main thread.
Only close the connections in the main thread.
But this can be harder to manage depending on how your keyword is structured.
I ended up with a solution where I store all the SSH connections to close in a global queue object and then close them when I am back in main thread basically. Thanks for input!