Skip to content

Commit 4883971

Browse files
committed
Update the translator
1 parent 9bcaa0c commit 4883971

File tree

1 file changed

+232
-27
lines changed

1 file changed

+232
-27
lines changed

seleniumbase/translate/translator.py

Lines changed: 232 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
Translates a SeleniumBase Python file into a different language
33
44
Usage:
5-
seleniumbase translate [SB_FILE].py [LANGUAGE] [ACTION]
6-
OR: sbase translate [SB_FILE].py [LANGUAGE] [ACTION]
5+
seleniumbase translate [SB_FILE.py] [LANGUAGE] [ACTION]
6+
OR: sbase translate [SB_FILE.py] [LANGUAGE] [ACTION]
77
Languages:
88
--en / --English | --zh / --Chinese
99
--nl / --Dutch | --fr / --French
@@ -28,6 +28,7 @@
2828

2929
import codecs
3030
import colorama
31+
import os
3132
import re
3233
import sys
3334
from seleniumbase.translate import master_dict
@@ -39,8 +40,8 @@
3940
def invalid_run_command(msg=None):
4041
exp = (" ** translate **\n\n")
4142
exp += " Usage:\n"
42-
exp += " seleniumbase translate [SB_FILE].py [LANGUAGE] [ACTION]\n"
43-
exp += " OR: sbase translate [SB_FILE].py [LANGUAGE] [ACTION]\n"
43+
exp += " seleniumbase translate [SB_FILE.py] [LANGUAGE] [ACTION]\n"
44+
exp += " OR: sbase translate [SB_FILE.py] [LANGUAGE] [ACTION]\n"
4445
exp += " Languages:\n"
4546
exp += " --en / --English | --zh / --Chinese\n"
4647
exp += " --nl / --Dutch | --fr / --French\n"
@@ -67,6 +68,41 @@ def invalid_run_command(msg=None):
6768
raise Exception('INVALID RUN COMMAND!\n%s\n\n%s' % (msg, exp))
6869

6970

