diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ba29818c46de..d391ab4a18eb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,3 +15,7 @@ updates: interval: "weekly" exclude-paths: - "ci/minver-requirements.txt" + - package-ecosystem: "pre-commit" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 32a3f0439b7f..f6af70b8b233 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -20,7 +20,7 @@ jobs: python-version: "3.x" - uses: j178/prek-action@cbc2f23eb5539cf20d82d1aabd0d0ecbcc56f4e3 # v2.0.2 with: - extra_args: --hook-stage manual --all-files + extra-args: --hook-stage manual --all-files ruff: name: ruff diff --git a/.github/workflows/nightlies.yml b/.github/workflows/nightlies.yml index 661234c3e16f..47d1de50781c 100644 --- a/.github/workflows/nightlies.yml +++ b/.github/workflows/nightlies.yml @@ -60,7 +60,7 @@ jobs: ls -l dist/ - name: Upload wheels to Anaconda Cloud as nightlies - uses: scientific-python/upload-nightly-action@5748273c71e2d8d3a61f3a11a16421c8954f9ecf # 0.6.3 + uses: scientific-python/upload-nightly-action@e76cfec8a4611fd02808a801b0ff5a7d7c1b2d99 # 0.6.4 with: artifacts_path: dist anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 947024cde375..bfce3c54a030 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ exclude: | ) repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - id: check-added-large-files - id: check-docstring-first @@ -29,7 +29,7 @@ repos: - id: trailing-whitespace exclude_types: [diff, svg] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 + rev: fc0f09a29bb495f4a91f00266155d6282d52485d # frozen: v1.20.2 hooks: - id: mypy additional_dependencies: @@ -42,16 +42,15 @@ repos: args: ["--config-file=pyproject.toml", "lib/matplotlib"] files: lib/matplotlib # Only run when files in lib/matplotlib are changed. pass_filenames: false - - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.15.11 + rev: d1b833175a5d08a925900115526febd8fe71c98e # frozen: v0.15.11 hooks: # Run the linter. - - id: ruff + - id: ruff-check args: [--fix, --show-fixes] - repo: https://github.com/codespell-project/codespell - rev: v2.4.1 + rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2 hooks: - id: codespell files: ^.*\.(py|c|cpp|h|m|md|rst|yml)$ @@ -61,25 +60,25 @@ repos: - "--skip" - "doc/project/credits.rst" - repo: https://github.com/pycqa/isort - rev: 6.0.1 + rev: a333737ed43df02b18e6c95477ea1b285b3de15a # frozen: 8.0.1 hooks: - id: isort name: isort (python) files: ^galleries/tutorials/|^galleries/examples/|^galleries/plot_types/ - repo: https://github.com/rstcheck/rstcheck - rev: v6.2.4 + rev: 77490ffa33bfc0928975ae3cf904219903db755d # frozen: v6.2.5 hooks: - id: rstcheck additional_dependencies: - sphinx>=1.8.1 - tomli - repo: https://github.com/adrienverge/yamllint - rev: v1.37.0 + rev: cba56bcde1fdd01c1deb3f945e69764c291a6530 # frozen: v1.38.0 hooks: - id: yamllint args: ["--strict", "--config-file=.yamllint.yml"] - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.33.0 + rev: 13614ab716a3113145f1294ed259d9fbe5678ff3 # frozen: 0.37.1 hooks: # TODO: Re-enable this when https://github.com/microsoft/azure-pipelines-vscode/issues/567 is fixed. # - id: check-azure-pipelines diff --git a/doc/conf.py b/doc/conf.py index ec522851c673..4be3fcf3afee 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -275,6 +275,7 @@ def autodoc_process_bases(app, name, obj, options, bases): 'python': ('https://docs.python.org/3/', None), 'scipy': ('https://docs.scipy.org/doc/scipy/', None), 'tornado': ('https://www.tornadoweb.org/en/stable/', None), + 'wx': ('https://docs.wxpython.org/', None), 'xarray': ('https://docs.xarray.dev/en/stable/', None), 'meson-python': ('https://mesonbuild.com/meson-python/', None), 'pip': ('https://pip.pypa.io/en/stable/', None), diff --git a/galleries/examples/user_interfaces/embedding_in_wx5_sgskip.py b/galleries/examples/user_interfaces/embedding_in_wx5_sgskip.py index f150e2106ead..f5442ec1f700 100644 --- a/galleries/examples/user_interfaces/embedding_in_wx5_sgskip.py +++ b/galleries/examples/user_interfaces/embedding_in_wx5_sgskip.py @@ -3,11 +3,17 @@ Embed in wx #5 ============== +.. tip:: + + As a development and debugging aid, you can replace :class:`wx.App` + by :class:`wx.lib.mixins.inspection.InspectableApp`. + This adds the capability to start the `Widget Inspection Tool + `_ + via :kbd:`Ctrl-Alt-I`. """ import wx import wx.lib.agw.aui as aui -import wx.lib.mixins.inspection as wit from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas from matplotlib.backends.backend_wxagg import \ @@ -44,11 +50,7 @@ def add(self, name="plot"): def demo(): - # Alternatively you could use: - # app = wx.App() - # InspectableApp is a great debug tool, see: - # http://wiki.wxpython.org/Widget%20Inspection%20Tool - app = wit.InspectableApp() + app = wx.App() frame = wx.Frame(None, -1, 'Plotter') plotter = PlotNotebook(frame) axes1 = plotter.add('figure 1').add_subplot() diff --git a/lib/matplotlib/_docstring.pyi b/lib/matplotlib/_docstring.pyi index fb52d0846123..7bb256a3032b 100644 --- a/lib/matplotlib/_docstring.pyi +++ b/lib/matplotlib/_docstring.pyi @@ -14,7 +14,6 @@ class Substitution: @overload def __init__(self, **kwargs: str): ... def __call__(self, func: _T) -> _T: ... - def update(self, *args, **kwargs): ... # type: ignore[no-untyped-def] class _ArtistKwdocLoader(dict[str, str]): diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index b2f8255367e8..c9e04a70b356 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -291,8 +291,11 @@ def get_datalim(self, transData): if isinstance(offsets, np.ma.MaskedArray): offsets = offsets.filled(np.nan) # get_path_collection_extents handles nan but not masked arrays + data_trf = transform.get_affine() - transData + if not data_trf.is_affine: + paths = [data_trf.transform_path_non_affine(p) for p in paths] return mpath.get_path_collection_extents( - transform.get_affine() - transData, paths, + data_trf.get_affine(), paths, self.get_transforms(), offset_trf.transform_non_affine(offsets), offset_trf.get_affine().frozen()) diff --git a/lib/matplotlib/colors.pyi b/lib/matplotlib/colors.pyi index 07bf01b8f995..d7fbbf181272 100644 --- a/lib/matplotlib/colors.pyi +++ b/lib/matplotlib/colors.pyi @@ -445,7 +445,7 @@ class MultiNorm(Norm): def __call__(self, values: tuple[float, ...], clip: ArrayLike | bool | None = ...) -> tuple[float, ...]: ... @overload def __call__(self, values: ArrayLike, clip: ArrayLike | bool | None = ...) -> tuple: ... - def inverse(self, values: ArrayLike) -> tuple: ... # type: ignore[override] + def inverse(self, values: ArrayLike) -> tuple: ... def autoscale(self, A: ArrayLike) -> None: ... def autoscale_None(self, A: ArrayLike) -> None: ... def scaled(self) -> bool: ... diff --git a/lib/matplotlib/dates.py b/lib/matplotlib/dates.py index f5f0918dcc13..77c803cde6e5 100644 --- a/lib/matplotlib/dates.py +++ b/lib/matplotlib/dates.py @@ -325,9 +325,7 @@ def _dt64_to_ordinalf(d): dt += extra.astype(np.float64) / 1.0e9 dt = dt / SEC_PER_DAY - NaT_int = np.datetime64('NaT').astype(np.int64) - d_int = d.astype(np.int64) - dt[d_int == NaT_int] = np.nan + dt[np.isnat(d)] = np.nan return dt diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 09f37018cc5f..dd80da45e332 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -870,7 +870,7 @@ def xkcd( "xkcd mode is not compatible with text.usetex = True") stack = ExitStack() - stack.callback(rcParams._update_raw, rcParams.copy()) # type: ignore[arg-type] + stack.callback(rcParams._update_raw, rcParams.copy()) from matplotlib import patheffects rcParams.update({ @@ -1077,8 +1077,7 @@ def figure( else: num = int(num) # crude validation of num argument - # Type of "num" has narrowed to int, but mypy can't quite see it - manager = _pylab_helpers.Gcf.get_fig_manager(num) # type: ignore[arg-type] + manager = _pylab_helpers.Gcf.get_fig_manager(num) if manager is None: max_open_warning = rcParams['figure.max_open_warning'] if len(allnums) == max_open_warning >= 1: @@ -1467,7 +1466,7 @@ def sca(ax: Axes) -> None: # but if you are calling this, it won't be None # Additionally the slight difference between `Figure` and `FigureBase` mypy catches fig = ax.get_figure(root=False) - figure(fig) # type: ignore[arg-type] + figure(fig) fig.sca(ax) # type: ignore[union-attr] diff --git a/lib/matplotlib/tests/test__style_helpers.py b/lib/matplotlib/tests/test__style_helpers.py index ecaccec224ae..23fca281b643 100644 --- a/lib/matplotlib/tests/test__style_helpers.py +++ b/lib/matplotlib/tests/test__style_helpers.py @@ -1,7 +1,7 @@ import pytest import matplotlib.colors as mcolors -from matplotlib.lines import _get_dash_pattern +from matplotlib.lines import _get_dash_pattern # type: ignore[attr-defined] from matplotlib._style_helpers import style_generator diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 7476e7b15e4f..6751666360b1 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -40,7 +40,7 @@ import matplotlib.text as mtext import matplotlib.ticker as mticker import matplotlib.transforms as mtransforms -import mpl_toolkits.axisartist as AA # type: ignore[import] +import mpl_toolkits.axisartist as AA from numpy.testing import ( assert_allclose, assert_array_equal, assert_array_almost_equal) from matplotlib.testing.decorators import ( @@ -4690,6 +4690,25 @@ def test_errorbar_limits(): ax.set_title('Errorbar upper and lower limits') +def test_errorbar_log_autoscale_order_independent(): + x = 10 ** np.array([18, 18.1, 18.2, 18.3]) + y = np.array([100, 80, 60, 30]) + yerr = np.ones_like(y) * 10 + + fig, (ax1, ax2) = plt.subplots(2) + + ax1.set_xscale("log") + ax1.set_yscale("log") + ax1.errorbar(x, y, yerr=yerr) + + ax2.errorbar(x, y, yerr=yerr) + ax2.set_xscale("log") + ax2.set_yscale("log") + + assert_allclose(ax1.get_xlim(), ax2.get_xlim()) + assert_allclose(ax1.get_ylim(), ax2.get_ylim()) + + def test_errorbar_nonefmt(): # Check that passing 'none' as a format still plots errorbars x = np.arange(5) diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index 306e6d794ccf..5d76300054d7 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -89,7 +89,7 @@ def _get_available_interactive_backends(): reason = "macosx backend fails on Azure" elif env["MPLBACKEND"].startswith('gtk'): try: - import gi # type: ignore[import] + import gi except ImportError: # Though we check that `gi` exists above, it is possible that its # C-level dependencies are not available, and then it still raises an diff --git a/lib/matplotlib/tests/test_basic.py b/lib/matplotlib/tests/test_basic.py index f6aa1e458555..f51f0ccf03ef 100644 --- a/lib/matplotlib/tests/test_basic.py +++ b/lib/matplotlib/tests/test_basic.py @@ -11,7 +11,7 @@ def test_simple(): def test_override_builtins(): - import pylab # type: ignore[import] + import pylab ok_to_override = { '__name__', '__doc__', diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index 294c62785159..9c9b4e643014 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -457,7 +457,7 @@ def test_EllipseCollection_setter_getter(): @image_comparison(['polycollection_close.png'], remove_text=True, style='mpl20') def test_polycollection_close(): - from mpl_toolkits.mplot3d import Axes3D # type: ignore[import] + from mpl_toolkits.mplot3d import Axes3D plt.rcParams['axes3d.automargin'] = True vertsQuad = [ diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py index b96505197890..7fd7cf356118 100644 --- a/lib/matplotlib/tests/test_ft2font.py +++ b/lib/matplotlib/tests/test_ft2font.py @@ -188,9 +188,9 @@ def test_ft2font_invalid_args(tmp_path): with pytest.raises(TypeError, match='incompatible constructor arguments'): # failing to be a list will fail before the 0 - ft2font.FT2Font(file, _fallback_list=(0,)) # type: ignore[arg-type] + ft2font.FT2Font(file, _fallback_list=(0,)) with pytest.raises(TypeError, match='incompatible constructor arguments'): - ft2font.FT2Font(file, _fallback_list=[0]) # type: ignore[list-item] + ft2font.FT2Font(file, _fallback_list=[0]) # kerning_factor argument. with pytest.raises(TypeError, match='incompatible constructor arguments'): diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index fe9405bcbdae..67b433fe7447 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -413,7 +413,7 @@ def test_error_mixed_args_and_kwargs(self): ax.legend((lnc, lns), labels=('a', 'b')) def test_parasite(self): - from mpl_toolkits.axes_grid1 import host_subplot # type: ignore[import] + from mpl_toolkits.axes_grid1 import host_subplot host = host_subplot(111) par = host.twinx() diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py index 1590990cdeb0..3494dceffe5d 100644 --- a/lib/matplotlib/tests/test_pickle.py +++ b/lib/matplotlib/tests/test_pickle.py @@ -12,12 +12,12 @@ from matplotlib import cm from matplotlib.testing import subprocess_run_helper, is_ci_environment from matplotlib.testing.decorators import check_figures_equal -from matplotlib.dates import rrulewrapper +from matplotlib.dates import rrulewrapper # type: ignore[attr-defined] from matplotlib.lines import VertexSelector import matplotlib.pyplot as plt import matplotlib.transforms as mtransforms import matplotlib.figure as mfigure -from mpl_toolkits.axes_grid1 import axes_divider, parasite_axes # type: ignore[import] +from mpl_toolkits.axes_grid1 import axes_divider, parasite_axes def test_simple(): diff --git a/lib/matplotlib/tests/test_units.py b/lib/matplotlib/tests/test_units.py index d2350667e94f..18106cd1713c 100644 --- a/lib/matplotlib/tests/test_units.py +++ b/lib/matplotlib/tests/test_units.py @@ -7,7 +7,7 @@ import matplotlib.patches as mpatches import matplotlib.units as munits from matplotlib.category import StrCategoryConverter, UnitData -from matplotlib.dates import DateConverter +from matplotlib.dates import DateConverter # type: ignore[attr-defined] import numpy as np import pytest diff --git a/lib/matplotlib/textpath.pyi b/lib/matplotlib/textpath.pyi index 07f81598aa75..49f2a4e1a21e 100644 --- a/lib/matplotlib/textpath.pyi +++ b/lib/matplotlib/textpath.pyi @@ -78,6 +78,6 @@ class TextPath(Path): # These are read only... there actually are protections in the base class, so probably can be deleted... @property # type: ignore[misc] - def vertices(self) -> np.ndarray: ... # type: ignore[override] + def vertices(self) -> np.ndarray: ... @property # type: ignore[misc] - def codes(self) -> np.ndarray: ... # type: ignore[override] + def codes(self) -> np.ndarray: ... diff --git a/lib/matplotlib/widgets.pyi b/lib/matplotlib/widgets.pyi index a80ed8bf8274..45422b6de719 100644 --- a/lib/matplotlib/widgets.pyi +++ b/lib/matplotlib/widgets.pyi @@ -164,7 +164,7 @@ class CheckButtons(AxesWidget): def set_label_props(self, props: dict[str, Sequence[Any]]) -> None: ... def set_frame_props(self, props: dict[str, Any]) -> None: ... def set_check_props(self, props: dict[str, Any]) -> None: ... - def set_active(self, index: int, state: bool | None = ...) -> None: ... # type: ignore[override] + def set_active(self, index: int, state: bool | None = ...) -> None: ... def clear(self) -> None: ... def get_status(self) -> list[bool]: ... def get_checked_labels(self) -> list[str]: ... diff --git a/pyproject.toml b/pyproject.toml index 357ad5786b93..f3c38512a2c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -151,7 +151,7 @@ test-extra = [ # Extra pip requirements for the GitHub Actions mypy build typing = [ - "mypy>=1.9", + "mypy>=1.14", "typing-extensions>=4.6", # Extra stubs distributed separately from the main pypi package "pandas-stubs", diff --git a/src/_enums.h b/src/_enums.h index 18f3d9aac9fa..e607b93f50f2 100644 --- a/src/_enums.h +++ b/src/_enums.h @@ -80,7 +80,7 @@ namespace p11x { auto ival = PyLong_AsLong(tmp); \ value = decltype(value)(ival); \ Py_DECREF(tmp); \ - return !(ival == -1 && !PyErr_Occurred()); \ + return !(ival == -1 && PyErr_Occurred()); \ } else { \ return false; \ } \