Skip to content

Commit 5bb03c1

Browse files
authored
Merge pull request #202 from seleniumbase/python-3.7.0-compatibility
Python 3.7.0 compatibility
2 parents 492f703 + cd830b1 commit 5bb03c1

File tree

12 files changed

+130
-60
lines changed

12 files changed

+130
-60
lines changed

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ setuptools
33
ipython==5.6.0
44
selenium==3.14.0
55
nose==1.3.7
6-
pytest==3.7.3
6+
pytest==3.7.4
77
pytest-html==1.19.0
88
pytest-xdist==1.23.0
99
six==1.11.0
@@ -12,6 +12,7 @@ requests==2.19.1
1212
beautifulsoup4==4.6.0
1313
unittest2==1.1.0
1414
chardet==3.0.4
15+
urllib3==1.23
1516
boto==2.48.0
1617
ipdb==0.11
1718
parameterized==0.6.1

seleniumbase/common/encryption.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ def reverse_shuffle_string(string):
4545
return string
4646
new_string = ""
4747
odd = (len(string) % 2 == 1)
48-
part1 = string[:int(len(string)/2):1]
49-
part2 = string[int(len(string)/2)::1]
48+
part1 = string[:int(len(string) / 2):1]
49+
part2 = string[int(len(string) / 2)::1]
5050
for c in range(len(part1)):
5151
new_string += part2[c]
5252
new_string += part1[c]
@@ -118,18 +118,18 @@ def decrypt(string):
118118
rem4 = (len(string) + ord_string_sum(string)) % 2
119119
if len(string) % 2 != 0:
120120
if rem3 == 1:
121-
string = (chr(ord(string[-1])-5-rem1) + string +
122-
chr(ord(string[-1])-13-rem1))
121+
string = (chr(ord(string[-1]) - 5 - rem1) + string +
122+
chr(ord(string[-1]) - 13 - rem1))
123123
else:
124-
string = (chr(ord(string[-1])-11-rem1) + string +
125-
chr(ord(string[-1])-23-rem1))
124+
string = (chr(ord(string[-1]) - 11 - rem1) + string +
125+
chr(ord(string[-1]) - 23 - rem1))
126126
elif len(string) > 1:
127127
if rem4 == 1:
128-
string = (chr(ord(string[0])-19+rem2) + string +
129-
chr(ord(string[0])-7-rem2))
128+
string = (chr(ord(string[0]) - 19 + rem2) + string +
129+
chr(ord(string[0]) - 7 - rem2))
130130
else:
131-
string = (chr(ord(string[0])-26+rem2) + string +
132-
chr(ord(string[0])-12-rem2))
131+
string = (chr(ord(string[0]) - 26 + rem2) + string +
132+
chr(ord(string[0]) - 12 - rem2))
133133
rem5 = (len(string) + ord_string_sum(string)) % 23
134134
string = rotate(string, rem5)
135135
result = str_xor(shuffle_string(string)[::-1], xor_key)

seleniumbase/console_scripts/sb_install.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@
1919
import shutil
2020
import sys
2121
import tarfile
22+
import urllib3
2223
import zipfile
2324
from seleniumbase import drivers # webdriver storage folder for SeleniumBase
24-
if sys.version_info[0] == 2:
25-
from urllib import urlopen
26-
else:
27-
from urllib.request import urlopen
25+
urllib3.disable_warnings()
2826
DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__))
2927

3028

@@ -62,7 +60,7 @@ def main():
6260
invalid_run_command()
6361
else:
6462
invalid_run_command()
65-
name = sys.argv[num_args-1]
63+
name = sys.argv[num_args - 1]
6664

6765
file_name = None
6866
download_url = None
@@ -190,7 +188,8 @@ def main():
190188
if not os.path.exists(downloads_folder):
191189
os.mkdir(downloads_folder)
192190
local_file = open(file_path, 'wb')
193-
remote_file = urlopen(download_url)
191+
http = urllib3.PoolManager()
192+
remote_file = http.request('GET', download_url, preload_content=False)
194193
print('\nDownloading %s from:\n%s ...' % (file_name, download_url))
195194
local_file.write(remote_file.read())
196195
local_file.close()

seleniumbase/console_scripts/sb_mkdir.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def main():
4040
invalid_run_command()
4141
else:
4242
invalid_run_command()
43-
dir_name = sys.argv[num_args-1]
43+
dir_name = sys.argv[num_args - 1]
4444
if len(str(dir_name)) < 2:
4545
raise Exception('Directory name length must be at least 2 '
4646
'characters long!')

