1
1
import ast
2
2
import os
3
3
import pathlib
4
+ import re
4
5
import subprocess
5
6
import sys
6
7
import tempfile
12
13
13
14
14
15
class Visitor (ast .NodeVisitor ):
15
- def __init__ (self , filepath , output ):
16
+ def __init__ (self , filepath , output , existing_allowed ):
16
17
self .filepath = filepath
17
18
self .context = list (filepath .with_suffix ("" ).relative_to (lib ).parts )
18
19
self .output = output
20
+ self .existing_allowed = existing_allowed
21
+
22
+ def _is_already_allowed (self , parts ):
23
+ # Skip outputting a path if it's already allowed before.
24
+ candidates = ['.' .join (parts [:s ]) for s in range (1 , len (parts ))]
25
+ for allow in self .existing_allowed :
26
+ if any (allow .fullmatch (path ) for path in candidates ):
27
+ return True
28
+ return False
19
29
20
30
def visit_FunctionDef (self , node ):
21
31
# delete_parameter adds a private sentinel value that leaks
@@ -43,7 +53,9 @@ def visit_FunctionDef(self, node):
43
53
):
44
54
parents .insert (0 , parent .name )
45
55
parent = parent .parent
46
- self .output .write (f"{ '.' .join (self .context + parents )} .{ node .name } \n " )
56
+ parts = [* self .context , * parents , node .name ]
57
+ if not self ._is_already_allowed (parts ):
58
+ self .output .write ("\\ ." .join (parts ) + "\n " )
47
59
break
48
60
49
61
def visit_ClassDef (self , node ):
@@ -62,20 +74,28 @@ def visit_ClassDef(self, node):
62
74
# for setters on items with only a getter
63
75
for substitutions in aliases .values ():
64
76
parts = self .context + parents + [node .name ]
65
- self .output .write (
66
- "\n " .join (
67
- f"{ '.' .join (parts )} .[gs]et_{ a } \n " for a in substitutions
68
- )
69
- )
77
+ for a in substitutions :
78
+ if not (self ._is_already_allowed ([* parts , f"get_{ a } " ]) and
79
+ self ._is_already_allowed ([* parts , f"set_{ a } " ])):
80
+ self .output .write ("\\ ." .join ([* parts , f"[gs]et_{ a } \n " ]))
70
81
for child in ast .iter_child_nodes (node ):
71
82
self .visit (child )
72
83
73
84
85
+ existing_allowed = []
86
+ with (root / 'ci/mypy-stubtest-allowlist.txt' ).open () as f :
87
+ for line in f :
88
+ line , _ , _ = line .partition ('#' )
89
+ line = line .strip ()
90
+ if line :
91
+ existing_allowed .append (re .compile (line ))
92
+
93
+
74
94
with tempfile .TemporaryDirectory () as d :
75
95
p = pathlib .Path (d ) / "allowlist.txt"
76
96
with p .open ("wt" ) as f :
77
97
for path in mpl .glob ("**/*.py" ):
78
- v = Visitor (path , f )
98
+ v = Visitor (path , f , existing_allowed )
79
99
tree = ast .parse (path .read_text ())
80
100
81
101
# Assign parents to tree so they can be backtraced
0 commit comments