71+
def ranges():
72+
# Get the ranges of special characters of Chinese, Japanese, and Korean
73+
special_char_ranges = ([
74+
{"from": ord(u"\u3300"), "to": ord(u"\u33ff")},
75+
{"from": ord(u"\ufe30"), "to": ord(u"\ufe4f")},
76+
{"from": ord(u"\uf900"), "to": ord(u"\ufaff")},
77+
{"from": ord(u"\U0002F800"), "to": ord(u"\U0002fa1f")},
78+
{'from': ord(u'\u3040'), 'to': ord(u'\u309f')},
79+
{"from": ord(u"\u30a0"), "to": ord(u"\u30ff")},
80+
{"from": ord(u"\u2e80"), "to": ord(u"\u2eff")},
81+
{"from": ord(u"\u4e00"), "to": ord(u"\u9fff")},
82+
{"from": ord(u"\u3400"), "to": ord(u"\u4dbf")},
83+
{"from": ord(u"\U00020000"), "to": ord(u"\U0002a6df")},
84+
{"from": ord(u"\U0002a700"), "to": ord(u"\U0002b73f")},
85+
{"from": ord(u"\U0002b740"), "to": ord(u"\U0002b81f")},
86+
{"from": ord(u"\U0002b820"), "to": ord(u"\U0002ceaf")}
87+
])
88+
return special_char_ranges
89+
90+
91+
def is_cjk(char):
92+
# Returns True if the special character is Chinese, Japanese, or Korean
93+
sc = any([range["from"] <= ord(char) <= range["to"] for range in ranges()])
94+
return sc
95+
96+
97+
def get_width(line):
98+
# Chinese/Japanese/Korean characters take up double width visually
99+
line_length = len(line)
100+
for char in line:
101+
if is_cjk(char):
102+
line_length += 1
103+
return line_length
104+
105+
70106
def process_test_file(code_lines, new_lang):
71107
detected_lang = None
72108
changed = False
@@ -211,21 +247,32 @@ def main():
211247
c6 = colorama.Fore.RED + colorama.Back.LIGHTCYAN_EX
212248
c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA
213249
cr = colorama.Style.RESET_ALL
214-
expected_arg = ("[A SeleniumBase Python file]")
215-
command_args = sys.argv[2:]
216-
217-
seleniumbase_file = command_args[0]
218-
if not seleniumbase_file.endswith('.py'):
219-
raise Exception("\n\n`%s` is not a Python file!\n\n"
220-
"Expecting: %s\n"
221-
% (seleniumbase_file, expected_arg))
222-
223250
new_lang = None
224251
overwrite = False
225252
copy = False
226253
print_only = False
227254
help_me = False
228-
if len(command_args) >= 2:
255+
invalid_cmd = None
256+
257+
expected_arg = ("A SeleniumBase Python file")
258+
command_args = sys.argv[2:]
259+
seleniumbase_file = command_args[0]
260+
if not seleniumbase_file.endswith('.py'):
261+
seleniumbase_file = (
262+
c7 + ">>" + c5 + " " + seleniumbase_file + " " + c7 + "<<" + cr)
263+
bad_file_error = ("\n`%s` is not a Python file!\n\n"
264+
"Expecting: [%s]"
265+
% (seleniumbase_file, expected_arg))
266+
bad_file_error = bad_file_error.replace(
267+
"is not a Python file!", c3 + "is not a Python file!" + cr)
268+
bad_file_error = bad_file_error.replace(
269+
expected_arg, c4 + expected_arg + cr)
270+
bad_file_error = bad_file_error.replace(
271+
"Expecting:", c3 + "Expecting:" + cr)
272+
print(bad_file_error)
273+
help_me = True
274+
275+
if len(command_args) >= 2 and not help_me:
229276
options = command_args[1:]
230277
for option in options:
231278
option = option.lower()
@@ -258,12 +305,13 @@ def main():
258305
elif option == "--es" or option == "--spanish":
259306
new_lang = "Spanish"
260307
else:
261-
invalid_cmd = "\n===> INVALID OPTION: >> %s <<" % option
308+
invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option
262309
invalid_cmd = invalid_cmd.replace('>> ', ">>" + c5 + " ")
263310
invalid_cmd = invalid_cmd.replace(' <<', " " + cr + "<<")
264311
invalid_cmd = invalid_cmd.replace('>>', c7 + ">>" + cr)
265312
invalid_cmd = invalid_cmd.replace('<<', c7 + "<<" + cr)
266-
invalid_run_command(invalid_cmd)
313+
help_me = True
314+
break
267315
else:
268316
help_me = True
269317

@@ -286,14 +334,14 @@ def main():
286334
example_run = (
287335
"\n> *** Examples: *** <\n"
288336
"Translate test_1.py into Chinese and only print the output:\n"
289-
" >$ seleniumbase translate test_1.py --zh -p\n"
337+
" >$ sbase translate test_1.py --zh -p\n"
290338
"Translate test_2.py into Portuguese and overwrite the file:\n"
291-
" >$ seleniumbase translate test_2.py --pt -o\n"
339+
" >$ sbase translate test_2.py --pt -o\n"
292340
"Translate test_3.py into Dutch and make a copy of the file:\n"
293-
" >$ seleniumbase translate test_3.py --nl -c\n")
341+
" >$ sbase translate test_3.py --nl -c\n")
294342
usage = (
295343
"\n> *** Usage: *** <\n"
296-
" >$ seleniumbase translate [SB_FILE.py] [LANGUAGE] [ACTION]\n")
344+
" >$ sbase translate [SB_FILE.py] [LANGUAGE] [ACTION]\n")
297345
specify_lang = specify_lang.replace('>*', c5 + ">*")
298346
specify_lang = specify_lang.replace('*<', "*<" + cr)
299347
specify_lang = specify_lang.replace(
@@ -352,6 +400,7 @@ def main():
352400
example_run = example_run.replace(" --zh", " " + c2 + "--zh" + cr)
353401
example_run = example_run.replace(" --pt", " " + c2 + "--pt" + cr)
354402
example_run = example_run.replace(" --nl", " " + c2 + "--nl" + cr)
403+
example_run = example_run.replace("sbase", c4 + "sbase" + cr)
355404
usage = usage.replace("Usage:", c4 + "Usage:" + cr)
356405
usage = usage.replace("> *** ", c3 + "> *** " + cr)
357406
usage = usage.replace(" *** <", c3 + " *** <" + cr)
@@ -360,31 +409,40 @@ def main():
360409
usage = usage.replace("ACTION", c1 + "ACTION" + cr)
361410

362411
if help_me:
363-
message = specify_lang + specify_action + example_run + usage
412+
message = ""
413+
if invalid_cmd:
414+
message += invalid_cmd
415+
message += (specify_lang + specify_action + example_run + usage)
416+
print("")
364417
raise Exception(message)
365418
if not overwrite and not copy and not print_only:
366419
message = specify_action + example_run + usage
367420
if not new_lang:
368421
message = specify_lang + specify_action + example_run + usage
422+
print("")
369423
raise Exception(message)
370424
if not new_lang:
425+
print("")
371426
raise Exception(specify_lang + example_run + usage)
372427
if overwrite and copy:
373428
part_1 = (
374429
'\n* You can choose either {-o / --overwrite} '
375-
'OR {-c / --copy}, but NOT BOTH!\n')
430+
'OR {-c / --copy}, BUT * NOT BOTH *!\n')
376431
part_1 = part_1.replace("-o ", c1 + "-o" + cr + " ")
377432
part_1 = part_1.replace("--overwrite", c1 + "--overwrite" + cr)
378433
part_1 = part_1.replace("-c ", c1 + "-c" + cr + " ")
379434
part_1 = part_1.replace("--copy", c1 + "--copy" + cr)
435+
part_1 = part_1.replace("* NOT BOTH *", c6 + "* NOT BOTH *" + cr)
380436
message = part_1 + example_run + usage
437+
print("")
381438
raise Exception(message)
382439

