Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1decae6

Browse files
committed
Add script for identifying missing/broken type hints
Could use some front end work to make it easier to run and supply options/configurationDoes not do set checks for class methods, as there are far too many false positives
1 parente4cedb1 commit1decae6

File tree

1 file changed

+257
-0
lines changed

1 file changed

+257
-0
lines changed

‎tools/check_typehints.py

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
#!/usr/bin/env python
2+
3+
importast
4+
importpathlib
5+
importsys
6+
7+
MISSING_STUB=1
8+
MISSING_IMPL=2
9+
POS_ARGS=4
10+
ARGS=8
11+
VARARG=16
12+
KWARGS=32
13+
VARKWARG=64
14+
15+
16+
defcheck_file(path,ignore=0):
17+
stubpath=path.with_suffix(".pyi")
18+
ret=0
19+
ifnotstubpath.exists():
20+
return0,0
21+
tree=ast.parse(path.read_text())
22+
stubtree=ast.parse(stubpath.read_text())
23+
returncheck_namespace(tree,stubtree,path,ignore)
24+
25+
26+
defcheck_namespace(tree,stubtree,path,ignore=0):
27+
ret=0
28+
count=0
29+
tree_items=set(
30+
i.nameforiintree.bodyifhasattr(i,"name")andnoti.name.startswith("_")
31+
)
32+
stubtree_items=set(
33+
i.name
34+
foriinstubtree.body
35+
ifhasattr(i,"name")andnoti.name.startswith("_")
36+
)
37+
38+
foritemintree.body:
39+
ifisinstance(item,ast.Assign):
40+
tree_items|=set(
41+
i.id
42+
foriinitem.targets
43+
ifhasattr(i,"id")andnoti.id.startswith("_")
44+
)
45+
fortargetinitem.targets:
46+
ifisinstance(target,ast.Tuple):
47+
tree_items|=set(i.idforiintarget.elts)
48+
elifisinstance(item,ast.AnnAssign):
49+
tree_items|= {item.target.id}
50+
foriteminstubtree.body:
51+
ifisinstance(item,ast.Assign):
52+
stubtree_items|=set(
53+
i.id
54+
foriinitem.targets
55+
ifhasattr(i,"id")andnoti.id.startswith("_")
56+
)
57+
fortargetinitem.targets:
58+
ifisinstance(target,ast.Tuple):
59+
stubtree_items|=set(i.idforiintarget.elts)
60+
elifisinstance(item,ast.AnnAssign):
61+
stubtree_items|= {item.target.id}
62+
63+
try:
64+
all_=ast.literal_eval(ast.unparse(get_subtree(tree,"__all__").value))
65+
exceptValueError:
66+
all_= []
67+
68+
ifall_:
69+
missing= (tree_items-stubtree_items)&set(all_)
70+
else:
71+
missing=tree_items-stubtree_items
72+
73+
deprecated=set()
74+
foritem_nameinmissing:
75+
item=get_subtree(tree,item_name)
76+
ifhasattr(item,"decorator_list"):
77+
if"deprecated"in [
78+
i.func.attr
79+
foriinitem.decorator_list
80+
ifhasattr(i,"func")andhasattr(i.func,"attr")
81+
]:
82+
deprecated|= {item_name}
83+
84+
ifmissing-deprecatedand~ignore&MISSING_STUB:
85+
print(f"{path}:{missing-deprecated} missing from stubs")
86+
ret|=MISSING_STUB
87+
count+=1
88+
89+
non_class_or_func=set()
90+
foritem_nameinstubtree_items-tree_items:
91+
try:
92+
get_subtree(tree,item_name)
93+
exceptValueError:
94+
pass
95+
else:
96+
non_class_or_func|= {item_name}
97+
98+
missing_implementation=stubtree_items-tree_items-non_class_or_func
99+
ifmissing_implementationand~ignore&MISSING_IMPL:
100+
print(
101+
f"{path}:{missing_implementation} in stubs and not source"
102+
)
103+
ret|=MISSING_IMPL
104+
count+=1
105+
106+
foritem_nameintree_items&stubtree_items:
107+
item=get_subtree(tree,item_name)
108+
stubitem=get_subtree(stubtree,item_name)
109+
ifisinstance(item,ast.FunctionDef)andisinstance(stubitem,ast.FunctionDef):
110+
err,c=check_function(item,stubitem,f"{path}::{item_name}",ignore)
111+
ret|=err
112+
count+=c
113+
ifisinstance(item,ast.ClassDef):
114+
# Ignore set differences for classes... while it would be nice to have
115+
# inheritance and attributes set in init/methods make both presence and
116+
# absence of nodes spurious
117+
err,c=check_namespace(
118+
item,
119+
stubitem,
120+
f"{path}::{item_name}",
121+
ignore|MISSING_STUB|MISSING_IMPL,
122+
)
123+
ret|=err
124+
count+=c
125+
126+
returnret,count
127+
128+
129+
defcheck_function(item,stubitem,path,ignore):
130+
ret=0
131+
count=0
132+
133+
# if the stub calls overload, assume it knows what its doing
134+
overloaded="overload"in [
135+
i.idforiinstubitem.decorator_listifhasattr(i,"id")
136+
]
137+
ifoverloaded:
138+
return0,0
139+
140+
item_posargs= [a.argforainitem.args.posonlyargs]
141+
stubitem_posargs= [a.argforainstubitem.args.posonlyargs]
142+
ifitem_posargs!=stubitem_posargsand~ignore&POS_ARGS:
143+
print(
144+
f"{path}{item.name} posargs differ:{item_posargs} vs{stubitem_posargs}"
145+
)
146+
ret|=POS_ARGS
147+
count+=1
148+
149+
item_args= [a.argforainitem.args.args]
150+
stubitem_args= [a.argforainstubitem.args.args]
151+
ifitem_args!=stubitem_argsand~ignore&ARGS:
152+
print(f"{path} args differ for{item.name}:{item_args} vs{stubitem_args}")
153+
ret|=ARGS
154+
count+=1
155+
156+
item_vararg=item.args.vararg
157+
stubitem_vararg=stubitem.args.vararg
158+
if~ignore&VARARG:
159+
if (item_varargisNone)^ (stubitem_varargisNone):
160+
ifitem_vararg:
161+
print(
162+
f"{path}{item.name} vararg differ: "
163+
f"{item_vararg.arg} vs{stubitem_vararg}"
164+
)
165+
else:
166+
print(
167+
f"{path}{item.name} vararg differ: "
168+
f"{item_vararg} vs{stubitem_vararg.arg}"
169+
)
170+
ret|=VARARG
171+
count+=1
172+
elifitem_varargisNone:
173+
pass
174+
elifitem_vararg.arg!=stubitem_vararg.arg:
175+
print(
176+
f"{path}{item.name} vararg differ: "
177+
f"{item_vararg.arg} vs{stubitem_vararg.arg}"
178+
)
179+
ret|=VARARG
180+
count+=1
181+
182+
item_kwonlyargs= [a.argforainitem.args.kwonlyargs]
183+
stubitem_kwonlyargs= [a.argforainstubitem.args.kwonlyargs]
184+
ifitem_kwonlyargs!=stubitem_kwonlyargsand~ignore&KWARGS:
185+
print(
186+
f"{path}{item.name} kwonlyargs differ: "
187+
f"{item_kwonlyargs} vs{stubitem_kwonlyargs}"
188+
)
189+
ret|=KWARGS
190+
count+=1
191+
192+
item_kwarg=item.args.kwarg
193+
stubitem_kwarg=stubitem.args.kwarg
194+
if~ignore&VARKWARG:
195+
if (item_kwargisNone)^ (stubitem_kwargisNone):
196+
ifitem_kwarg:
197+
print(
198+
f"{path}{item.name} kwarg differ: "
199+
f"{item_kwarg.arg} vs{stubitem_kwarg}"
200+
)
201+
else:
202+
print(
203+
f"{path}{item.name} kwarg differ: "
204+
f"{item_kwarg} vs{stubitem_kwarg.arg}"
205+
)
206+
ret|=VARKWARG
207+
count+=1
208+
elifitem_kwargisNone:
209+
pass
210+
elifitem_kwarg.arg!=stubitem_kwarg.arg:
211+
print(
212+
f"{path}{item.name} kwarg differ: "
213+
f"{item_kwarg.arg} vs{stubitem_kwarg.arg}"
214+
)
215+
ret|=VARKWARG
216+
count+=1
217+
218+
returnret,count
219+
220+
221+
defget_subtree(tree,name):
222+
foritemintree.body:
223+
ifisinstance(item,ast.Assign):
224+
ifnamein [i.idforiinitem.targetsifhasattr(i,"id")]:
225+
returnitem
226+
fortargetinitem.targets:
227+
ifisinstance(target,ast.Tuple):
228+
ifnamein [i.idforiintarget.elts]:
229+
returnitem
230+
ifisinstance(item,ast.AnnAssign):
231+
ifname==item.target.id:
232+
returnitem
233+
ifnothasattr(item,"name"):
234+
continue
235+
ifitem.name==name:
236+
returnitem
237+
raiseValueError(f"no such item{name} in tree")
238+
239+
240+
if__name__=="__main__":
241+
out=0
242+
count=0
243+
basedir=pathlib.Path("lib/matplotlib")
244+
per_file_ignore= {
245+
# Edge cases for items set via `get_attr`, etc
246+
basedir/"__init__.py":MISSING_IMPL,
247+
# Base class has **kwargs, subclasses have more specific
248+
basedir/"ticker.py":VARKWARG,
249+
basedir/"layout_engine.py":VARKWARG,
250+
}
251+
forfinbasedir.rglob("**/*.py"):
252+
err,c=check_file(f,ignore=0|per_file_ignore.get(f,0))
253+
out|=err
254+
count+=c
255+
print("\n")
256+
print(f"{count} total errors found")
257+
sys.exit(out)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp