Alright guys... I know this issue has seemingly been answered before but I have tried several solutions and none have worked. If I see StaleElementReferenceException one more time, I'm going to lose it. My wife's boyfriend is mocking me, please help.
EDIT: Removed some code as I don't think it's relevant anymore in an effort to condense this post.
UPDATE: From @vitaliis suggestion below, I changed the code to:
while True:
xpath_atc = '//div[@class="fulfillment-add-to-cart-button"]'
try:
wait = WebDriverWait(self.driver, 60)
wait.until(EC.visibility_of_all_elements_located((By.XPATH, xpath_atc)))
except TimeoutException:
print(f'A timeout occured after 60s. Refreshing page and trying again...')
self.driver.refresh()
continue
element_arr = self.driver.find_elements_by_xpath(xpath_atc)
for i in range(len(element_arr)):
wait.until(EC.visibility_of(element_arr[i]))
if element_arr[i].text == 'Add to Cart':
element_arr[i].click()
return
time.sleep(30)
self.driver.refresh()
Now, this code seems to be the most effective code I've tried so far in that it produces less StaleElementReferenceExceptions but it still does produce them...
I am seriously so confused, it's inconsistent, sometimes the exception occurs, sometimes it doesn't. Any other thoughts on this issue would be appreciated.
SOLUTION: tldr; This works
element = self.driver.find_element_by_xpath(xpath)
while(EC.staleness_of(disc_ele).__call__(False)):
element = self.driver.find_element_by_xpath(xpath)
text = element.text
If anyone is interested in why this works, I'll give my best guess. The staleness_of class is defined as follows:
class staleness_of(object):
""" Wait until an element is no longer attached to the DOM.
element is the element to wait for.
returns False if the element is still attached to the DOM, true otherwise.
"""
def __init__(self, element):
self.element = element
def __call__(self, ignored):
try:
# Calling any method forces a staleness check
self.element.is_enabled()
return False
except StaleElementReferenceException:
return True
When used in conjunction with the WebDriverWait class, i.e WebDriverWait(driver, timeout).until(EC.staleness_of(element)) we are waiting for the element to be stale which is what we're looking for right? Well, due to the inconsistency of the stale exception, the element may not be stale, and if it is not stale, then we'll get a timeout exception which is not the desired result.
Luckily, the staleness_of class has a method __call__() which returns true if the element is stale and false otherwise. So, using a while loop, we can continually check if the element is stale by updating the element each iteration and checking staleness with the __call__() method on the updated element. Eventually, the updated element will not be stale and the __call__() method will return false exiting the loop.
EDIT: Welp, doesn't work. I guess there's a case where the element is not stale at time of the while loop condition but becomes stale at time of calling .text.