Reputation: 967
I'm using Selenium with Python to test a web app; however, Firefox will periodically go in the "Not Responding" state. While Firefox is in this state the script will hang until I close it.
I tried:
br = webdriver.Firefox()
br.set_page_load_timeout(10)
and
br = webdriver.Firefox()
br.implicitly_wait(10)
I fear my issue won't be solved by a wait
or timeout
method since the page is Ajax. The .click()
or .submit()
method sends a POST
to answer a question and the response is a dictionary which via JavaScript updates the page with a new question. (This is what I understand from using the Network tab in Google Chrome Developer Tools Ctrl+Shift+I)
The question is:
How do I catch the Firefox Not Responding situation in Selenium?
I wish to catch this situation so I can close the browser and start a new one.
Edited after ExperimentsWithCode's answer:
Here is a snippet of my code to illustrate my problem.
def answer_questions(self):
q = QuestionParser(self.br.page_source)
q.add_to_db()
qid = q.get_qid()
# My answer
my_answer_id = q.get_my_answer()
self.br.find_element_by_id(my_answer_id).click()
# Submit answer
self.br.find_element_by_xpath('//*[contains(@id, "submit_btn_")]').click()
try:
find_id = "submit_btn_" + str(qid)
element = WebDriverWait(self.br,10).until(EC.presence_of_element_located((By.ID, find_id)))
except:
print 'I caught you'
self.rnd(mu=7, s=2) # a method to sleep for a random time
I call the answer_questions()
method in a loop. This code runs fine until Firefox crashes then it will hang until I close it. I read the docs and fear I've exhausted everything. The problem is not loading a page or element. The problem is that I don't know how to detect when Firefox crashes. WebDriverWait
seemed promising but it did not throw a TimeoutException
when Firefox crashed.
Thank you for your time.
I devised a remedy to my problem. Here is a snippet what I did:
import os, socket
from threading import Timer
def force_timeout(self):
os.system('taskkill /im firefox.exe /f /t')
# for Windows
# /im firefox.exe is to select firefox
# /f is to forcefully kill
# /t is to kill all child processes
for i in range(0, 1000):
try:
t = Timer(60, self.force_timeout)
# run force_timeout from new thread after 60 seconds
t.start() # start timer
self.answer_questions()
# if self.answer_questions() finishes before 60s, cancel timer
t.cancel() # cancel timer
except socket.error:
self.new_browser() # create a new browser
self.login() # login
self.go_to_questions() # Go to questions page so the loop can continue
# Code to recover from crash here
If the answer_questions()
method does not finish in 60 seconds the force_timeout()
will run from a new thread and forcefully kill firefox.exe
. Once Firefox is terminated the code will throw a socket.error
error. Catch this error then do what you need to do to recover from a browser crashing. I find the code to be a bit dirty but it's working exactly as i need it to.
For future reference: OS - Win8, Selenium Version - 2.40.0, Firefox Version - 27.0.1
Windows Taskkill
Python threading.Timer
Upvotes: 15
Views: 9380
Reputation: 19
I don't know how to explain the often unpredictable unresponsiveness of Selenium, but I can say that quitting or closing the driver (driver.quit()
or driver.close()
) doesn't help, because the object and the process that controls the browser is still alive.
I recommend deleting the object (del driver
) and re-creating a new driver.
Upvotes: 0
Reputation: 245
For those of you on *nix,
os.system("pkill -P %d firefox" % os.getpid())
works nicely to kill the Firefox children of your current process.
Upvotes: 0
Reputation: 1184
The Webdriver docs have a section on things like this here.
From the docs, you could try an Explicit Wait:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0
br = webdriver.Firefox()
br.get("http://somedomain/url_that_delays_loading")
try:
element = WebDriverWait(br, 10).until(EC.presence_of_element_located((By.ID, "_element_You_Are_Need_To_Load")))
finally:
br.quit()
This will time out if the element cannot be found in 10 seconds, or return the element within that window.
Upvotes: 1