Search code examples
pythonpython-3.xmacosseleniumgeckodriver

"No such file or directory" error message upon completion of firefox selenium test


I have a stdout error message that occurs after running my python selenium test using geckodriver (firefox) but doesn't occur when using chrome. I can't figure out where the error line is coming from. It happens after the call to sys.exit() so somewhere during the cleanup process.

[Errno 2] No such file or directory: '/var/folders/ml/tbnznl592397p4h1svx4t5xr0000gr/T/tmpr8ypd0y1'
  • MacOS 10.14.5 (Mojave)
  • Driver info: firefox=67.0
  • Session info: firefox=0.24.0
  • Selenium 3.141.0
  • Python 3.7.3

Here is the driver initialization for both firefox and chrome.

    def createDriver(self, browser: str, headless: bool = False, performancelog=False, 
                     consolelog=False, verbose: bool = False):

        ## DRIVERS
        #chrome
        # https://chromedriver.storage.googleapis.com/index.html

        #firefox
        # https://github.com/mozilla/geckodriver/releases
        # 20.1 has a bug where headless doesn't work
        # 19 has a bug where it closes a frame?

        # safari

        # edge
        # https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
        logger.trace("createDriver()")
        if browser.upper() == 'FIREFOX':
            ## setting log_path to /dev/null will prevent geckodriver from creating it's own log file.
            ## if we enable root logging, we can capture the logging from geckodriver, ourselves.
            options = webdriver.FirefoxOptions()
            profile = webdriver.FirefoxProfile()

            profile.set_preference('app.update.auto', False)
            profile.set_preference('app.update.enabled', False)

            # options.binary_location = binary ## use this only if we want to move the executable outside the available path
            if headless:
                options.headless = True

            caps = DesiredCapabilities.FIREFOX
            caps['unhandledPromptBehavior'] = "ignore"
            driver = webdriver.Firefox(profile, log_path='/dev/null', firefox_options=options, 
                                       desired_capabilities=caps
                                       )
            logger.debug(f"Driver info: firefox={driver.capabilities['browserVersion']}")
            logger.debug(f"Session info: firefox={driver.capabilities['moz:geckodriverVersion']}")

            if driver.capabilities['moz:geckodriverVersion'] == '0.20.1':
                if headless:
                    raise Exception("Headless mode doesn't work in Gecko Driver 0.20.1")

        elif browser.upper() == 'CHROME':
            options = webdriver.ChromeOptions()
            options.add_argument("--disable-extensions")
            options.add_argument("--allow-running-insecure-content")
            options.add_argument("--ignore-certificate-errors")
            options.add_argument("--disable-single-click-autofill")
            options.add_argument("--disable-autofill-keyboard-accessory-view[8]")
            options.add_argument("--disable-full-form-autofill-ios")
            options.add_argument("--dns-prefetch-disable") # https://bugs.chromium.org/p/chromedriver/issues/detail?id=402#c128

            ## ChromeDriver is just AWFUL because every version or two it breaks unless you pass cryptic arguments
            options.add_argument("start-maximized") # https://stackoverflow.com/a/26283818/1689770
            options.add_argument("enable-automation") # https://stackoverflow.com/a/43840128/1689770
            options.add_argument("--no-sandbox") # https://stackoverflow.com/a/50725918/1689770
            options.add_argument("--disable-infobars") # https://stackoverflow.com/a/43840128/1689770
            options.add_argument("--disable-dev-shm-usage") # https://stackoverflow.com/a/50725918/1689770
            options.add_argument("--disable-browser-side-navigation") # https://stackoverflow.com/a/49123152/1689770
            options.add_argument("--disable-gpu") # https://stackoverflow.com/questions/51959986/how-to-solve-selenium-chromedriver-timed-out-receiving-message-from-renderer-exc
            # options.add_argument("--window-size=1280,960")
            options.add_argument("--enable-features=NetworkService,NetworkServiceInProcess") # https://groups.google.com/forum/m/#!topic/chromedriver-users/ktp-s_0M5NM[21-40]

            if headless:
                options.headless = True
                # options.add_argument('--disable-gpu')

            args = [
                "--verbose"
                ]

            caps = DesiredCapabilities.CHROME
            caps['loggingPrefs'] = {
                'browser': 'OFF',
                'performance' : 'OFF',
                'driver' : 'OFF'
                }
            # https://www.skptricks.com/2018/08/timed-out-receiving-message-from-renderer-selenium.html
            # caps['pageLoadStrategy'] = 'none'
            # caps['pageLoadStrategy'] = 'normal'

            if consolelog:
                caps['loggingPrefs']['browser'] = 'ALL'

            ## by default performance is disabled.
            if performancelog:
                caps['loggingPrefs']['performance'] = 'ALL'
                caps['perfLoggingPrefs'] = {
                    'enableNetwork' : True,
                    'enablePage' : False,
                    'enableTimeline' : False
                    }

            if verbose:
                driver = webdriver.Chrome(options=options, service_log_path='chromedriver.log', service_args=args, 
                                          desired_capabilities=caps
                                          )
            else:
                driver = webdriver.Chrome(options=options, 
                                          # service_log_path='chromedriver.log',
                                          desired_capabilities=caps
                                          )
            logger.debug(f"Driver info: chrome={driver.capabilities['chrome']['chromedriverVersion']}")
            logger.debug(f"Session info: chromedriver={driver.capabilities['version']}")

        elif browser.upper() == 'EDGE':
            # driver = webdriver.Edge()
            raise NotImplemented("Edge not supported yet")

        elif browser.upper() == 'SAFARI':
            # driver = webdriver.Safari()
            raise NotImplemented("Safari not supported yet")

        # support is depricated for this -- should use chrome or firefox headless. 
        elif browser.upper() == 'PHANTOMJS':
            # driver = webdriver.PhantomJS()
            raise NotImplemented("PhantomJS not supported yet")
        else:
            raise ValueError(f"Unknown browser: {browser}")

        # driver.set_page_load_timeout(self.timeout)
        # driver.set_script_timeout(self.timeout)
        return driver

