0

I've got a PyQt QThread that's running a worker object, and as one of the duties of the worker object, it instantiates and uses a selenium webdriver (Firefox in this case).

I've also got a PyQt slot on that worker object that needs to cleanly teardown the object. Since the worker "owns" the webdriver, it needs to be able to kill it at any time. Calling webdriver.quit() only causes the webdriver to quit some of the time, exclusively after the webdriver is fully set up. Tracing the issue through the call stack of Selenium's source code makes me think that the issue happens when I try to execute Command.QUIT on the remote webdriver. If its remote command executor hasn't instantiated yet, then naturally it can't quit.

How can I be absolutely certain that selenium has torn down?

Jakob Lovern
  • 1,301
  • 7
  • 24
  • Part of the quitting process involves closing the browser. (or at least sending that command to the browser...) There are also some cleanup tasks that the browser needs to finish before fully shutting itself down. So you can't use the driver's quit command until a browser has been launched. You can kill the task whenever you like, but that will leave the browser open. Maybe check here for taskkill: https://stackoverflow.com/questions/47999568/selenium-how-to-stop-geckodriver-process-impacting-pc-memory-without-calling – pcalkins Mar 02 '22 at 22:45

1 Answers1

0

I've since refactored my code completely, but here's what I've learned in the meantime:

One way to avoid this would be to setup a mutex that unlocks when we're done "setting up" selenium. That way, we can only actually run the "kill" code when we're sure that there's something to kill. That looks something like this:

class Worker(QObject):
    finished = QtCore.pyqtSignal()

    def __init__(self):
        #Setup the lock early
        self.setupLock = QMutex()
 
    def setup(self):
        self.setupLock.lock()
        #do whatever selenium initialization you need to
        self.setupLock.unlock()

    def quit(self):
        self.setupLock.lock()
        self.driver.quit()
        self.setupLock.unlock()
        self.finished.emit()

Note that I've very purposefully moved the setup() function out of __init__. Workers don't necessarily destruct once the thread ends, so we practice good mutex security on setup() and quit().

Jakob Lovern
  • 1,301
  • 7
  • 24