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

Appearance settings

[TYP] Add tool for running stubtest #26928

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions 5 .github/workflows/mypy-stubtest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ jobs:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -o pipefail
MPLBACKEND=agg python -m mypy.stubtest \
--mypy-config-file pyproject.toml \
--allowlist ci/mypy-stubtest-allowlist.txt \
matplotlib | \
MPLBACKEND=agg python tools/stubtest.py | \
reviewdog \
-efm '%Eerror: %m' \
-efm '%CStub: in file %f:%l' \
Expand Down
85 changes: 0 additions & 85 deletions 85 ci/mypy-stubtest-allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,99 +42,14 @@ matplotlib.cm.register_cmap
matplotlib.cm.unregister_cmap

# 3.8 deprecations
matplotlib.cbook.get_sample_data
matplotlib.contour.ContourSet.allkinds
matplotlib.contour.ContourSet.allsegs
matplotlib.contour.ContourSet.tcolors
matplotlib.contour.ContourSet.tlinewidths
matplotlib.ticker.LogLocator.__init__
matplotlib.ticker.LogLocator.set_params

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

# Aliases (dynamically generated, not type hinted)
matplotlib.collections.Collection.get_aa
matplotlib.collections.Collection.get_antialiaseds
matplotlib.collections.Collection.get_dashes
matplotlib.collections.Collection.get_ec
matplotlib.collections.Collection.get_edgecolors
matplotlib.collections.Collection.get_facecolors
matplotlib.collections.Collection.get_fc
matplotlib.collections.Collection.get_linestyles
matplotlib.collections.Collection.get_linewidths
matplotlib.collections.Collection.get_ls
matplotlib.collections.Collection.get_lw
matplotlib.collections.Collection.get_transOffset
matplotlib.collections.Collection.set_aa
matplotlib.collections.Collection.set_antialiaseds
matplotlib.collections.Collection.set_dashes
matplotlib.collections.Collection.set_ec
matplotlib.collections.Collection.set_edgecolors
matplotlib.collections.Collection.set_facecolors
matplotlib.collections.Collection.set_fc
matplotlib.collections.Collection.set_linestyles
matplotlib.collections.Collection.set_linewidths
matplotlib.collections.Collection.set_ls
matplotlib.collections.Collection.set_lw
matplotlib.collections.Collection.set_transOffset
matplotlib.lines.Line2D.get_aa
matplotlib.lines.Line2D.get_c
matplotlib.lines.Line2D.get_ds
matplotlib.lines.Line2D.get_ls
matplotlib.lines.Line2D.get_lw
matplotlib.lines.Line2D.get_mec
matplotlib.lines.Line2D.get_mew
matplotlib.lines.Line2D.get_mfc
matplotlib.lines.Line2D.get_mfcalt
matplotlib.lines.Line2D.get_ms
matplotlib.lines.Line2D.set_aa
matplotlib.lines.Line2D.set_c
matplotlib.lines.Line2D.set_ds
matplotlib.lines.Line2D.set_ls
matplotlib.lines.Line2D.set_lw
matplotlib.lines.Line2D.set_mec
matplotlib.lines.Line2D.set_mew
matplotlib.lines.Line2D.set_mfc
matplotlib.lines.Line2D.set_mfcalt
matplotlib.lines.Line2D.set_ms
matplotlib.patches.Patch.get_aa
matplotlib.patches.Patch.get_ec
matplotlib.patches.Patch.get_fc
matplotlib.patches.Patch.get_ls
matplotlib.patches.Patch.get_lw
matplotlib.patches.Patch.set_aa
matplotlib.patches.Patch.set_ec
matplotlib.patches.Patch.set_fc
matplotlib.patches.Patch.set_ls
matplotlib.patches.Patch.set_lw
matplotlib.text.Text.get_c
matplotlib.text.Text.get_family
matplotlib.text.Text.get_font
matplotlib.text.Text.get_font_properties
matplotlib.text.Text.get_ha
matplotlib.text.Text.get_name
matplotlib.text.Text.get_size
matplotlib.text.Text.get_style
matplotlib.text.Text.get_va
matplotlib.text.Text.get_variant
matplotlib.text.Text.get_weight
matplotlib.text.Text.set_c
matplotlib.text.Text.set_family
matplotlib.text.Text.set_font
matplotlib.text.Text.set_font_properties
matplotlib.text.Text.set_ha
matplotlib.text.Text.set_ma
matplotlib.text.Text.set_name
matplotlib.text.Text.set_size
matplotlib.text.Text.set_stretch
matplotlib.text.Text.set_style
matplotlib.text.Text.set_va
matplotlib.text.Text.set_variant
matplotlib.text.Text.set_weight
matplotlib.axes._base._AxesBase.get_fc
matplotlib.axes._base._AxesBase.set_fc

