Skip to content

Commit 1f6bfca

Browse files
committed
ci: Skip adding a stubtest exception when already allowed
This fixes the "unused allowlist entry" error we see in matplotlib#28874.
1 parent 674751a commit 1f6bfca

File tree

1 file changed

+28
-8
lines changed

1 file changed

+28
-8
lines changed

tools/stubtest.py

+28-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ast
22
import os
33
import pathlib
4+
import re
45
import subprocess
56
import sys
67
import tempfile
@@ -12,10 +13,19 @@
1213

1314

1415
class Visitor(ast.NodeVisitor):
15-
def __init__(self, filepath, output):
16+
def __init__(self, filepath, output, existing_allowed):
1617
self.filepath = filepath
1718
self.context = list(filepath.with_suffix("").relative_to(lib).parts)
1819
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
1929

2030
def visit_FunctionDef(self, node):
2131
# delete_parameter adds a private sentinel value that leaks
@@ -43,7 +53,9 @@ def visit_FunctionDef(self, node):
4353
):
4454
parents.insert(0, parent.name)
4555
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")
4759
break
4860

4961
def visit_ClassDef(self, node):
@@ -62,20 +74,28 @@ def visit_ClassDef(self, node):
6274
# for setters on items with only a getter
6375
for substitutions in aliases.values():
6476
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"]))
7081
for child in ast.iter_child_nodes(node):
7182
self.visit(child)
7283

7384

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+
7494
with tempfile.TemporaryDirectory() as d:
7595
p = pathlib.Path(d) / "allowlist.txt"
7696
with p.open("wt") as f:
7797
for path in mpl.glob("**/*.py"):
78-
v = Visitor(path, f)
98+
v = Visitor(path, f, existing_allowed)
7999
tree = ast.parse(path.read_text())
80100

81101
# Assign parents to tree so they can be backtraced

0 commit comments

Comments
 (0)