383440
with open(seleniumbase_file, 'r', encoding='utf-8') as f:
384441
all_code = f.read()
385442
if "def test_" not in all_code and "from seleniumbase" not in all_code:
443+
print("")
386444
raise Exception("\n\n`%s` is not a valid SeleniumBase test file!\n"
387-
"\nExpecting: %s\n"
445+
"\nExpecting: [%s]\n"
388446
% (seleniumbase_file, expected_arg))
389447
code_lines = all_code.split('\n')
390448

@@ -407,13 +465,160 @@ def main():
407465
save_line = save_line.replace("]]]", cr + "")
408466

409467
if print_only:
468+
console_width = None # width of console output when running script
469+
used_width = None # code_width and few spaces on right for padding
470+
magic_console = None
471+
magic_syntax = None
472+
try:
473+
console_width = os.popen('stty size', 'r').read().split()[1]
474+
if console_width:
475+
console_width = int(console_width)
476+
except Exception:
477+
console_width = None
478+
479+
if sys.version_info[0] == 3 and sys.version_info[1] >= 6:
480+
from rich.console import Console
481+
from rich.syntax import Syntax
482+
python_code = "\n".join(seleniumbase_lines)
483+
code_width = 1
484+
485+
w = 4 # line number whitespace
486+
num_lines = len(seleniumbase_lines)
487+
if num_lines >= 10:
488+
w = 5
489+
if num_lines >= 100:
490+
w = 6
491+
if num_lines >= 1000:
492+
w = 7
493+
494+
new_sb_lines = []
495+
for line in seleniumbase_lines:
496+
line_length = len(line)
497+
line_length2 = len(line)
498+
line_length = get_width(line)
499+
if line_length > code_width:
500+
code_width = line_length
501+
502+
if console_width:
503+
# If line is larger than console_width, try to optimize it
504+
if line_length + w > console_width: # 5 is line number ws
505+
if line.count(' # ') == 1: # Has comments like this
506+
if get_width(
507+
line.split(
508+
' # ')[0]) + w <= console_width:
509+
new_sb_lines.append(line)
510+
continue
511+
elif line.count(' # ') == 1: # Has bad flake8 comment
512+
if get_width(
513+
line.split(
514+
' # ')[0]) + w <= console_width:
515+
new_sb_lines.append(line)
516+
continue
517+
if line.startswith("from") and " import " in line:
518+
line1 = line.split(" import ")[0] + " \\"
519+
line2 = " import " + line.split(" import ")[1]
520+
new_sb_lines.append(line1)
521+
new_sb_lines.append(line2)
522+
continue
523+
elif line.count('(') == 1 and line.count(')') == 1:
524+
whitespace = line_length2 - len(line.lstrip())
525+
new_ws = line[0:whitespace] + " "
526+
line1 = line.split('(')[0] + '('
527+
line2 = new_ws + line.split('(')[1]
528+
if not ('):') in line2:
529+
new_sb_lines.append(line1)
530+
if get_width(line2) + w > console_width:
531+
if line2.count('", "') == 1:
532+
line2a = line2.split('", "')[0] + '",'
533+
line2b = new_ws + '"' + (
534+
line2.split('", "')[1])
535+
new_sb_lines.append(line2a)
536+
new_sb_lines.append(line2b)
537+
continue
538+
elif line2.count("', '") == 1:
539+
line2a = line2.split("', '")[0] + "',"
540+
line2b = new_ws + "'" + (
541+
line2.split("', '")[1])
542+
new_sb_lines.append(line2a)
543+
new_sb_lines.append(line2b)
544+
continue
545+
elif line2.count("://") == 1 and (
546+
line2.count('")') == 1):
547+
line2a = line2.split("://")[0] + '://"'
548+
line2b = new_ws + '"' + (
549+
line2.split("://")[1])
550+
new_sb_lines.append(line2a)
551+
new_sb_lines.append(line2b)
552+
continue
553+
elif line2.count("://") == 1 and (
554+
line2.count("')") == 1):
555+
line2a = line2.split("://")[0] + "://'"
556+
line2b = new_ws + "'" + (
557+
line2.split("://")[1])
558+
new_sb_lines.append(line2a)
559+
new_sb_lines.append(line2b)
560+
continue
561+
elif line2.count('="') == 1 and (
562+
line2.lstrip().startswith("'")):
563+
line2a = line2.split('="')[0] + "='"
564+
line2b = new_ws + "'\"" + (
565+
line2.split('="')[1])
566+
new_sb_lines.append(line2a)
567+
new_sb_lines.append(line2b)
568+
continue
569+
elif line2.count("='") == 1 and (
570+
line2.lstrip().startswith('"')):
571+
line2a = line2.split("='")[0] + '="'
572+
line2b = new_ws + '"\'' + (
573+
line2.split("='")[1])
574+
new_sb_lines.append(line2a)
575+
new_sb_lines.append(line2b)
576+
continue
577+
new_sb_lines.append(line2)
578+
elif get_width(line2) + 4 + w <= console_width:
579+
line2 = " " + line2
580+
new_sb_lines.append(line1)
581+
new_sb_lines.append(line2)
582+
else:
583+
new_sb_lines.append(line)
584+
continue
585+
new_sb_lines.append(line)
586+
587+
if new_sb_lines:
588+
seleniumbase_lines = new_sb_lines
589+
python_code = "\n".join(seleniumbase_lines)
590+
591+
extra_r_spaces = 2
592+
if console_width and (code_width + extra_r_spaces < console_width):
593+
used_width = code_width + extra_r_spaces
594+
595+
magic_syntax = Syntax(
596+
python_code, "python", theme="monokai",
597+
line_numbers=True, code_width=used_width, word_wrap=False)
598+
magic_console = Console()
410599
print("")
411600
print(save_line)
412601
print(c1 + "* Here are the results: >>>" + cr)
413-
print("--------------------------------------------------------------")
414-
for line in seleniumbase_lines:
415-
print(line)
416-
print("--------------------------------------------------------------")
602+
# ----------------------------------------
603+
dash_length = 62 # May change
604+
if used_width and used_width + w < console_width:
605+
dash_length = used_width + w
606+
elif console_width:
607+
dash_length = console_width
608+
dashes = "-" * dash_length
609+
print(dashes)
610+
print_success = False
611+
if magic_syntax:
612+
try:
613+
magic_console.print(magic_syntax) # noqa
614+
print_success = True
615+
except Exception:
616+
pass
617+
if not magic_syntax or not print_success:
618+
for line in seleniumbase_lines:
619+
print(line)
620+
print(dashes)
621+
# ----------------------------------------
417622

418623
new_file_name = None
419624
if copy:

0 commit comments

Comments
 (0)