# Maybe should be abstractmethods, required for subclasses, stubs define once
matplotlib.tri.*TriInterpolator.__call__
matplotlib.tri.*TriInterpolator.gradient
Expand Down
16 changes: 6 additions & 10 deletions 16 doc/devel/contribute.rst
Original file line number Diff line number Diff line change
Expand Up @@ -425,11 +425,7 @@ Introducing
updated on introduction.
- Items decorated with ``@_api.delete_parameter`` should include a default value hint
for the deleted parameter, even if it did not previously have one (e.g.
``param: <type> = ...``). Even so, the decorator changes the default value to a
sentinel value which should not be included in the type stub. Thus, Mypy Stubtest
needs to be informed of the inconsistency by placing the method into
:file:`ci/mypy-stubtest-allowlist.txt` under a heading indicating the deprecation
version number.
``param: <type> = ...``).

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

Adding new API
--------------
Expand Down
84 changes: 84 additions & 0 deletions 84 tools/stubtest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import ast
import os
import pathlib
import subprocess
import sys
import tempfile

root = pathlib.Path(__file__).parent.parent

lib = root / "lib"
mpl = lib / "matplotlib"


class Visitor(ast.NodeVisitor):
def __init__(self, filepath, output):
self.filepath = filepath
self.context = list(filepath.with_suffix("").relative_to(lib).parts)
self.output = output

def visit_FunctionDef(self, node):
if any("delete_parameter" in ast.unparse(line) for line in node.decorator_list):
parents = []
if hasattr(node, "parent"):
parent = node.parent
while hasattr(parent, "parent") and not isinstance(parent, ast.Module):
parents.insert(0, parent.name)
parent = parent.parent
self.output.write(f"{'.'.join(self.context + parents)}.{node.name}\n")

def visit_ClassDef(self, node):
for dec in node.decorator_list:
if "define_aliases" in ast.unparse(dec):
parents = []
if hasattr(node, "parent"):
parent = node.parent
while hasattr(parent, "parent") and not isinstance(
parent, ast.Module
):
parents.insert(0, parent.name)
parent = parent.parent
aliases = ast.literal_eval(dec.args[0])
# Written as a regex rather than two lines to avoid unused entries
# for setters on items with only a getter
for substitutions in aliases.values():
parts = self.context + parents + [node.name]
self.output.write(
"\n".join(
f"{'.'.join(parts)}.[gs]et_{a}\n" for a in substitutions
)
)
for child in ast.iter_child_nodes(node):
self.visit(child)


with tempfile.TemporaryDirectory() as d:
p = pathlib.Path(d) / "allowlist.txt"
with p.open("wt") as f:
for path in mpl.glob("**/*.py"):
v = Visitor(path, f)
tree = ast.parse(path.read_text())

# Assign parents to tree so they can be backtraced
for node in ast.walk(tree):
for child in ast.iter_child_nodes(node):
child.parent = node

v.visit(tree)
proc = subprocess.run(
[
"stubtest",
"--mypy-config-file=pyproject.toml",
"--allowlist=ci/mypy-stubtest-allowlist.txt",
f"--allowlist={p}",
"matplotlib",
],
cwd=root,
env=os.environ | {"MPLBACKEND": "agg"},
)
try:
QuLogic marked this conversation as resolved.
Show resolved Hide resolved
os.unlink(f.name)
except OSError:
pass
Comment on lines +79 to +82
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I just realized this is still here; it can probably be deleted now that you're using TemporaryDirectory?


sys.exit(proc.returncode)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.