Merge pull request #578 from seleniumbase/simplify-error-output

Simplify error output and fix Remote WebDriver issues
This commit is contained in:
Michael Mintz 2020-05-22 17:13:44 -04:00 committed by GitHub
commit a854d75b6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 187 additions and 77 deletions

View File

@ -3,7 +3,10 @@
<meta property="og:description" content="Simple browser automation and testing with Python." />
<meta property="og:image" content="https://seleniumbase.io/img/sb_logo_7.png" />
<link rel="icon" href="https://seleniumbase.io/img/logo3a.png" />
<p align="center"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.io/img/sb_logo_7.png" alt="SeleniumBase" width="260" /></a></p>
<p align="center"><a href="https://github.com/seleniumbase/SeleniumBase/">
<img src="https://seleniumbase.io/img/sb_logo_7.png" alt="SeleniumBase" width="260" />
</a></p>
<!-- View on GitHub -->
<p align="center">
<a href="https://github.com/seleniumbase/SeleniumBase/releases">

View File

@ -93,6 +93,15 @@ def main(*args, **kwargs):
if ' href="' in line and '.md"' in line:
changed = True
line = line.replace('.md"', '/"')
if "<!-- View on GitHub -->" in line:
changed = True
line = (
r'<p align="center"><div align="center">'
r'<a href="https://github.com/seleniumbase/SeleniumBase">'
r'<img src="https://img.shields.io/badge/'
r'%20💛%20View%20Code-on%20GitHub%20🌎%20🚀'
r'-02A79E.svg" alt="SeleniumBase.io Docs" />'
r'</a></div></p>')
seleniumbase_lines.append(line)
if changed:
out_file = codecs.open(readme_file, "w+", encoding='utf-8')

View File

@ -1,5 +1,5 @@
mkdocs==1.1.2
mkdocs-material==5.2.0
mkdocs-material==5.2.1
mkdocs-simple-hooks==0.1.1
mkdocs-material-extensions==1.0
mkdocs-minify-plugin==0.3.0

View File

@ -4,15 +4,16 @@
<h3>Table of Contents (<a href="https://seleniumbase.io">seleniumbase.io</a>)</h3>
<div><a href="https://seleniumbase.io/help_docs/features_list/"><b>SeleniumBase Features List</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/customizing_test_runs/"><b>The Command Line Tutorial</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/features_list/"><b>Features List</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/customizing_test_runs/"><b>Command Line Tutorial</b></a></div>
<div><a href="https://seleniumbase.io/examples/ReadMe/"><b>Usage Examples</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/how_it_works/"><b>How SeleniumBase Works</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/install_python_pip_git/"><b>Installing Python, Pip, & Git</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/virtualenv_instructions/"><b>Python Virtual Env Tutorial</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/install/"><b>SeleniumBase Installation</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/webdriver_installation/"><b>Webdriver Installation</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/verify_webdriver/"><b>Verify Webdriver Works</b></a></div>
<div><a href="https://seleniumbase.io/seleniumbase/console_scripts/ReadMe/"><b>The Console Scripts Tutorial</b></a></div>
<div><a href="https://seleniumbase.io/seleniumbase/console_scripts/ReadMe/"><b>Console Scripts Tutorial</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/mobile_testing/"><b>Mobile Device Testing</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/method_summary/"><b>Method Summary (API Ref)</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/translations/"><b>Language Translations</b></a></div>
@ -22,21 +23,22 @@
<div><a href="https://seleniumbase.io/help_docs/desired_capabilities/"><b>Browser Desired Capabilities</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/using_safari_driver/"><b>Safari Driver Detailed Info</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/hidden_files_info/"><b>Seeing Hidden Files on macOS</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/happy_customers/"><b>SeleniumBase Case Studies</b></a></div>
<div><a href="https://seleniumbase.io/help_docs/happy_customers/"><b>Case Studies</b></a></div>
--------
<h3>GitHub Pages ToC (<a href="https://seleniumbase.com">seleniumbase.com</a>)</h3>
<div><a href="https://seleniumbase.com/help_docs/features_list.html"><b>SeleniumBase Features List</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/customizing_test_runs.html"><b>The Command Line Tutorial</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/features_list.html"><b>Features List</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/customizing_test_runs.html"><b>Command Line Tutorial</b></a></div>
<div><a href="https://seleniumbase.com/examples/"><b>Usage Examples</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/how_it_works.html"><b>How SeleniumBase Works</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/install_python_pip_git.html"><b>Installing Python, Pip, & Git</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/virtualenv_instructions.html"><b>Python Virtual Env Tutorial</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/install.html"><b>SeleniumBase Installation</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/webdriver_installation.html"><b>Webdriver Installation</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/verify_webdriver.html"><b>Verify Webdriver Works</b></a></div>
<div><a href="https://seleniumbase.com/seleniumbase/console_scripts/"><b>The Console Scripts Tutorial</b></a></div>
<div><a href="https://seleniumbase.com/seleniumbase/console_scripts/"><b>Console Scripts Tutorial</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/mobile_testing.html"><b>Mobile Device Testing</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/method_summary.html"><b>Method Summary (API Ref)</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/translations.html"><b>Language Translations</b></a></div>
@ -46,4 +48,11 @@
<div><a href="https://seleniumbase.com/help_docs/desired_capabilities.html"><b>Browser Desired Capabilities</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/using_safari_driver.html"><b>Safari Driver Detailed Info</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/hidden_files_info.html"><b>Seeing Hidden Files on macOS</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/happy_customers.html"><b>SeleniumBase Case Studies</b></a></div>
<div><a href="https://seleniumbase.com/help_docs/happy_customers.html"><b>Case Studies</b></a></div>
--------
<p align="center"><a href="https://github.com/seleniumbase/SeleniumBase/">
<img src="https://seleniumbase.io/img/sb_logo_7.png" alt="SeleniumBase" width="260" />
</a></p>
<!-- View on GitHub -->

