Как исправить ошибку python-selenium "Отказано в соединении" при инициализации веб-драйвера selenium? - PullRequest
0 голосов
/ 24 сентября 2018

Я выполняю очень сложные тесты python-selenium на закрытых веб-страницах.В большинстве случаев эти тесты работают нормально, но иногда один из этих тестов не выполняется во время инициализации самого веб-драйвера.

Подсказка: эта ошибка возникает при попытке инициализировать веб-драйвер, т.е. при выполнении чего-то подобного:

# Start of the tests
mydriver =  webdriver.Firefox(firefox_profile=profile, log_path=logfile)
# ERROR HAPPENS HERE

# Doing other stuff here
....
# Doing tests here
....
# Doing shutdown here
mydriver.quit()

Вот полный пример такой ошибки:

___________ ERROR at setup of TestSuite.test_synaptic_events_fitting ___________

>   lambda: ihook(item=item, **kwds),
    when=when,
            )

/usr/local/lib/python2.7/dist-packages/flaky/flaky_pytest_plugin.py:273: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
conftest.py:157: in basedriver
    mydriver = firefox.get_driver(*args)
bsp_usecase_tests/tools/firefox.py:44: in get_driver
    driver = webdriver.Firefox(firefox_profile=profile, log_path=logfile)  #### INITIALIZING OF WEBDRIVER HERE
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/firefox/webdriver.py:158: in __init__
    keep_alive=True)
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py:154: in __init__
    self.start_session(desired_capabilities, browser_profile)
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py:243: in start_session
    response = self.execute(Command.NEW_SESSION, parameters)
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py:311: in execute
    self.error_handler.check_response(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7efd3b702f90>
response = {'status': 500, 'value': '{"value":{"error":"unknown error","message":"connection refused","stacktrace":"stack backtra...s::imp::thread::{{impl}}::new::thread_start\n                        at /checkout/src/libstd/sys/unix/thread.rs:84"}}'}

    def check_response(self, response):
        """
            Checks that a JSON response from the WebDriver does not have an error.

            :Args:
             - response - The JSON response from the WebDriver server as a dictionary
               object.

            :Raises: If the response contains an error message.
            """
        status = response.get('status', None)
        if status is None or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get('value', None)
            if value_json and isinstance(value_json, basestring):
                import json
                try:
                    value = json.loads(value_json)
                    if len(value.keys()) == 1:
                        value = value['value']
                    status = value.get('error', None)
                    if status is None:
                        status = value["status"]
                        message = value["value"]
                        if not isinstance(message, basestring):
                            value = message
                            message = message.get('message')
                    else:
                        message = value.get('message', None)
                except ValueError:
                    pass

        exception_class = ErrorInResponseException
        if status in ErrorCode.NO_SUCH_ELEMENT:
            exception_class = NoSuchElementException
        elif status in ErrorCode.NO_SUCH_FRAME:
            exception_class = NoSuchFrameException
        elif status in ErrorCode.NO_SUCH_WINDOW:
            exception_class = NoSuchWindowException
        elif status in ErrorCode.STALE_ELEMENT_REFERENCE:
            exception_class = StaleElementReferenceException
        elif status in ErrorCode.ELEMENT_NOT_VISIBLE:
            exception_class = ElementNotVisibleException
        elif status in ErrorCode.INVALID_ELEMENT_STATE:
            exception_class = InvalidElementStateException
        elif status in ErrorCode.INVALID_SELECTOR \
                or status in ErrorCode.INVALID_XPATH_SELECTOR \
                or status in ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPER:
            exception_class = InvalidSelectorException
        elif status in ErrorCode.ELEMENT_IS_NOT_SELECTABLE:
            exception_class = ElementNotSelectableException
        elif status in ErrorCode.ELEMENT_NOT_INTERACTABLE:
            exception_class = ElementNotInteractableException
        elif status in ErrorCode.INVALID_COOKIE_DOMAIN:
            exception_class = InvalidCookieDomainException
        elif status in ErrorCode.UNABLE_TO_SET_COOKIE:
            exception_class = UnableToSetCookieException
        elif status in ErrorCode.TIMEOUT:
            exception_class = TimeoutException
        elif status in ErrorCode.SCRIPT_TIMEOUT:
            exception_class = TimeoutException
        elif status in ErrorCode.UNKNOWN_ERROR:
            exception_class = WebDriverException
        elif status in ErrorCode.UNEXPECTED_ALERT_OPEN:
            exception_class = UnexpectedAlertPresentException
        elif status in ErrorCode.NO_ALERT_OPEN:
            exception_class = NoAlertPresentException
        elif status in ErrorCode.IME_NOT_AVAILABLE:
            exception_class = ImeNotAvailableException
        elif status in ErrorCode.IME_ENGINE_ACTIVATION_FAILED:
            exception_class = ImeActivationFailedException
        elif status in ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS:
            exception_class = MoveTargetOutOfBoundsException
        elif status in ErrorCode.JAVASCRIPT_ERROR:
            exception_class = JavascriptException
        elif status in ErrorCode.SESSION_NOT_CREATED:
            exception_class = SessionNotCreatedException
        elif status in ErrorCode.INVALID_ARGUMENT:
            exception_class = InvalidArgumentException
        elif status in ErrorCode.NO_SUCH_COOKIE:
            exception_class = NoSuchCookieException
        elif status in ErrorCode.UNABLE_TO_CAPTURE_SCREEN:
            exception_class = ScreenshotException
        elif status in ErrorCode.ELEMENT_CLICK_INTERCEPTED:
            exception_class = ElementClickInterceptedException
        elif status in ErrorCode.INSECURE_CERTIFICATE:
            exception_class = InsecureCertificateException
        elif status in ErrorCode.INVALID_COORDINATES:
            exception_class = InvalidCoordinatesException
        elif status in ErrorCode.INVALID_SESSION_ID:
            exception_class = InvalidSessionIdException
        elif status in ErrorCode.UNKNOWN_METHOD:
            exception_class = UnknownMethodException
        else:
            exception_class = WebDriverException
        if value == '' or value is None:
            value = response['value']
        if isinstance(value, basestring):
            if exception_class == ErrorInResponseException:
                raise exception_class(response, value)
            raise exception_class(value)
        if message == "" and 'message' in value:
            message = value['message']

        screen = None
        if 'screen' in value:
            screen = value['screen']

        stacktrace = None
        if 'stackTrace' in value and value['stackTrace']:
            stacktrace = []
            try:
                for frame in value['stackTrace']:
                    line = self._value_or_default(frame, 'lineNumber', '')
                    file = self._value_or_default(frame, 'fileName', '<anonymous>')
                    if line:
                        file = "%s:%s" % (file, line)
                    meth = self._value_or_default(frame, 'methodName', '<anonymous>')
                    if 'className' in frame:
                        meth = "%s.%s" % (frame['className'], meth)
                    msg = "    at %s (%s)"
                    msg = msg % (meth, file)
                    stacktrace.append(msg)
            except TypeError:
                pass
        if exception_class == ErrorInResponseException:
            raise exception_class(response, message)
        elif exception_class == UnexpectedAlertPresentException and 'alert' in value:
            raise exception_class(message, screen, stacktrace, value['alert'].get('text'))
>       raise exception_class(message, screen, stacktrace)
E       WebDriverException: Message: connection refused

/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/errorhandler.py:237: WebDriverException

Эти тесты выполняются как часть плана Дженкинса в док-контейнере, чтобы постоянно обеспечивать одинаковую среду.Вот список используемых пакетов и их версий:

  • python 2.7.12
  • pytest 3.6.1
  • селен 3.8.0
  • geckodriver 0.19.1
  • firefox 62.0
  • flaky 3.4.0

Ошибка примерно появляется примерно в 1% всех тестов.Существует около 15 различных тестов, и ошибка кажется случайной (т.е. не всегда один и тот же тест).

Это ошибка в firefox / selenium / geckodriver?И есть ли способ это исправить?

Следующий фрагмент кода не тот код, который я использую !Это просто идея, как исправить мою проблему, описанную выше.Может быть, это хороший способ решить мою первоначальную проблему или нет?

while counter<5:
    try:
        webdriver = webdriver.Firefox(firefox_profile=profile, log_path=logfile) 
        break
    except WebDriverException:
        counter +=1

Есть ли лучший способ сделать это?

1 Ответ

0 голосов
/ 27 сентября 2018

Это сообщение об ошибке ...

{'status': 500, 'value': '{"value":{"error":"unknown error","message":"connection refused","stacktrace":"stack backtra...s::imp::thread::{{impl}}::new::thread_start\n at /checkout/src/libstd/sys/unix/thread.rs:84"}}'}

... означает, что GeckoDriver не смог инициировать / порождать новый сеанс WebBrowsing , т.е. Браузер Firefox сессия.

В комментарии в обсуждении DELETE '/ session / {session id}' больше не работает @andreastt упоминает, что:

geckodriver неявно завершает (предыдущий) сеанс, когда закрывается последнее окно.Если в качестве последующей команды вызывается driver.quit(), произойдет сбой, поскольку сеанс уже был неявно удален.

В этих случаях GeckoDriver должен обнаружить, что сеанс был закрыт неявно после driver.close() или игнорировать ответ от driver.quit() в случае, если сеанс уже был закрыт.

Вв таких случаях создаются следующие журналы трассировки:

1505753594121   webdriver::server   DEBUG   Last window was closed, deleting session
1505753594121   webdriver::server   DEBUG   Deleting session
1505753594121   geckodriver::marionette DEBUG   Stopping browser process
1505753594364   webdriver::server   DEBUG   <- 200 OK {"value": []}
1505753594523   webdriver::server   DEBUG   -> DELETE /session/a8312282-af00-4931-94d4-0d401abf01c9 
1505753594524   webdriver::server   DEBUG   <- 500 Internal Server Error {"value":{"error":"session not created","message":"Tried to run command without establishing a connection","stacktrace":"stack backtrace:\n   0:           0x4f388c - backtrace::backtrace::trace::h736111741fa0878e\n   1:           0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9\n   2:           0x442c61 - webdriver::error::WebDriverError::new::hc4fe6a1ced4e57dd\n   3:           0x42a926 - <webdriver::server::Dispatcher<T, U>>::run::hba9181b5aacf8f04\n   4:           0x402c59 - std::sys_common::backtrace::__rust_begin_short_backtrace::h19de262639927233\n   5:           0x40c065 - std::panicking::try::do_call::h6c1659fc4d01af51\n   6:           0x5e38ec - panic_unwind::__rust_maybe_catch_panic\n                        at /checkout/src/libpanic_unwind/lib.rs:98\n   7:           0x420d32 - <F as alloc::boxed::FnBox<A>>::call_box::h953e5f59694972c5\n   8:           0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>\n                        at /checkout/src/liballoc/boxed.rs:661\n                         - std::sys_common::thread::start_thread\n                        at /checkout/src/libstd/sys_common/thread.rs:21\n                         - std::sys::imp::thread::{{impl}}::new::thread_start\n                        at /checkout/src/libstd/sys/unix/thread.rs:84"}}
1505753594533   webdriver::server   DEBUG   -> DELETE /session/a8312282-af00-4931-94d4-0d401abf01c9 
1505753594542   webdriver::server   DEBUG   <- 500 Internal Server Error {"value":{"error":"session not created","message":"Tried to run command without establishing a connection","stacktrace":"stack backtrace:\n   0:           0x4f388c - backtrace::backtrace::trace::h736111741fa0878e\n   1:           0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9\n   2:           0x442c61 - webdriver::error::WebDriverError::new::hc4fe6a1ced4e57dd\n   3:           0x42a926 - <webdriver::server::Dispatcher<T, U>>::run::hba9181b5aacf8f04\n   4:           0x402c59 - std::sys_common::backtrace::__rust_begin_short_backtrace::h19de262639927233\n   5:           0x40c065 - std::panicking::try::do_call::h6c1659fc4d01af51\n   6:           0x5e38ec - panic_unwind::__rust_maybe_catch_panic\n                        at /checkout/src/libpanic_unwind/lib.rs:98\n   7:           0x420d32 - <F as alloc::boxed::FnBox<A>>::call_box::h953e5f59694972c5\n   8:           0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>\n                        at /checkout/src/liballoc/boxed.rs:661\n                         - std::sys_common::thread::start_thread\n                        at /checkout/src/libstd/sys_common/thread.rs:21\n                         - std::sys::imp::thread::{{impl}}::new::thread_start\n                        at /checkout/src/libstd/sys/unix/thread.rs:84"}}
1505753594549   webdriver::server   DEBUG   -> GET /shutdown 
1505753594551   webdriver::server DEBUG <- 404 Not Found {"value":{"error":"unknown command","message":"GET /shutdown did not match a known command","stacktrace":"stack backtrace:\n 0: 0x4f388c - backtrace::backtrace::trace::h736111741fa0878e\n 1: 0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9\n 2: 0x442d88 - webdriver::error::WebDriverError::new::hea6d4dbf778b2b24\n 3: 0x43c65f - <webdriver::server::HttpHandler<U> as hyper::server::Handler>::handle::hd03629bd67672697\n 4: 0x403a04 - std::sys_common::backtrace::__rust_begin_short_backtrace::h32e6ff325c0d7f46\n 5: 0x40c036 - std::panicking::try::do_call::h5f902dc1eea01ffe\n 6: 0x5e38ec - panic_unwind::__rust_maybe_catch_panic\n at /checkout/src/libpanic_unwind/lib.rs:98\n 7: 0x4209a2 - <F as alloc::boxed::FnBox<A>>::call_box::h032bafb4b576d1cd\n 8: 0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>\n 

Хотя коды ошибок, которые вы видите, имеют вид 'status': 500 , и приведенный мной пример ошибки 404 Не найдено , видимо, выглядит иначе, основная причина похожа на:

"message":"connection refused" 

из-за:

imp::thread::{{impl}}::new::thread_start

от:

/checkout/src/libstd/sys/unix/thread.rs:84

С другой стороны, пока вы используете GeckoDriver , Selenium и Firefox , убедитесь, что двоичные файлы совместимы следующим образом:

Geckodriver Releases


Анализ

В бинарном файле geckodriver произошли значительные изменения с момента появления geckodriver 0.19.1 ,Пара изменений:

  • GeckoDriver v0.22.0 (2018-09-15):
    • Коды состояния HTTP, используемые для [script timeout]и [timeout] ошибки были изменены с Request Timeout (408) на Internal Server Error (500), чтобы не нарушать поддержку HTTP / 1.1 Keep-Alive, так как клиенты HTTP интерпретируют старый код состояния, чтобы означать, что они должны дублировать запрос.
    • Тайм-аут HTTP / 1.1 Keep-Alive для постоянных соединений увеличен до 90 секунд.
    • Теперь возвращается ошибка [недопустимый идентификатор сеанса], когда нет активного сеанса.
    • Рукопожатие, когда geckodriver подключается к Marionette, было усилено, убив процесс Firefox в случае его сбоя.
    • Тайм-аут чтения рукопожатия был сокращен до 10 секунд вместо ожидания навсегда.
  • GeckoDriver v0.21.0 (2018-06-15):
    • Команды WebDriver, которые не имеют возвращаемого значения, теперь корректно возвращают {value: null} вместо пустого словаря.
    • Принудительное использование сетевого стека IPv4.
    • В некоторых конфигурациях системы, где localhost преобразуется в адрес IPv6, geckodriver будет пытаться подключиться к Firefox в неправильном стеке IP,истечение времени ожидания попытки подключения через 60 секунд.Теперь мы гарантируем, что geckodriver использует IPv4 последовательно как для подключения к Firefox, так и для выделения свободного порта.

  • GeckoDriver v0.20.1 (2018-04-06):
    • Старайтесь не пытаться уничтожить остановленный процесс Firefox.
    • С изменением, позволяющим Firefox достаточно времени для отключения в 0.20.0,geckodriver начал безоговорочно убивать процесс, чтобы получить статус выхода.Это привело к тому, что geckodriver неверно сообщил об успешном завершении работы Firefox как о сбое.

  • GeckoDriver v0.20.0 (2018-03-08):
    • Обратные следы от geckodriver больше не заменяют отсутствующие трассировки стека Marionette.
    • Теперь процессу Firefox предоставляется достаточно времени для выключения, что дает достаточно времени для включения монитора зависания при отключении Firefox.
    • Firefox имеет встроенный фоновый монитор, который наблюдает за продолжительными потоками во время завершения работы.Эти темы будут убиты через 63 секунды в случае зависания.Чтобы позволить Firefox самостоятельно отключить эти потоки, geckodriver должен подождать это время и несколько дополнительных секунд.


Решение

  • Обновление Селен до текущих уровней Версия 3.14.0 .
  • Обновление GeckoDriver до GeckoDriver v0.22.0 уровень.
  • Обновление Firefox версия до Firefox v62.0.2 уровней.
  • Если ваша базовая веб-клиент версия слишком старая, то удалите ее через Revo Uninstaller и установите последнюю версию GA и выпущенную версию Web Client .
  • Всегда вызывайте driver.quit() в методе tearDown(){}, чтобы закрыть и уничтожить WebDriver и Web Client экземпляров изящно.
  • Выполните Test как пользователь без полномочий root.

Обновление

В соответствии с обновлением вашего вопроса - грубое, вы можете запустить цикл для нескольких испытаний для инициализации экземпляра селенового веб-драйвера следующим образом:

  • Убедитесь, что нет висящего экземпляраИз geckodriver , вызвав команду taskkill ( для WindowsOS ) следующим образом:

    os.system("taskkill /f /im geckodriver.exe /T") 
    
  • Убедитесь, что нет висячих экземпляров geckodriver , вызвав команду kill() ( Кроссплатформенный ) следующим образом:

    from selenium import webdriver
    import psutil
    from selenium.common.exceptions import WebDriverException
    
    for counter in range(5):
        try:
            webdriver = webdriver.Firefox(executable_path=r'C:\Utility\BrowserDrivers\geckodriver.exe') 
            print("WebDriver and WebBrowser initialized ...")
            break
        except WebDriverException:
            #Cross platform
            PROCNAME = "geckodriver"
            for proc in psutil.process_iter():
                # check whether the process name matches
                if proc.name() == PROCNAME:
                    proc.kill()        
            print("Retrying ...")
    print("Out of loop ...")
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...