Skip to content

Commit 6404c95

Browse files
authored
Merge pull request matplotlib#28897 from QuLogic/clearer-stubtest
Fix minor issues in stubtest wrapper
2 parents bb68469 + 1f6bfca commit 6404c95

File tree

2 files changed

+56
-36
lines changed

2 files changed

+56
-36
lines changed

ci/mypy-stubtest-allowlist.txt

+28-28
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,51 @@
11
# Non-typed (and private) modules/functions
2-
matplotlib.backends.*
3-
matplotlib.tests.*
4-
matplotlib.pylab.*
5-
matplotlib._.*
6-
matplotlib.rcsetup._listify_validator
7-
matplotlib.rcsetup._validate_linestyle
8-
matplotlib.ft2font.Glyph
9-
matplotlib.testing.jpl_units.*
10-
matplotlib.sphinxext.*
2+
matplotlib\.backends\..*
3+
matplotlib\.tests(\..*)?
4+
matplotlib\.pylab\..*
5+
matplotlib\._.*
6+
matplotlib\.rcsetup\._listify_validator
7+
matplotlib\.rcsetup\._validate_linestyle
8+
matplotlib\.ft2font\.Glyph
9+
matplotlib\.testing\.jpl_units\..*
10+
matplotlib\.sphinxext(\..*)?
1111

1212
# set methods have heavy dynamic usage of **kwargs, with differences for subclasses
1313
# which results in technically inconsistent signatures, but not actually a problem
14-
matplotlib.*\.set$
14+
matplotlib\..*\.set$
1515

1616
# Typed inline, inconsistencies largely due to imports
17-
matplotlib.pyplot.*
18-
matplotlib.typing.*
17+
matplotlib\.pyplot\..*
18+
matplotlib\.typing\..*
1919

2020
# Other decorator modifying signature
2121
# Backcompat decorator which does not modify runtime reported signature
22-
matplotlib.offsetbox.*Offset[Bb]ox.get_offset
22+
matplotlib\.offsetbox\..*Offset[Bb]ox\.get_offset
2323

2424
# Inconsistent super/sub class parameter name (maybe rename for consistency)
25-
matplotlib.projections.polar.RadialLocator.nonsingular
26-
matplotlib.ticker.LogLocator.nonsingular
27-
matplotlib.ticker.LogitLocator.nonsingular
25+
matplotlib\.projections\.polar\.RadialLocator\.nonsingular
26+
matplotlib\.ticker\.LogLocator\.nonsingular
27+
matplotlib\.ticker\.LogitLocator\.nonsingular
2828

2929
# Stdlib/Enum considered inconsistent (no fault of ours, I don't think)
30-
matplotlib.backend_bases._Mode.__new__
31-
matplotlib.units.Number.__hash__
30+
matplotlib\.backend_bases\._Mode\.__new__
31+
matplotlib\.units\.Number\.__hash__
3232

3333
# 3.6 Pending deprecations
34-
matplotlib.figure.Figure.set_constrained_layout
35-
matplotlib.figure.Figure.set_constrained_layout_pads
36-
matplotlib.figure.Figure.set_tight_layout
34+
matplotlib\.figure\.Figure\.set_constrained_layout
35+
matplotlib\.figure\.Figure\.set_constrained_layout_pads
36+
matplotlib\.figure\.Figure\.set_tight_layout
3737

3838
# Maybe should be abstractmethods, required for subclasses, stubs define once
39-
matplotlib.tri.*TriInterpolator.__call__
40-
matplotlib.tri.*TriInterpolator.gradient
39+
matplotlib\.tri\..*TriInterpolator\.__call__
40+
matplotlib\.tri\..*TriInterpolator\.gradient
4141

4242
# TypeVar used only in type hints
43-
matplotlib.backend_bases.FigureCanvasBase._T
44-
matplotlib.backend_managers.ToolManager._T
45-
matplotlib.spines.Spine._T
43+
matplotlib\.backend_bases\.FigureCanvasBase\._T
44+
matplotlib\.backend_managers\.ToolManager\._T
45+
matplotlib\.spines\.Spine\._T
4646

4747
# Parameter inconsistency due to 3.10 deprecation
48-
matplotlib.figure.FigureBase.get_figure
48+
matplotlib\.figure\.FigureBase\.get_figure
4949

5050
# getitem method only exists for 3.10 deprecation backcompatability
51-
matplotlib.inset.InsetIndicator.__getitem__
51+
matplotlib\.inset\.InsetIndicator\.__getitem__

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)