In both cases I the driver sits in a wrapper that handles automatically quitting the driver.

    ############################################################################
    def close(self):
        if self.driver is not None:
            self.driver.close()

    ############################################################################
    def quit(self):
        if self.driver is not None:
            self.driver.quit()

    ############################################################################
    def __del__(self):
        self.quit()

Calling close or quit before exiting the script doesn't trigger the error message which has me confused. I had thought the the error message was something in the geckodriver upon garbage collection, but if that were the case I would think I could manually trigger the error by closing the driver prior to sys.exit().

Anyone know where this error message comes from after a firefox selenium run?


EDIT: It would appear the folder in question is the temporary directory FirefoxProfile creates. I would appear that the firefox.WebDriver.quit() method is where the log line is coming from.

    def quit(self):
        """Quits the driver and close every associated window."""
        try:
            RemoteWebDriver.quit(self)
        except Exception:
            # We don't care about the message because something probably has gone wrong
            pass

        if self.w3c:
            self.service.stop()
        else:
            self.binary.kill()

        if self.profile is not None:
            try:
                shutil.rmtree(self.profile.path)
                if self.profile.tempfolder is not None:
                    shutil.rmtree(self.profile.tempfolder)
            except Exception as e:
                print(str(e)) #<-- this is the line where the error occurs.

Solution

  • Gah! I completely forgot I added a __del__ method to the Firefox webdriver. (this was before I had written the wrapper)

    # hack to force firefox to quit when closing scope
    def __del_replacement__(self: webdriver.Firefox):
        print("__del__ called in Firefox")
        self.quit()
    webdriver.Firefox.__del__ = __del_replacement__
    

    The problem is there is also a __del__ method in the wrapper around the webdriver which tries to call driver.quit() as well. The problem was caused by both calls to the quit() method trying to remove the temp directory. The 2nd was failing because it had already been removed.