8000 Merge pull request #26928 from ksunden/stubtest_script · matplotlib/matplotlib@ef3bf5c · GitHub
[go: up one dir, main page]

Skip to content

Commit ef3bf5c

Browse files
authored
Merge pull request #26928 from ksunden/stubtest_script
[TYP] Add tool for running stubtest
2 parents 78e3661 + 06e4731 commit ef3bf5c

File tree

4 files changed

+91
-99
lines changed

4 files changed

+91
-99
lines changed

.github/workflows/mypy-stubtest.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ jobs:
3737
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3838
run: |
3939
set -o pipefail
40-
MPLBACKEND=agg python -m mypy.stubtest \
41-
--mypy-config-file pyproject.toml \
42-
--allowlist ci/mypy-stubtest-allowlist.txt \
43-
matplotlib | \
40+
MPLBACKEND=agg python tools/stubtest.py | \
4441
reviewdog \
4542
-efm '%Eerror: %m' \
4643
-efm '%CStub: in file %f:%l' \

ci/mypy-stubtest-allowlist.txt

Lines changed: 0 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -42,99 +42,14 @@ matplotlib.cm.register_cmap
4242
matplotlib.cm.unregister_cmap
4343

4444
# 3.8 deprecations
45-
matplotlib.cbook.get_sample_data
4645
matplotlib.contour.ContourSet.allkinds
4746
matplotlib.contour.ContourSet.allsegs
4847
matplotlib.contour.ContourSet.tcolors
4948
matplotlib.contour.ContourSet.tlinewidths
50-
matplotlib.ticker.LogLocator.__init__
51-
matplotlib.ticker.LogLocator.set_params
5249

5350
# positional-only argument name lacking leading underscores
5451
matplotlib.axes._base._AxesBase.axis
5552

56-
# Aliases (dynamically generated, not type hinted)
57-
matplotlib.collections.Collection.get_aa
58-
matplotlib.collections.Collection.get_antialiaseds
59-
matplotlib.collections.Collection.get_dashes
60-
matplotlib.collections.Collection.get_ec
61-
matplotlib.collections.Collection.get_edgecolors
62-
matplotlib.collections.Collection.get_facecolors
63-
matplotlib.collections.Collection.get_fc
64-
matplotlib.collections.Collection.get_linestyles
65-
matplotlib.collections.Collection.get_linewidths
66-
matplotlib.collections.Collection.get_ls
67-
matplotlib.collections.Collection.get_lw
68-
matplotlib.collections.Collection.get_transOffset
69-
matplotlib.collections.Collection.set_aa
70-
matplotlib.collections.Collection.set_antialiaseds
71-
matplotlib.collections.Collection.set_dashes
72-
matplotlib.collections.Collection.set_ec
73-
matplotlib.collections.Collection.set_edgecolors
74-
matplotlib.collections.Collection.set_facecolors
75-
matplotlib.collections.Collection.set_fc
76-
matplotlib.collections.Collection.set_linestyles
77-
matplotlib.collections.Collection.set_linewidths
78-
matplotlib.collections.Collection.set_ls
79-
matplotlib.collections.Collection.set_lw
80-
matplotlib.collections.Collection.set_transOffset
81-
matplotlib.lines.Line2D.get_aa
82-
matplotlib.lines.Line2D.get_c
83-
matplotlib.lines.Line2D.get_ds
84-
matplotlib.lines.Line2D.get_ls
85-
matplotlib.lines.Line2D.get_lw
86-
matplotlib.lines.Line2D.get_mec
87-
matplotlib.lines.Line2D.get_mew
88-
matplotlib.lines.Line2D.get_mfc
89-
matplotlib.lines.Line2D.get_mfcalt
90-
matplotlib.lines.Line2D.get_ms
91-
matplotlib.lines.Line2D.set_aa
92-
matplotlib.lines.Line2D.set_c
93-
matplotlib.lines.Line2D.set_ds
94-
matplotlib.lines.Line2D.set_ls
95-
matplotlib.lines.Line2D.set_lw
96-
matplotlib.lines.Line2D.set_mec
97-
matplotlib.lines.Line2D.set_mew
98-
matplotlib.lines.Line2D.set_mfc
99-
matplotlib.lines.Line2D.set_mfcalt
100-
matplotlib.lines.Line2D.set_ms
101-
matplotlib.patches.Patch.get_aa
102-
matplotlib.patches.Patch.get_ec
103-
matplotlib.patches.Patch.get_fc
104-
matplotlib.patches.Patch.get_ls
105-
matplotlib.patches.Patch.get_lw
106-
matplotlib.patches.Patch.set_aa
107-
matplotlib.patches.Patch.set_ec
108-
matplotlib.patches.Patch.set_fc
109-
matplotlib.patches.Patch.set_ls
110-
matplotlib.patches.Patch.set_lw
111-
matplotlib.text.Text.get_c
112-
matplotlib.text.Text.get_family
113-
matplotlib.text.Text.get_font
114-
matplotlib.text.Text.get_font_properties
115-
matplotlib.text.Text.get_ha
116-
matplotlib.text.Text.get_name
117-
matplotlib.text.Text.get_size
118-
matplotlib.text.Text.get_style
119-
matplotlib.text.Text.get_va
120-
matplotlib.text.Text.get_variant
121-
matplotlib.text.Text.get_weight
122-
matplotlib.text.Text.set_c
123-
matplotlib.text.Text.set_family
124-
matplotlib.text.Text.set_font
125-
matplotlib.text.Text.set_font_properties
126-
matplotlib.text.Text.set_ha
127-
matplotlib.text.Text.set_ma
128-
matplotlib.text.Text.set_name
129-
matplotlib.text.Text.set_size
130-
matplotlib.text.Text.set_stretch
131-
matplotlib.text.Text.set_style
132-
matplotlib.text.Text.set_va
133-
matplotlib.text.Text.set_variant
134-
matplotlib.text.Text.set_weight
135-
matplotlib.axes._base._AxesBase.get_fc
136-
matplotlib.axes._base._AxesBase.set_fc
137-
13853
# Maybe should be abstractmethods, required for subclasses, stubs define once
13954
matplotlib.tri.*TriInterpolator.__call__
14055
matplotlib.tri.*TriInterpolator.gradient