seleniumbase/core/style_sheet.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
<title>Test Report</title>
33
<link rel="SHORTCUT ICON"
44
href="%s" /> ''' % (
5-
"https://raw.githubusercontent.com/seleniumbase/SeleniumBase"
6-
"/master/seleniumbase/resources/favicon.ico")
5+
"https://raw.githubusercontent.com/seleniumbase/SeleniumBase"
6+
"/master/seleniumbase/resources/favicon.ico")
77

88
style = title + '''<style type="text/css">
99
html {

seleniumbase/core/testcase_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def get_params(self):
8484
"total_execution_time": self.total_execution_time,
8585
"username": self.username,
8686
"guid": self.guid
87-
}
87+
}
8888

8989

9090
class TestcaseDataPayload:

seleniumbase/fixtures/base_case.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -755,7 +755,9 @@ def add_css_style(self, css_style):
755755
'''s.type = "text/css";'''
756756
'''s.appendChild(document.createTextNode("%s"));'''
757757
'''h.appendChild(s);''')
758-
self.execute_script(add_css_style_script % re.escape(css_style))
758+
css_style = re.escape(css_style)
759+
css_style = self.__escape_quotes_if_needed(css_style)
760+
self.execute_script(add_css_style_script % css_style)
759761

760762
def add_js_code_from_link(self, js_link):
761763
if js_link.startswith("//"):
@@ -768,7 +770,9 @@ def add_js_code_from_link(self, js_link):
768770
'''s.onload = function() { null };'''
769771
'''s.appendChild(document.createTextNode("%s"));'''
770772
'''h.appendChild(s);''')
771-
self.execute_script(add_js_code_script % re.escape(js_code))
773+
js_code = re.escape(js_code)
774+
js_code = self.__escape_quotes_if_needed(js_code)
775+
self.execute_script(add_js_code_script % js_code)
772776

773777
def add_meta_tag(self, http_equiv=None, content=None):
774778
if http_equiv is None:
@@ -812,6 +816,12 @@ def activate_jquery(self):
812816
'''of the website's Content Security Policy '''
813817
'''directive. ''' % self.driver.current_url)
814818