View File

@ -1,9 +1,10 @@
pip>=20.1
pip>=20.1.1
packaging>=20.4
setuptools>=44.1.0;python_version<"3.5"
setuptools>=46.4.0;python_version>="3.5"
setuptools-scm>=3.5.0
wheel>=0.34.2
six==1.14.0
six==1.15.0
nose==1.3.7
ipdb==0.13.2
idna==2.9
@ -15,7 +16,7 @@ pluggy==0.13.1
attrs>=19.3.0
pytest==4.6.10;python_version<"3.5"
pytest==5.4.2;python_version>="3.5"
pytest-cov==2.8.1
pytest-cov==2.9.0
pytest-forked==1.1.3
pytest-html==1.22.1;python_version<"3.6"
pytest-html==2.0.1;python_version>="3.6"
@ -33,9 +34,8 @@ cryptography==2.9.2
pyopenssl==19.1.0
pygments==2.5.2;python_version<"3.5"
pygments==2.6.1;python_version>="3.5"
packaging>=20.3
colorama==0.4.3
brython==3.8.8
brython>=3.8.9
pymysql==0.9.3
coverage==5.1
pyotp==2.3.0

View File

@ -451,7 +451,8 @@ def get_remote_driver(
capabilities[key] = desired_caps[key]
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities)
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.FIREFOX:
try:
# Use Geckodriver for Firefox if it's on the PATH
@ -469,7 +470,8 @@ def get_remote_driver(
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities,
browser_profile=profile)
browser_profile=profile,
keep_alive=True)
except WebDriverException:
# Don't use Geckodriver: Only works for old versions of Firefox
profile = _create_firefox_profile(
@ -485,35 +487,40 @@ def get_remote_driver(
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities,
browser_profile=profile)
browser_profile=profile,
keep_alive=True)
elif browser_name == constants.Browser.INTERNET_EXPLORER:
capabilities = webdriver.DesiredCapabilities.INTERNETEXPLORER
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities)
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.EDGE:
capabilities = webdriver.DesiredCapabilities.EDGE
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities)
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.SAFARI:
capabilities = webdriver.DesiredCapabilities.SAFARI
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities)
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.OPERA:
capabilities = webdriver.DesiredCapabilities.OPERA
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities)
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.PHANTOM_JS:
capabilities = webdriver.DesiredCapabilities.PHANTOMJS
for key in desired_caps.keys():
@ -523,32 +530,37 @@ def get_remote_driver(
warnings.simplefilter("ignore", category=UserWarning)
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities)
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.ANDROID:
capabilities = webdriver.DesiredCapabilities.ANDROID
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities)
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.IPHONE:
capabilities = webdriver.DesiredCapabilities.IPHONE
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities)
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.IPAD:
capabilities = webdriver.DesiredCapabilities.IPAD
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities)
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.REMOTE:
return webdriver.Remote(
command_executor=address,
desired_capabilities=desired_caps)
desired_capabilities=desired_caps,
keep_alive=True)
def get_local_driver(

View File

@ -2519,11 +2519,12 @@ class BaseCase(unittest.TestCase):
if now_ms >= stop_ms:
break
time.sleep(1)
self.assertTrue(
os.path.exists(self.get_path_of_downloaded_file(file)),
"File [%s] was not found in the downloads folder [%s] "
"after %s seconds! (Or the download didn't complete!)"
"" % (file, self.get_downloads_folder(), timeout))
if not os.path.exists(self.get_path_of_downloaded_file(file)):
message = (
"File {%s} was not found in the downloads folder {%s} "
"after %s seconds! (Or the download didn't complete!)"
"" % (file, self.get_downloads_folder(), timeout))
page_actions.timeout_exception("NoSuchFileException", message)
if self.demo_mode:
messenger_post = ("ASSERT DOWNLOADED FILE: [%s]" % file)
js_utils.post_messenger_success_message(
@ -3769,9 +3770,10 @@ class BaseCase(unittest.TestCase):
if now_ms >= stop_ms:
break
time.sleep(0.2)
raise Exception(
"Link text {%s} was not present after %s seconds!" % (
link_text, timeout))
message = (
"Link text {%s} was not present after %s seconds!"
"" % (link_text, timeout))
page_actions.timeout_exception("NoSuchElementException", message)
def wait_for_partial_link_text_present(self, link_text, timeout=None):
if not timeout:
@ -3790,9 +3792,10 @@ class BaseCase(unittest.TestCase):
if now_ms >= stop_ms:
break
time.sleep(0.2)
raise Exception(
"Partial Link text {%s} was not present after %s seconds!" % (
link_text, timeout))
message = (
"Partial Link text {%s} was not present after %s seconds!"
"" % (link_text, timeout))
page_actions.timeout_exception("NoSuchElementException", message)
def wait_for_link_text_visible(self, link_text, timeout=None):
if not timeout:

View File

@ -1,7 +1,8 @@
"""
This module contains test-state related exceptions.
Raising one of these in a test will cause the
test-state to be logged appropriately.
test-state to be logged appropriately in the database
for tests that use the SeleniumBase MySQL plugin.
"""

View File

@ -113,6 +113,34 @@ def hover_element(driver, element):
hover.perform()
def format_message(exception, message):
"""
Formats an exception message to make the output cleaner.
"""
if exception == Exception:
pass
elif exception == ElementNotVisibleException:
message = "ElementNotVisibleException: %s" % message
elif exception == NoSuchElementException:
message = "NoSuchElementException: %s" % message
elif exception == NoAlertPresentException:
message = "NoAlertPresentException: %s" % message
elif exception == NoSuchFrameException:
message = "NoSuchFrameException: %s" % message
elif exception == NoSuchWindowException:
message = "NoSuchWindowException: %s" % message
elif type(exception) is str:
message = "%s: %s" % (exception, message)
else:
pass
return message
def timeout_exception(exception, message):
message = format_message(exception, message)
raise Exception(message)
def hover_and_click(driver, hover_selector, click_selector,
hover_by=By.CSS_SELECTOR, click_by=By.CSS_SELECTOR,
timeout=settings.SMALL_TIMEOUT):
@ -145,9 +173,10 @@ def hover_and_click(driver, hover_selector, click_selector,
plural = "s"
if timeout == 1:
plural = ""
raise NoSuchElementException(
"Element {%s} was not present after %s second%s!" % (
click_selector, timeout, plural))
message = (
"Element {%s} was not present after %s second%s!"
"" % (click_selector, timeout, plural))
timeout_exception(NoSuchElementException, message)
def hover_element_and_click(driver, element, click_selector,
@ -173,9 +202,10 @@ def hover_element_and_click(driver, element, click_selector,
plural = "s"
if timeout == 1:
plural = ""
raise NoSuchElementException(
"Element {%s} was not present after %s second%s!" % (
click_selector, timeout, plural))
message = (
"Element {%s} was not present after %s second%s!"
"" % (click_selector, timeout, plural))
timeout_exception(NoSuchElementException, message)
def hover_element_and_double_click(driver, element, click_selector,
@ -201,9 +231,10 @@ def hover_element_and_double_click(driver, element, click_selector,
plural = "s"
if timeout == 1:
plural = ""
raise NoSuchElementException(
"Element {%s} was not present after %s second%s!" % (
click_selector, timeout, plural))
message = (
"Element {%s} was not present after %s second%s!"
"" % (click_selector, timeout, plural))
timeout_exception(NoSuchElementException, message)
def wait_for_element_present(driver, selector, by=By.CSS_SELECTOR,
@ -238,9 +269,10 @@ def wait_for_element_present(driver, selector, by=By.CSS_SELECTOR,
if timeout == 1:
plural = ""
if not element:
raise NoSuchElementException(
"Element {%s} was not present after %s second%s!" % (
selector, timeout, plural))
message = (
"Element {%s} was not present after %s second%s!"
"" % (selector, timeout, plural))
timeout_exception(NoSuchElementException, message)
def wait_for_element_visible(driver, selector, by=By.CSS_SELECTOR,
@ -280,13 +312,15 @@ def wait_for_element_visible(driver, selector, by=By.CSS_SELECTOR,
if timeout == 1:
plural = ""
if not element and by != By.LINK_TEXT:
raise ElementNotVisibleException(
"Element {%s} was not visible after %s second%s!" % (
selector, timeout, plural))
message = (
"Element {%s} was not visible after %s second%s!"
"" % (selector, timeout, plural))
timeout_exception(ElementNotVisibleException, message)
if not element and by == By.LINK_TEXT:
raise ElementNotVisibleException(
"Link text {%s} was not visible after %s second%s!" % (
selector, timeout, plural))
message = (
"Link text {%s} was not visible after %s second%s!"
"" % (selector, timeout, plural))
timeout_exception(ElementNotVisibleException, message)
def wait_for_text_visible(driver, text, selector, by=By.CSS_SELECTOR,
@ -326,9 +360,10 @@ def wait_for_text_visible(driver, text, selector, by=By.CSS_SELECTOR,
if timeout == 1:
plural = ""
if not element:
raise ElementNotVisibleException(
"Expected text {%s} for {%s} was not visible after %s second%s!" %
(text, selector, timeout, plural))
message = (
"Expected text {%s} for {%s} was not visible after %s second%s!"
"" % (text, selector, timeout, plural))
timeout_exception(ElementNotVisibleException, message)
def wait_for_exact_text_visible(driver, text, selector, by=By.CSS_SELECTOR,
@ -369,9 +404,10 @@ def wait_for_exact_text_visible(driver, text, selector, by=By.CSS_SELECTOR,
if timeout == 1:
plural = ""
if not element:
raise ElementNotVisibleException(
message = (
"Expected exact text {%s} for {%s} was not visible "
"after %s second%s!" % (text, selector, timeout, plural))
timeout_exception(ElementNotVisibleException, message)
def wait_for_element_absent(driver, selector, by=By.CSS_SELECTOR,
@ -401,8 +437,10 @@ def wait_for_element_absent(driver, selector, by=By.CSS_SELECTOR,
plural = "s"
if timeout == 1:
plural = ""
raise Exception("Element {%s} was still present after %s second%s!" %
(selector, timeout, plural))
message = (
"Element {%s} was still present after %s second%s!"
"" % (selector, timeout, plural))
timeout_exception(Exception, message)
def wait_for_element_not_visible(driver, selector, by=By.CSS_SELECTOR,
@ -435,9 +473,10 @@ def wait_for_element_not_visible(driver, selector, by=By.CSS_SELECTOR,
plural = "s"
if timeout == 1:
plural = ""
raise Exception(
"Element {%s} was still visible after %s second%s!" % (
selector, timeout, plural))
message = (
"Element {%s} was still visible after %s second%s!"
"" % (selector, timeout, plural))
timeout_exception(Exception, message)
def wait_for_text_not_visible(driver, text, selector, by=By.CSS_SELECTOR,
@ -468,8 +507,10 @@ def wait_for_text_not_visible(driver, text, selector, by=By.CSS_SELECTOR,
plural = "s"
if timeout == 1:
plural = ""
raise Exception("Text {%s} in {%s} was still visible after %s "
"second%s!" % (text, selector, timeout, plural))
message = (
"Text {%s} in {%s} was still visible after %s "
"second%s!" % (text, selector, timeout, plural))
timeout_exception(Exception, message)
def find_visible_elements(driver, selector, by=By.CSS_SELECTOR):
@ -634,7 +675,9 @@ def wait_for_and_switch_to_alert(driver, timeout=settings.LARGE_TIMEOUT):
if now_ms >= stop_ms:
break
time.sleep(0.1)
raise Exception("Alert was not present after %s seconds!" % timeout)
message = (
"Alert was not present after %s seconds!" % timeout)
timeout_exception(Exception, message)
def switch_to_frame(driver, frame, timeout=settings.SMALL_TIMEOUT):
@ -671,7 +714,13 @@ def switch_to_frame(driver, frame, timeout=settings.SMALL_TIMEOUT):
if now_ms >= stop_ms:
break
time.sleep(0.1)
raise Exception("Frame was not present after %s seconds!" % timeout)
plural = "s"
if timeout == 1:
plural = ""
message = (
"Frame {%s} was not visible after %s second%s!"
"" % (frame, timeout, plural))
timeout_exception(Exception, message)
def switch_to_window(driver, window, timeout=settings.SMALL_TIMEOUT):
@ -697,7 +746,13 @@ def switch_to_window(driver, window, timeout=settings.SMALL_TIMEOUT):
if now_ms >= stop_ms:
break
time.sleep(0.1)
raise Exception("Window was not present after %s seconds!" % timeout)
plural = "s"
if timeout == 1:
plural = ""
message = (
"Window {%s} was not present after %s second%s!"
"" % (window, timeout, plural))
timeout_exception(Exception, message)
else:
window_handle = window
for x in range(int(timeout * 10)):
@ -710,4 +765,10 @@ def switch_to_window(driver, window, timeout=settings.SMALL_TIMEOUT):
if now_ms >= stop_ms:
break
time.sleep(0.1)
raise Exception("Window was not present after %s seconds!" % timeout)
plural = "s"
if timeout == 1:
plural = ""
message = (
"Window {%s} was not present after %s second%s!"
"" % (window, timeout, plural))
timeout_exception(Exception, message)

View File

@ -934,6 +934,18 @@ class MD:
md["hover_and_click"][8] = "наведите_и_нажмите"
md["hover_and_click"][9] = "pasar_el_ratón_y_hacer_clic"
md["is_selected"] = ["*"] * num_langs
md["is_selected"][0] = "is_selected"
md["is_selected"][1] = "是否被选中"
md["is_selected"][2] = "is_het_geselecteerd"
md["is_selected"][3] = "est_il_sélectionné"
md["is_selected"][4] = "è_selezionato"
md["is_selected"][5] = "選択されていることを"
md["is_selected"][6] = "선택되어_있는지"
md["is_selected"][7] = "é_selecionado"
md["is_selected"][8] = "выбран"
md["is_selected"][9] = "está_seleccionado"
md["press_right_arrow"] = ["*"] * num_langs
md["press_right_arrow"][0] = "press_right_arrow"
md["press_right_arrow"][1] = "按向右箭头"

View File

@ -54,7 +54,7 @@ if sys.argv[-1] == 'publish':
setup(
name='seleniumbase',
version='1.38.4',
version='1.38.5',
description='Fast, Easy, and Reliable Browser Automation & Testing.',
long_description=long_description,
long_description_content_type='text/markdown',
@ -90,7 +90,8 @@ setup(
"Topic :: Utilities",
],
install_requires=[
'pip>=20.1',
'pip>=20.1.1',
'packaging>=20.4',
'setuptools',
'setuptools-scm',
'wheel',
@ -106,7 +107,7 @@ setup(
'attrs>=19.3.0',
'pytest==4.6.10;python_version<"3.5"',
'pytest==5.4.2;python_version>="3.5"',
'pytest-cov==2.8.1',
'pytest-cov==2.9.0',
'pytest-forked==1.1.3',
'pytest-html==1.22.1;python_version<"3.6"',
'pytest-html==2.0.1;python_version>="3.6"',
@ -124,9 +125,8 @@ setup(
'pyopenssl==19.1.0',
'pygments==2.5.2;python_version<"3.5"',
'pygments==2.6.1;python_version>="3.5"',
'packaging>=20.3',
'colorama==0.4.3',
'brython==3.8.8',
'brython>=3.8.9',
'pymysql==0.9.3',
'coverage==5.1',
'pyotp==2.3.0',