doc/devel/contribute.rst

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,7 @@ Introducing
425425
updated on introduction.
426426
- Items decorated with ``@_api.delete_parameter`` should include a default value hint
427427
for the deleted parameter, even if it did not previously have one (e.g.
428-
``param: <type> = ...``). Even so, the decorator changes the default value to a
429-
sentinel value which should not be included in the type stub. Thus, Mypy Stubtest
430-
needs to be informed of the inconsistency by placing the method into
431-
:file:`ci/mypy-stubtest-allowlist.txt` under a heading indicating the deprecation
432-
version number.
428+
``param: <type> = ...``).
433429

434430
Expiring
435431
~~~~~~~~
@@ -452,11 +448,11 @@ Expiring
452448
will have been updated at introduction, and require no change now.
453449
- Items decorated with ``@_api.delete_parameter`` will need to be updated to the
454450
final signature, in the same way as the ``.py`` file signature is updated.
455-
The entry in :file:`ci/mypy-stubtest-allowlist.txt` should be removed.
456-
- Any other entries in :file:`ci/mypy-stubtest-allowlist.txt` under a version's
457-
deprecations should be double checked, as only ``delete_parameter`` should normally
458-
require that mechanism for deprecation. For removed items that were not in the stub
459-
file, only deleting from the allowlist is required.
451+
- Any entries in :file:`ci/mypy-stubtest-allowlist.txt` which indicate a deprecation
452+
version should be double checked. In most cases this is not needed, though some
453+
items were never type hinted in the first place and were added to this file
454+
instead. For removed items that were not in the stub file, only deleting from the
455+
allowlist is required.
460456

461457
Adding new API
462458
--------------

tools/stubtest.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import ast
2+
import os
3+
import pathlib
4+
import subprocess
5+
import sys
6+
import tempfile
7+
8+
root = pathlib.Path(__file__).parent.parent
9+
10+
lib = root / "lib"
11+
mpl = lib / "matplotlib"
12+
13+
14+
class Visitor(ast.NodeVisitor):
15+
def __init__(self, filepath, output):
16+
self.filepath = filepath
17+
self.context = list(filepath.with_suffix("").relative_to(lib).parts)
18+
self.output = output
19+
20+
def visit_FunctionDef(self, node):
21+
if any("delete_parameter" in ast.unparse(line) for line in node.decorator_list):
22+
parents = []
23+
if hasattr(node, "parent"):
24+
parent = node.parent
25+
while hasattr(parent, "parent") and not isinstance(parent, ast.Module):
26+
parents.insert(0, parent.name)
27+
parent = parent.parent
28+
self.output.write(f"{'.'.join(self.context + parents)}.{node.name}\n")
29+
30+
def visit_ClassDef(self, node):
31+
for dec in node.decorator_list:
32+
if "define_aliases" in ast.unparse(dec):
33+
parents = []
34+
if hasattr(node, "parent"):
35+
parent = node.parent
36+
while hasattr(parent, "parent") and not isinstance(
37+
parent, ast.Module
38+
):
39+
parents.insert(0, parent.name)
40+
parent = parent.parent
41+
aliases = ast.literal_eval(dec.args[0])
42+
# Written as a regex rather than two lines to avoid unused entries
43+
# for setters on items with only a getter
44+
for substitutions in aliases.values():
45+
parts = self.context + parents + [node.name]
46+
self.output.write(
47+
"\n".join(
48+
f"{'.'.join(parts)}.[gs]et_{a}\n" for a in substitutions
49+
)
50+
)
51+
for child in ast.iter_child_nodes(node):
52+
self.visit(child)
53+
54+
55+
with tempfile.TemporaryDirectory() as d:
56+
p = pathlib.Path(d) / "allowlist.txt"
57+
with p.open("wt") as f:
58+
for path in mpl.glob("**/*.py"):
59+
v = Visitor(path, f)
60+
tree = ast.parse(path.read_text())
61+
62+
# Assign parents to tree so they can be backtraced
63+
for node in ast.walk(tree):
64+
for child in ast.iter_child_nodes(node):
65+
child.parent = node
66+
67+
v.visit(tree)
68+
proc = subprocess.run(
69+
[
70+
"stubtest",
71+
"--mypy-config-file=pyproject.toml",
72+
"--allowlist=ci/mypy-stubtest-allowlist.txt",
73+
f"--allowlist={p}",
74+
"matplotlib",
75+
],
76+
cwd=root,
77+
env=os.environ | {"MPLBACKEND": "agg"},
78+
)
79+
try:
80+
os.unlink(f.name)
81+
except OSError:
82+
pass
83+
84+
sys.exit(proc.returncode)

0 commit comments

Comments
 (0)
0