819+
def __are_quotes_escaped(self, string):
820+
return page_utils.are_quotes_escaped(string)
821+
822+
def __escape_quotes_if_needed(self, string):
823+
return page_utils.escape_quotes_if_needed(string)
824+
815825
def __activate_bootstrap(self):
816826
""" Allows you to use Bootstrap Tours with SeleniumBase
817827
http://bootstraptour.com/
@@ -995,6 +1005,7 @@ def add_tour_step(self, message, selector=None, name=None,
9951005
if not selector:
9961006
selector = "html"
9971007
selector = re.escape(selector)
1008+
selector = self.__escape_quotes_if_needed(selector)
9981009

9991010
if not name:
10001011
name = "default"
@@ -1005,9 +1016,11 @@ def add_tour_step(self, message, selector=None, name=None,
10051016
if not title:
10061017
title = ""
10071018
title = re.escape(title)
1019+
title = self.__escape_quotes_if_needed(title)
10081020

10091021
if message:
10101022
message = re.escape(message)
1023+
message = self.__escape_quotes_if_needed(message)
10111024
else:
10121025
message = ""
10131026

@@ -1324,6 +1337,7 @@ def __wait_for_css_query_selector(
13241337
for x in range(int(timeout * 10)):
13251338
try:
13261339
selector = re.escape(selector)
1340+
selector = self.__escape_quotes_if_needed(selector)
13271341
element = self.execute_script(
13281342
"""return document.querySelector('%s')""" % selector)
13291343
if element:
@@ -1439,9 +1453,11 @@ def post_message(self, message, style="info", duration=None):
14391453
duration = settings.DEFAULT_MESSAGE_DURATION
14401454
else:
14411455
duration = self.message_duration
1456+
message = re.escape(message)
1457+
message = self.__escape_quotes_if_needed(message)
14421458
messenger_script = ('''Messenger().post({message: "%s", type: "%s", '''
14431459
'''hideAfter: %s, hideOnNavigate: true});'''
1444-
% (re.escape(message), style, duration))
1460+
% (message, style, duration))
14451461
try:
14461462
self.execute_script(messenger_script)
14471463
except Exception:
@@ -1481,6 +1497,7 @@ def get_property_value(self, selector, property, by=By.CSS_SELECTOR,
14811497
"Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!" % (
14821498
selector, by))
14831499
selector = re.escape(selector)
1500+
selector = self.__escape_quotes_if_needed(selector)
14841501
script = ("""var $elm = document.querySelector('%s');
14851502
$val = window.getComputedStyle($elm).getPropertyValue('%s');
14861503
return $val;"""
@@ -1505,6 +1522,7 @@ def bring_to_front(self, selector, by=By.CSS_SELECTOR):
15051522
# Don't run action if can't convert to CSS_Selector for JavaScript
15061523
return
15071524
selector = re.escape(selector)
1525+
selector = self.__escape_quotes_if_needed(selector)
15081526
script = ("""document.querySelector('%s').style.zIndex = '9999';"""
15091527
% selector)
15101528
self.execute_script(script)
@@ -1560,10 +1578,12 @@ def highlight(self, selector, by=By.CSS_SELECTOR,
15601578

15611579
if ":contains" not in selector and ":first" not in selector:
15621580
selector = re.escape(selector)
1581+
selector = self.__escape_quotes_if_needed(selector)
15631582
self.__highlight_with_js(selector, loops, o_bs)
15641583
else:
15651584
selector = self.__make_css_match_first_element_only(selector)
15661585
selector = re.escape(selector)
1586+
selector = self.__escape_quotes_if_needed(selector)
15671587
try:
15681588
self.__highlight_with_jquery(selector, loops, o_bs)
15691589
except Exception:
@@ -1703,6 +1723,7 @@ def js_click(self, selector, by=By.CSS_SELECTOR):
17031723
self.__scroll_to_element(element)
17041724
css_selector = self.convert_to_css_selector(selector, by=by)
17051725
css_selector = re.escape(css_selector)
1726+
css_selector = self.__escape_quotes_if_needed(css_selector)
17061727
self.__js_click(selector, by=by) # The real "magic" happens here
17071728
self.__demo_mode_pause_if_active()
17081729

@@ -1768,6 +1789,7 @@ def ad_block(self):
17681789
from seleniumbase.config import ad_block_list
17691790
for css_selector in ad_block_list.AD_BLOCK_LIST:
17701791
css_selector = re.escape(css_selector)
1792+
css_selector = self.__escape_quotes_if_needed(css_selector)
17711793
script = ("""var $elements = document.querySelectorAll('%s');
17721794
var index = 0, length = $elements.length;
17731795
for(; index < length; index++){
@@ -1879,7 +1901,9 @@ def set_value(self, selector, new_value, by=By.CSS_SELECTOR,
18791901
if not self.demo_mode:
18801902
self.scroll_to(orginal_selector, by=by, timeout=timeout)
18811903
value = re.escape(new_value)
1904+
value = self.__escape_quotes_if_needed(value)
18821905
css_selector = re.escape(css_selector)
1906+
css_selector = self.__escape_quotes_if_needed(css_selector)
18831907
script = ("""document.querySelector('%s').value='%s';"""
18841908
% (css_selector, value))
18851909
self.execute_script(script)
@@ -1915,8 +1939,11 @@ def jquery_update_text_value(self, selector, new_value, by=By.CSS_SELECTOR,
19151939
self.scroll_to(selector, by=by)
19161940
selector = self.convert_to_css_selector(selector, by=by)
19171941
selector = self.__make_css_match_first_element_only(selector)
1942+
selector = self.__escape_quotes_if_needed(selector)
1943+
new_value = re.escape(new_value)
1944+
new_value = self.__escape_quotes_if_needed(new_value)
19181945
update_text_script = """jQuery('%s').val('%s')""" % (
1919-
selector, re.escape(new_value))
1946+
selector, new_value)
19201947
self.safe_execute_script(update_text_script)
19211948
if new_value.endswith('\n'):
19221949
element.send_keys('\n')
@@ -1967,8 +1994,8 @@ def hover_and_click(self, hover_selector, click_selector,
19671994
self.scroll_to(hover_selector, by=hover_by)
19681995
pre_action_url = self.driver.current_url
19691996
element = page_actions.hover_and_click(
1970-
self.driver, hover_selector, click_selector,
1971-
hover_by, click_by, timeout)
1997+
self.driver, hover_selector, click_selector,
1998+
hover_by, click_by, timeout)
19721999
if self.demo_mode:
19732000
if self.driver.current_url != pre_action_url:
19742001
self.__demo_mode_pause_if_active()
@@ -2557,8 +2584,8 @@ def __add_delayed_assert_failure(self):
25572584
current_url = self.driver.current_url
25582585
message = self.__get_exception_message()
25592586
self.__page_check_failures.append(
2560-
"CHECK #%s: (%s)\n %s" % (
2561-
self.__page_check_count, current_url, message))
2587+
"CHECK #%s: (%s)\n %s" % (
2588+
self.__page_check_count, current_url, message))
25622589

25632590
def delayed_assert_element(self, selector, by=By.CSS_SELECTOR,
25642591
timeout=settings.MINI_TIMEOUT):
@@ -2650,6 +2677,7 @@ def __js_click(self, selector, by=By.CSS_SELECTOR):
26502677
selector, by = self.__recalculate_selector(selector, by)
26512678
css_selector = self.convert_to_css_selector(selector, by=by)
26522679
css_selector = re.escape(css_selector)
2680+
css_selector = self.__escape_quotes_if_needed(css_selector)
26532681
script = ("""var simulateClick = function (elem) {
26542682
var evt = new MouseEvent('click', {
26552683
bubbles: true,
@@ -2775,7 +2803,7 @@ def __demo_mode_pause_if_active(self, tiny=False):
27752803
if not tiny:
27762804
time.sleep(wait_time)
27772805
else:
2778-
time.sleep(wait_time/3.4)
2806+
time.sleep(wait_time / 3.4)
27792807

27802808
def __demo_mode_scroll_if_active(self, selector, by):
27812809
if self.demo_mode:
@@ -2876,10 +2904,12 @@ def __highlight_with_assert_success(
28762904

28772905
if ":contains" not in selector and ":first" not in selector:
28782906
selector = re.escape(selector)
2907+
selector = self.__escape_quotes_if_needed(selector)
28792908
self.__highlight_with_js_2(message, selector, o_bs)
28802909
else:
28812910
selector = self.__make_css_match_first_element_only(selector)
28822911
selector = re.escape(selector)
2912+
selector = self.__escape_quotes_if_needed(selector)
28832913
try:
28842914
self.__highlight_with_jquery_2(message, selector, o_bs)
28852915
except Exception:

seleniumbase/fixtures/page_utils.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,37 @@ def _save_data_as(data, destination_folder, file_name):
8383
out_file.close()
8484

8585

86+
def are_quotes_escaped(string):
87+
if (string.count("\\'") != string.count("'") or
88+
string.count('\\"') != string.count('"')):
89+
return True
90+
return False
91+
92+
93+
def escape_quotes_if_needed(string):
94+
"""
95+
re.escape() works differently in Python 3.7.0 than earlier versions:
96+
97+
Python 3.6.5:
98+
>>> import re
99+
>>> re.escape('"')
100+
'\\"'
101+
102+
Python 3.7.0:
103+
>>> import re
104+
>>> re.escape('"')
105+
'"'
106+
107+
SeleniumBase needs quotes to be properly escaped for Javascript calls.
108+
"""
109+
if are_quotes_escaped(string):
110+
if string.count("'") != string.count("\\'"):
111+
string = string.replace("'", "\\'")
112+
if string.count('"') != string.count('\\"'):
113+
string = string.replace('"', '\\"')
114+
return string
115+
116+
86117
def _jq_format(code):
87118
"""
88119
DEPRECATED - Use re.escape() instead, which performs the intended action.

seleniumbase/fixtures/xpath_to_css.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def _filter_xpath_grouping(xpath):
7070
index = xpath.rfind(')')
7171
if index == -1:
7272
raise XpathException("Invalid or unsupported Xpath: %s" % xpath)
73-
xpath = xpath[:index] + xpath[index+1:]
73+
xpath = xpath[:index] + xpath[index + 1:]
7474
return xpath
7575

7676

seleniumbase/plugins/pytest_plugin.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ def pytest_addoption(parser):
2323
parser.addoption('--env', action='store',
2424
dest='environment',
2525
choices=(
26-
constants.Environment.QA,
27-
constants.Environment.STAGING,
28-
constants.Environment.PRODUCTION,
29-
constants.Environment.MASTER,
30-
constants.Environment.LOCAL,
31-
constants.Environment.TEST),
26+
constants.Environment.QA,
27+
constants.Environment.STAGING,
28+
constants.Environment.PRODUCTION,
29+
constants.Environment.MASTER,
30+
constants.Environment.LOCAL,
31+
constants.Environment.TEST
32+
),
3233
default=constants.Environment.TEST,
3334
help="The environment to run the tests in.")
3435
parser.addoption('--data', dest='data',
@@ -48,7 +49,8 @@ def pytest_addoption(parser):
4849
parser.addoption('--database_env', action='store',
4950
dest='database_env',
5051
choices=(
51-
'prod', 'qa', 'staging', 'test', 'local', 'master'),
52+
'prod', 'qa', 'staging', 'test', 'local', 'master'
53+
),
5254
default='test',
5355
help=optparse.SUPPRESS_HELP)
5456
parser.addoption('--with-s3_logging', action="store_true",

0 commit comments

Comments
 (0)