2
2
Translates a SeleniumBase Python file into a different language
3
3
4
4
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]
7
7
Languages:
8
8
--en / --English | --zh / --Chinese
9
9
--nl / --Dutch | --fr / --French
28
28
29
29
import codecs
30
30
import colorama
31
+ import os
31
32
import re
32
33
import sys
33
34
from seleniumbase .translate import master_dict
39
40
def invalid_run_command (msg = None ):
40
41
exp = (" ** translate **\n \n " )
41
42
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 "
44
45
exp += " Languages:\n "
45
46
exp += " --en / --English | --zh / --Chinese\n "
46
47
exp += " --nl / --Dutch | --fr / --French\n "
@@ -67,6 +68,41 @@ def invalid_run_command(msg=None):
67
68
raise Exception ('INVALID RUN COMMAND!\n %s\n \n %s' % (msg , exp ))
68
69
69
70
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
+
70
106
def process_test_file (code_lines , new_lang ):
71
107
detected_lang = None
72
108
changed = False
@@ -211,21 +247,32 @@ def main():
211
247
c6 = colorama .Fore .RED + colorama .Back .LIGHTCYAN_EX
212
248
c7 = colorama .Fore .BLACK + colorama .Back .MAGENTA
213
249
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
-
223
250
new_lang = None
224
251
overwrite = False
225
252
copy = False
226
253
print_only = False
227
254
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 :
229
276
options = command_args [1 :]
230
277
for option in options :
231
278
option = option .lower ()
@@ -258,12 +305,13 @@ def main():
258
305
elif option == "--es" or option == "--spanish" :
259
306
new_lang = "Spanish"
260
307
else :
261
- invalid_cmd = "\n ===> INVALID OPTION: >> %s <<" % option
308
+ invalid_cmd = "\n ===> INVALID OPTION: >> %s <<\n " % option
262
309
invalid_cmd = invalid_cmd .replace ('>> ' , ">>" + c5 + " " )
263
310
invalid_cmd = invalid_cmd .replace (' <<' , " " + cr + "<<" )
264
311
invalid_cmd = invalid_cmd .replace ('>>' , c7 + ">>" + cr )
265
312
invalid_cmd = invalid_cmd .replace ('<<' , c7 + "<<" + cr )
266
- invalid_run_command (invalid_cmd )
313
+ help_me = True
314
+ break
267
315
else :
268
316
help_me = True
269
317
@@ -286,14 +334,14 @@ def main():
286
334
example_run = (
287
335
"\n > *** Examples: *** <\n "
288
336
"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 "
290
338
"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 "
292
340
"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 " )
294
342
usage = (
295
343
"\n > *** Usage: *** <\n "
296
- " >$ seleniumbase translate [SB_FILE.py] [LANGUAGE] [ACTION]\n " )
344
+ " >$ sbase translate [SB_FILE.py] [LANGUAGE] [ACTION]\n " )
297
345
specify_lang = specify_lang .replace ('>*' , c5 + ">*" )
298
346
specify_lang = specify_lang .replace ('*<' , "*<" + cr )
299
347
specify_lang = specify_lang .replace (
@@ -352,6 +400,7 @@ def main():
352
400
example_run = example_run .replace (" --zh" , " " + c2 + "--zh" + cr )
353
401
example_run = example_run .replace (" --pt" , " " + c2 + "--pt" + cr )
354
402
example_run = example_run .replace (" --nl" , " " + c2 + "--nl" + cr )
403
+ example_run = example_run .replace ("sbase" , c4 + "sbase" + cr )
355
404
usage = usage .replace ("Usage:" , c4 + "Usage:" + cr )
356
405
usage = usage .replace ("> *** " , c3 + "> *** " + cr )
357
406
usage = usage .replace (" *** <" , c3 + " *** <" + cr )
@@ -360,31 +409,40 @@ def main():
360
409
usage = usage .replace ("ACTION" , c1 + "ACTION" + cr )
361
410
362
411
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 ("" )
364
417
raise Exception (message )
365
418
if not overwrite and not copy and not print_only :
366
419
message = specify_action + example_run + usage
367
420
if not new_lang :
368
421
message = specify_lang + specify_action + example_run + usage
422
+ print ("" )
369
423
raise Exception (message )
370
424
if not new_lang :
425
+ print ("" )
371
426
raise Exception (specify_lang + example_run + usage )
372
427
if overwrite and copy :
373
428
part_1 = (
374
429
'\n * You can choose either {-o / --overwrite} '
375
- 'OR {-c / --copy}, but NOT BOTH!\n ' )
430
+ 'OR {-c / --copy}, BUT * NOT BOTH * !\n ' )
376
431
part_1 = part_1 .replace ("-o " , c1 + "-o" + cr + " " )
377
432
part_1 = part_1 .replace ("--overwrite" , c1 + "--overwrite" + cr )
378
433
part_1 = part_1 .replace ("-c " , c1 + "-c" + cr + " " )
379
434
part_1 = part_1 .replace ("--copy" , c1 + "--copy" + cr )
435
+ part_1 = part_1 .replace ("* NOT BOTH *" , c6 + "* NOT BOTH *" + cr )
380
436
message = part_1 + example_run + usage
437
+ print ("" )
381
438
raise Exception (message )
382
439
383
440
with open (seleniumbase_file , 'r' , encoding = 'utf-8' ) as f :
384
441
all_code = f .read ()
385
442
if "def test_" not in all_code and "from seleniumbase" not in all_code :
443
+ print ("" )
386
444
raise Exception ("\n \n `%s` is not a valid SeleniumBase test file!\n "
387
- "\n Expecting: %s \n "
445
+ "\n Expecting: [%s] \n "
388
446
% (seleniumbase_file , expected_arg ))
389
447
code_lines = all_code .split ('\n ' )
390
448
@@ -407,13 +465,160 @@ def main():
407
465
save_line = save_line .replace ("]]]" , cr + "" )
408
466
409
467
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 ()
410
599
print ("" )
411
600
print (save_line )
412
601
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
+ # ----------------------------------------
417
622
418
623
new_file_name = None
419
624
if copy :
0 commit comments