How to make SSHLibrary not call logger.log_background_messages() on close unless in main thread?

Hi,

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

def close(self):
    """Closes the connection."""
    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()
    try:
        logger.log_background_messages()
    except AttributeError:
        pass

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?

Hi Kas,

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.

Dave.

3 Likes

Hello,

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

original_log_background_messages = robot.api.logger.log_background_messages

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.

Best Regard,
Helen

2 Likes

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!

1 Like