50
50
)
51
51
52
52
import click
53
- from parso import (
54
- parse ,
55
- ParserSyntaxError ,
53
+ from libcst import (
54
+ ClassDef ,
55
+ FunctionDef ,
56
+ ImportFrom ,
57
+ Module ,
58
+ Name ,
59
+ parse_module ,
60
+ SimpleStatementLine ,
61
+ Yield ,
56
62
)
57
63
from rich .text import Text
58
64
from setproctitle import setproctitle
@@ -254,7 +260,7 @@ def create_mutants_for_file(filename, output_path):
254
260
# validate no syntax errors of mutants
255
261
with open (output_path ) as f :
256
262
try :
257
- ast .parse (f .read ())
263
+ ast .parse_module (f .read ())
258
264
except (IndentationError , SyntaxError ) as e :
259
265
print (output_path , 'has invalid syntax: ' , e )
260
266
exit (1 )
@@ -286,14 +292,14 @@ def write_all_mutants_to_file(*, out, source, filename):
286
292
hash_by_function_name = {}
287
293
mutant_names = []
288
294
289
- try :
290
- ast = parse (ensure_ends_with_newline (source ), error_recovery = False )
291
- except ParserSyntaxError :
292
- print (f'Warning: unsupported syntax in { filename } , skipping' )
293
- out .write (source )
294
- return [], {}
295
+ # try:
296
+ module = parse_module (ensure_ends_with_newline (source ))
297
+ # except ParserSyntaxError:
298
+ # print(f'Warning: unsupported syntax in {filename}, skipping')
299
+ # out.write(source)
300
+ # return [], {}
295
301
296
- for type_ , x , name_and_hash , mutant_name in yield_mutants_for_module (ast , no_mutate_lines ):
302
+ for type_ , x , name_and_hash , mutant_name in yield_mutants_for_module (node = module , no_mutate_lines = no_mutate_lines ):
297
303
out .write (x )
298
304
if mutant_name :
299
305
mutant_names .append (mutant_name )
@@ -368,17 +374,20 @@ def filter_funcdef_children(children):
368
374
369
375
def yield_mutants_for_node (* , func_node , class_name = None , context , node ):
370
376
# do not mutate static typing annotations
371
- if node . type == 'tfpdef' :
377
+ if type ( node ). __name__ == 'tfpdef' :
372
378
return
373
379
380
+
381
+ print (type (node ).__name__ )
382
+
374
383
# Some functions should not be mutated
375
- if node . type == 'atom_expr' and node .children [0 ].type == 'name' and node .children [0 ].value in NEVER_MUTATE_FUNCTION_CALLS :
384
+ if type ( node ). __name__ == 'atom_expr' and node .children [0 ].type == 'name' and node .children [0 ].value in NEVER_MUTATE_FUNCTION_CALLS :
376
385
return
377
386
378
387
# The rest
379
388
if hasattr (node , 'children' ):
380
389
children = node .children
381
- if node . type == 'funcdef' :
390
+ if isinstance ( node , FunctionDef ) :
382
391
children = filter_funcdef_children (children )
383
392
for child_node in children :
384
393
context .stack .append (child_node )
@@ -387,7 +396,7 @@ def yield_mutants_for_node(*, func_node, class_name=None, context, node):
387
396
finally :
388
397
context .stack .pop ()
389
398
390
- mutation = mutmut .mutation_by_ast_type .get (node . type )
399
+ mutation = mutmut .mutation_by_ast_type .get (type ( node ). __name__ )
391
400
if not mutation :
392
401
return
393
402
@@ -416,7 +425,7 @@ def yield_mutants_for_node(*, func_node, class_name=None, context, node):
416
425
417
426
# noinspection PyArgumentList
418
427
with rename_function_node (func_node , suffix = f'{ context .count } ' , class_name = class_name ):
419
- code = func_node . get_code ( )
428
+ code = func_module . code_for_node ( node )
420
429
if valid_syntax (code ):
421
430
context .count += 1
422
431
@@ -431,7 +440,7 @@ def yield_mutants_for_node(*, func_node, class_name=None, context, node):
431
440
432
441
def valid_syntax (code ):
433
442
try :
434
- ast .parse (dedent (code ))
443
+ ast .parse_module (dedent (code ))
435
444
return True
436
445
except (SyntaxError , IndentationError ):
437
446
return False
@@ -452,25 +461,25 @@ def exclude_node(self, node):
452
461
453
462
def is_inside_annassign (self ):
454
463
for node in self .stack :
455
- if node . type == 'annassign' :
464
+ if type ( node ). __name__ == 'annassign' :
456
465
return True
457
466
return False
458
467
459
468
def is_inside_dict_synonym_call (self ):
460
469
for node in self .stack :
461
- if node . type == 'atom_expr' and node .children [0 ].type == 'name' and node .children [0 ].value in self .dict_synonyms :
470
+ if type ( node ). __name__ == 'atom_expr' and node .children [0 ].type == 'name' and node .children [0 ].value in self .dict_synonyms :
462
471
return True
463
472
return False
464
473
465
474
466
475
def is_generator (node ):
467
- assert node . type == 'funcdef'
476
+ assert isinstance ( node , FunctionDef )
468
477
469
478
def _is_generator (n ):
470
- if n is not node and n . type in ( 'funcdef' , 'classdef' ):
479
+ if n is not node and isinstance ( n , ( FunctionDef , ClassDef ) ):
471
480
return False
472
481
473
- if n . type == 'keyword' and n . value == 'yield' :
482
+ if isinstance ( n , Yield ) :
474
483
return True
475
484
476
485
for c in getattr (n , 'children' , []):
@@ -480,29 +489,29 @@ def _is_generator(n):
480
489
return _is_generator (node )
481
490
482
491
483
- def yield_mutants_for_function (node , * , class_name = None , no_mutate_lines ):
484
- assert node . type == 'funcdef'
492
+ def yield_mutants_for_function (* , module , node , class_name = None , no_mutate_lines ):
493
+ assert isinstance ( node , FunctionDef )
485
494
486
495
if node .name .value in NEVER_MUTATE_FUNCTION_NAMES :
487
- yield 'filler' , node . get_code ( ), None , None
496
+ yield 'filler' , module . code_for_node ( node ), None , None
488
497
return
489
498
490
- hash_of_orig = md5 (node . get_code ( ).encode ()).hexdigest ()
499
+ hash_of_orig = md5 (module . code_for_node ( node ).encode ()).hexdigest ()
491
500
492
501
orig_name = node .name .value
493
502
# noinspection PyArgumentList
494
503
with rename_function_node (node , suffix = 'orig' , class_name = class_name ):
495
- yield 'orig' , node . get_code ( ), (orig_name , hash_of_orig ), None
504
+ yield 'orig' , module . code_for_node ( node ), (orig_name , hash_of_orig ), None
496
505
497
506
context = FuncContext (no_mutate_lines = no_mutate_lines )
498
507
499
508
return_annotation_started = False
500
509
501
510
for child_node in node .children :
502
- if child_node . type == 'operator' and child_node .value == '->' :
511
+ if type ( child_node ). __name__ == 'operator' and child_node .value == '->' :
503
512
return_annotation_started = True
504
513
505
- if return_annotation_started and child_node . type == 'operator' and child_node .value == ':' :
514
+ if return_annotation_started and type ( child_node ). __name__ == 'operator' and child_node .value == ':' :
506
515
return_annotation_started = False
507
516
508
517
if return_annotation_started :
@@ -521,60 +530,61 @@ def yield_mutants_for_function(node, *, class_name=None, no_mutate_lines):
521
530
yield 'filler' , '\n \n ' , None , None
522
531
523
532
524
- def yield_mutants_for_class (node , no_mutate_lines ):
525
- assert node . type == 'classdef'
533
+ def yield_mutants_for_class (* , module , node , no_mutate_lines ):
534
+ assert isinstance ( node , ClassDef )
526
535
for child_node in node .children :
527
- if child_node . type == 'suite' :
528
- yield from yield_mutants_for_class_body (child_node , no_mutate_lines = no_mutate_lines )
536
+ if type ( child_node ). __name__ == 'suite' :
537
+ yield from yield_mutants_for_class_body (module = module , node = child_node , no_mutate_lines = no_mutate_lines )
529
538
else :
530
- yield 'filler' , child_node . get_code ( ), None , None
539
+ yield 'filler' , module . code_for_node ( child_node ), None , None
531
540
532
541
533
- def yield_mutants_for_class_body (node , no_mutate_lines ):
534
- assert node . type == 'suite'
542
+ def yield_mutants_for_class_body (* , module , node , no_mutate_lines ):
543
+ assert type ( node ). __name__ == 'suite'
535
544
class_name = node .parent .name .value
536
545
537
546
for child_node in node .children :
538
- if child_node . type == 'funcdef' :
539
- yield from yield_mutants_for_function (child_node , class_name = class_name , no_mutate_lines = no_mutate_lines )
547
+ if isinstance ( node , FunctionDef ) :
548
+ yield from yield_mutants_for_function (module = module , node = child_node , class_name = class_name , no_mutate_lines = no_mutate_lines )
540
549
else :
541
- yield 'filler' , child_node . get_code ( ), None , None
550
+ yield 'filler' , module . code_for_node ( child_node ), None , None
542
551
543
552
544
553
def is_from_future_import_node (c ):
545
- if c . type == 'simple_stmt' :
554
+ if isinstance ( c , SimpleStatementLine ) :
546
555
if c .children :
547
556
c2 = c .children [0 ]
548
- if c2 . type == 'import_from' and c2 .children [1 ]. type == 'name' and c2 .children [1 ].value == '__future__' :
557
+ if isinstance ( c2 , ImportFrom ) and isinstance ( c2 .children [1 ], Name ) and c2 .children [1 ].value == '__future__' :
549
558
return True
550
559
return False
551
560
552
561
553
- def yield_future_imports (node ):
562
+ def yield_future_imports (* , module , node ):
554
563
for c in node .children :
555
564
if is_from_future_import_node (c ):
556
- yield 'filler' , c . get_code ( ), None , None
565
+ yield 'filler' , module . code_for_node ( c ), None , None
557
566
558
567
559
- def yield_mutants_for_module (node , no_mutate_lines ):
560
- assert node .type == 'file_input'
568
+ def yield_mutants_for_module (* , node , no_mutate_lines ):
569
+ assert isinstance (node , Module )
570
+ module = node
561
571
562
572
# First yield `from __future__`, then the rest
563
- yield from yield_future_imports (node )
564
-
573
+ yield from yield_future_imports (module = module , node = node )
565
574
yield 'trampoline_impl' , trampoline_impl , None , None
566
575
yield 'trampoline_impl' , yield_from_trampoline_impl , None , None
567
576
yield 'filler' , '\n ' , None , None
577
+ assert type (node ).__name__ == 'Module'
568
578
for child_node in node .children :
569
- if child_node . type == 'funcdef' :
570
- yield from yield_mutants_for_function (child_node , no_mutate_lines = no_mutate_lines )
571
- elif child_node . type == 'classdef' :
572
- yield from yield_mutants_for_class (child_node , no_mutate_lines = no_mutate_lines )
579
+ if isinstance ( child_node , FunctionDef ) :
580
+ yield from yield_mutants_for_function (module = module , node = child_node , no_mutate_lines = no_mutate_lines )
581
+ elif isinstance ( child_node , ClassDef ) :
582
+ yield from yield_mutants_for_class (module = module , node = child_node , no_mutate_lines = no_mutate_lines )
573
583
elif is_from_future_import_node (child_node ):
574
584
# Don't yield `from __future__` after trampoline
575
585
pass
576
586
else :
577
- yield 'filler' , child_node . get_code ( ), None , None
587
+ yield 'filler' , module . code_for_node ( child_node ), None , None
578
588
579
589
580
590
class SourceFileMutationData :
@@ -1390,25 +1400,25 @@ def results(all):
1390
1400
1391
1401
def read_mutants_ast (path ):
1392
1402
with open (Path ('mutants' ) / path ) as f :
1393
- return parse (f .read (), error_recovery = False )
1403
+ return parse_module (f .read ())
1394
1404
1395
1405
1396
1406
def read_orig_ast (path ):
1397
1407
with open (path ) as f :
1398
- return parse (f .read ())
1408
+ return parse_module (f .read ())
1399
1409
1400
1410
1401
1411
def find_ast_node (ast , function_name , orig_function_name ):
1402
1412
function_name = function_name .rpartition ('.' )[- 1 ]
1403
1413
orig_function_name = orig_function_name .rpartition ('.' )[- 1 ]
1404
1414
1405
1415
for node in ast .children :
1406
- if node . type == 'classdef' :
1416
+ if isinstance ( node , ClassDef ) :
1407
1417
(body ,) = [x for x in node .children if x .type == 'suite' ]
1408
1418
result = find_ast_node (body , function_name = function_name , orig_function_name = orig_function_name )
1409
1419
if result :
1410
1420
return result
1411
- if node . type == 'funcdef' and node .name .value == function_name :
1421
+ if isinstance ( node , FunctionDef ) and node .name .value == function_name :
1412
1422
node .name .value = orig_function_name
1413
1423
return node
1414
1424
@@ -1457,9 +1467,9 @@ def get_diff_for_mutant(mutant_name, source=None, path=None):
1457
1467
if source is None :
1458
1468
ast = read_mutants_ast (path )
1459
1469
else :
1460
- ast = parse (source , error_recovery = False )
1461
- orig_code = read_original_ast_node (ast , mutant_name ). get_code ( ).strip ()
1462
- mutant_code = read_mutant_ast_node (ast , mutant_name ). get_code ( ).strip ()
1470
+ ast = parse_module (source )
1471
+ orig_code = module . code_for_node ( read_original_ast_node (ast , mutant_name )).strip ()
1472
+ mutant_code = module . code_for_node ( read_mutant_ast_node (ast , mutant_name )).strip ()
1463
1473
1464
1474
path = str (path ) # difflib requires str, not Path
1465
1475
return '\n ' .join ([
@@ -1500,14 +1510,14 @@ def apply_mutant(mutant_name):
1500
1510
mutant_ast_node .name .value = orig_function_name
1501
1511
1502
1512
for node in orig_ast .children :
1503
- if node . type == 'funcdef' and node .name .value == orig_function_name :
1513
+ if isinstance ( node , FunctionDef ) and node .name .value == orig_function_name :
1504
1514
node .children = mutant_ast_node .children
1505
1515
break
1506
1516
else :
1507
1517
raise FileNotFoundError (f'Could not apply mutant { mutant_name } ' )
1508
1518
1509
1519
with open (path , 'w' ) as f :
1510
- f .write (orig_ast .get_code ( ))
1520
+ f .write (orig_ast .code_for_node ( orig_ast ))
1511
1521
1512
1522
1513
1523
# TODO: junitxml, html commands
0 commit comments