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/api/next_api_changes/deprecations/31416-TH.rst b/doc/api/next_api_changes/deprecations/31416-TH.rst new file mode 100644 index 000000000000..6d6d4b7b7809 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/31416-TH.rst @@ -0,0 +1,8 @@ +Formatter attributes +~~~~~~~~~~~~~~~~~~~~ + +These following attributes are considered internal and users should not have a need to access them: + +- `.ScalarFormatter`: ``orderOfMagnitude`` and ``format`` +- `.ConciseDateFormatter`: ``offset_format`` +- `.Formatter`: ``locs`` 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/doc/release/next_whats_new/underline-23616.rst b/doc/release/next_whats_new/underline-23616.rst new file mode 100644 index 000000000000..184c588b842c --- /dev/null +++ b/doc/release/next_whats_new/underline-23616.rst @@ -0,0 +1,12 @@ +Underlining text while using Mathtext +------------------------------------- + +Mathtext now supports the ``\underline`` command. + +.. code-block:: python + + import matplotlib.pyplot as plt + + plt.text(0.4, 0.7, r'This is $\underline{underlined}$ text.') + plt.text(0.4, 0.3, r'So is $\underline{\mathrm{this}}$.') + plt.show() 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/_mathtext.py b/lib/matplotlib/_mathtext.py index 21ec24d73286..b04386e7666a 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -2207,6 +2207,8 @@ def csnames(group: str, names: Iterable[str]) -> Regex: p.overline = cmd(r"\overline", p.required_group("body")) + p.underline = cmd(r"\underline", p.required_group("body")) + p.overset = cmd( r"\overset", p.optional_group("annotation") + p.optional_group("body")) @@ -2259,6 +2261,7 @@ def csnames(group: str, names: Iterable[str]) -> Regex: | p.underset | p.sqrt | p.overline + | p.underline | p.text | p.boldsymbol | p.substack @@ -2945,6 +2948,21 @@ def overline(self, toks: ParseResults) -> T.Any: hlist = Hlist([rightside]) return [hlist] + def underline(self, toks: ParseResults) -> T.Any: + body = toks["body"] + state = self.get_state() + thickness = state.get_current_underline_thickness() + # Place the underline below `body` (node735). + vlist = Vlist([ + Hlist([body]), + Kern(3 * thickness), + Hrule(state, thickness), + ]) + delta = vlist.height + vlist.depth + thickness + vlist.height = body.height + vlist.depth = delta - vlist.height + return [Hlist([vlist])] + def _auto_sized_delimiter(self, front: str, middle: list[Box | Char | str], back: str) -> T.Any: 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..225ca8e6ce76 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 @@ -665,6 +663,10 @@ class ConciseDateFormatter(ticker.Formatter): """ + offset_string = _api.deprecate_privatize_attribute( + "3.11", alternative="get_offset()" + ) + def __init__(self, locator, tz=None, formats=None, offset_formats=None, zero_formats=None, show_offset=True, *, usetex=None): """ @@ -719,7 +721,7 @@ def __init__(self, locator, tz=None, formats=None, offset_formats=None, '%Y-%b-%d', '%Y-%b-%d', '%Y-%b-%d %H:%M'] - self.offset_string = '' + self._offset_string = '' self.show_offset = show_offset self._usetex = mpl._val_or_rc(usetex, 'text.usetex') @@ -799,13 +801,13 @@ def format_ticks(self, values): if (self._locator.axis and self._locator.axis.__name__ in ('xaxis', 'yaxis') and self._locator.axis.get_inverted()): - self.offset_string = tickdatetime[0].strftime(offsetfmts[level]) + self._offset_string = tickdatetime[0].strftime(offsetfmts[level]) else: - self.offset_string = tickdatetime[-1].strftime(offsetfmts[level]) + self._offset_string = tickdatetime[-1].strftime(offsetfmts[level]) if self._usetex: - self.offset_string = _wrap_in_tex(self.offset_string) + self._offset_string = _wrap_in_tex(self._offset_string) else: - self.offset_string = '' + self._offset_string = '' if self._usetex: return [_wrap_in_tex(l) for l in labels] @@ -813,7 +815,7 @@ def format_ticks(self, values): return labels def get_offset(self): - return self.offset_string + return self._offset_string def format_data_short(self, value): return num2date(value, tz=self._tz).strftime('%Y-%m-%d %H:%M:%S') 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/testing/jpl_units/UnitDblFormatter.py b/lib/matplotlib/testing/jpl_units/UnitDblFormatter.py index 30a9914015bc..d5b815d1fc67 100644 --- a/lib/matplotlib/testing/jpl_units/UnitDblFormatter.py +++ b/lib/matplotlib/testing/jpl_units/UnitDblFormatter.py @@ -14,7 +14,7 @@ class UnitDblFormatter(ticker.ScalarFormatter): def __call__(self, x, pos=None): # docstring inherited - if len(self.locs) == 0: + if len(self._locs) == 0: return '' else: return f'{x:.12}' diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_09.png new file mode 100644 index 000000000000..9d96e8eb87b9 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_09.png differ 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_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index 33fb8918d22a..a8f9236b5208 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -154,6 +154,7 @@ r'$\left\lbrace\frac{\left\lbrack A^b_c\right\rbrace}{\left\leftbrace D^e_f \right\rbrack}\right\rightbrace\ \left\leftparen\max_{x} \left\lgroup \frac{A}{B}\right\rgroup \right\rightparen$', r'$\left( a\middle. b \right)$ $\left( \frac{a}{b} \middle\vert x_i \in P^S \right)$ $\left[ 1 - \middle| a\middle| + \left( x - \left\lfloor \dfrac{a}{b}\right\rfloor \right) \right]$', r'$\sum_{\substack{k = 1\\ k \neq \lfloor n/2\rfloor}}^{n}P(i,j) \sum_{\substack{i \neq 0\\ -1 \leq i \leq 3\\ 1 \leq j \leq 5}} F^i(x,y) \sum_{\substack{\left \lfloor \frac{n}{2} \right\rfloor}} F(n)$', + ' '.join(f'${c}\\underline{{{c}}}$' for c in 'abfghy') + r' $\underline{\left(\dfrac{num}{\underline{den}}\right)}^{\underline{p_1}}$', ] digits = "0123456789" 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_ticker.py b/lib/matplotlib/tests/test_ticker.py index 478a54b8a317..0d01677acc8c 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -920,7 +920,7 @@ def test_scilimits(self, sci_type, scilimits, lim, orderOfMag, fewticks): ax.yaxis.set_major_locator(mticker.MaxNLocator(4)) tmp_form.set_locs(ax.yaxis.get_majorticklocs()) - assert orderOfMag == tmp_form.orderOfMagnitude + assert orderOfMag == tmp_form._orderOfMagnitude @pytest.mark.parametrize('value, expected', format_data) def test_format_data(self, value, expected): 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/ticker.py b/lib/matplotlib/ticker.py index cec84e1f3f36..f214f44b97eb 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -213,7 +213,9 @@ class Formatter(TickHelper): """ # some classes want to see all the locs to help format # individual ones - locs = [] + _locs = [] + + locs = _api.deprecate_privatize_attribute("3.11") def __call__(self, x, pos=None): """ @@ -293,7 +295,7 @@ def set_locs(self, locs): This method is called before computing the tick labels because some formatters need to know all tick locations to do so. """ - self.locs = locs + self._locs = locs @staticmethod def fix_minus(s): @@ -507,6 +509,9 @@ class ScalarFormatter(Formatter): """ + orderOfMagnitude = _api.deprecate_privatize_attribute("3.11") + format = _api.deprecate_privatize_attribute("3.11") + def __init__(self, useOffset=None, useMathText=None, useLocale=None, *, usetex=None): useOffset = mpl._val_or_rc(useOffset, 'axes.formatter.useoffset') @@ -514,8 +519,8 @@ def __init__(self, useOffset=None, useMathText=None, useLocale=None, *, self.set_useOffset(useOffset) self.set_usetex(usetex) self.set_useMathText(useMathText) - self.orderOfMagnitude = 0 - self.format = '' + self._orderOfMagnitude = 0 + self._format = '' self._scientific = True self._powerlimits = mpl.rcParams['axes.formatter.limits'] self.set_useLocale(useLocale) @@ -664,13 +669,13 @@ def __call__(self, x, pos=None): """ Return the format for tick value *x* at position *pos*. """ - if len(self.locs) == 0: + if len(self._locs) == 0: return '' else: - xp = (x - self.offset) / (10. ** self.orderOfMagnitude) + xp = (x - self.offset) / (10. ** self._orderOfMagnitude) if abs(xp) < 1e-8: xp = 0 - return self._format_maybe_minus_and_locale(self.format, xp) + return self._format_maybe_minus_and_locale(self._format, xp) def set_scientific(self, b): """ @@ -764,20 +769,20 @@ def get_offset(self): """ Return scientific notation, plus offset. """ - if len(self.locs) == 0: + if len(self._locs) == 0: return '' - if self.orderOfMagnitude or self.offset: + if self._orderOfMagnitude or self.offset: offsetStr = '' sciNotStr = '' if self.offset: offsetStr = self.format_data(self.offset) if self.offset > 0: offsetStr = '+' + offsetStr - if self.orderOfMagnitude: + if self._orderOfMagnitude: if self._usetex or self._useMathText: - sciNotStr = self.format_data(10 ** self.orderOfMagnitude) + sciNotStr = self.format_data(10 ** self._orderOfMagnitude) else: - sciNotStr = '1e%d' % self.orderOfMagnitude + sciNotStr = '1e%d' % self._orderOfMagnitude if self._useMathText or self._usetex: if sciNotStr != '': sciNotStr = r'\times\mathdefault{%s}' % sciNotStr @@ -789,15 +794,15 @@ def get_offset(self): def set_locs(self, locs): # docstring inherited - self.locs = locs - if len(self.locs) > 0: + self._locs = locs + if len(self._locs) > 0: if self._useOffset: self._compute_offset() self._set_order_of_magnitude() self._set_format() def _compute_offset(self): - locs = self.locs + locs = self._locs # Restrict to visible ticks. vmin, vmax = sorted(self.axis.get_view_interval()) locs = np.asarray(locs) @@ -840,19 +845,19 @@ def _set_order_of_magnitude(self): # if using a numerical offset, find the exponent after applying the # offset. When lower power limit = upper <> 0, use provided exponent. if not self._scientific: - self.orderOfMagnitude = 0 + self._orderOfMagnitude = 0 return if self._powerlimits[0] == self._powerlimits[1] != 0: # fixed scaling when lower power limit = upper <> 0. - self.orderOfMagnitude = self._powerlimits[0] + self._orderOfMagnitude = self._powerlimits[0] return # restrict to visible ticks vmin, vmax = sorted(self.axis.get_view_interval()) - locs = np.asarray(self.locs) + locs = np.asarray(self._locs) locs = locs[(vmin <= locs) & (locs <= vmax)] locs = np.abs(locs) if not len(locs): - self.orderOfMagnitude = 0 + self._orderOfMagnitude = 0 return if self.offset: oom = math.floor(math.log10(vmax - vmin)) @@ -863,20 +868,20 @@ def _set_order_of_magnitude(self): else: oom = math.floor(math.log10(val)) if oom <= self._powerlimits[0]: - self.orderOfMagnitude = oom + self._orderOfMagnitude = oom elif oom >= self._powerlimits[1]: - self.orderOfMagnitude = oom + self._orderOfMagnitude = oom else: - self.orderOfMagnitude = 0 + self._orderOfMagnitude = 0 def _set_format(self): # set the format string to format all the ticklabels - if len(self.locs) < 2: + if len(self._locs) < 2: # Temporarily augment the locations with the axis end points. - _locs = [*self.locs, *self.axis.get_view_interval()] + _locs = [*self._locs, *self.axis.get_view_interval()] else: - _locs = self.locs - locs = (np.asarray(_locs) - self.offset) / 10. ** self.orderOfMagnitude + _locs = self._locs + locs = (np.asarray(_locs) - self.offset) / 10. ** self._orderOfMagnitude loc_range = np.ptp(locs) # Curvilinear coordinates can yield two identical points. if loc_range == 0: @@ -884,7 +889,7 @@ def _set_format(self): # Both points might be zero. if loc_range == 0: loc_range = 1 - if len(self.locs) < 2: + if len(self._locs) < 2: # We needed the end points only for the loc_range calculation. locs = locs[:-2] loc_range_oom = int(math.floor(math.log10(loc_range))) @@ -898,9 +903,9 @@ def _set_format(self): else: break sigfigs += 1 - self.format = f'%1.{sigfigs}f' + self._format = f'%1.{sigfigs}f' if self._usetex or self._useMathText: - self.format = r'$\mathdefault{%s}$' % self.format + self._format = r'$\mathdefault{%s}$' % self._format class LogFormatter(Formatter): @@ -1292,7 +1297,7 @@ def set_minor_number(self, minor_number): self._minor_number = minor_number def set_locs(self, locs): - self.locs = np.array(locs) + self._locs = np.array(locs) self._labelled.clear() if not self._minor: @@ -1318,7 +1323,7 @@ def set_locs(self, locs): # the previous, and between the ticks and the next one. Ticks # with smallest minimum are chosen. As tiebreak, the ticks # with smallest sum is chosen. - diff = np.diff(-np.log(1 / self.locs - 1)) + diff = np.diff(-np.log(1 / self._locs - 1)) space_pessimistic = np.minimum( np.concatenate(((np.inf,), diff)), np.concatenate((diff, (np.inf,))), @@ -1328,7 +1333,7 @@ def set_locs(self, locs): + np.concatenate((diff, (0,))) ) good_minor = sorted( - range(len(self.locs)), + range(len(self._locs)), key=lambda i: (space_pessimistic[i], space_sum[i]), )[-self._minor_number:] self._labelled.update(locs[i] for i in good_minor) @@ -1379,11 +1384,11 @@ def __call__(self, x, pos=None): exponent = round(math.log10(1 - x)) s = self._one_minus("10^{%d}" % exponent) elif x < 0.1: - s = self._format_value(x, self.locs) + s = self._format_value(x, self._locs) elif x > 0.9: - s = self._one_minus(self._format_value(1-x, 1-self.locs)) + s = self._one_minus(self._format_value(1-x, 1-self._locs)) else: - s = self._format_value(x, self.locs, sci_notation=False) + s = self._format_value(x, self._locs, sci_notation=False) return r"$\mathdefault{%s}$" % s def format_data_short(self, value): @@ -1489,18 +1494,18 @@ def __call__(self, x, pos=None): If there is no currently offset in the data, it returns the best engineering formatting that fits the given argument, independently. """ - if len(self.locs) == 0 or self.offset == 0: + if len(self._locs) == 0 or self.offset == 0: return self.fix_minus(self.format_data(x)) else: - xp = (x - self.offset) / (10. ** self.orderOfMagnitude) + xp = (x - self.offset) / (10. ** self._orderOfMagnitude) if abs(xp) < 1e-8: xp = 0 - return self._format_maybe_minus_and_locale(self.format, xp) + return self._format_maybe_minus_and_locale(self._format, xp) def set_locs(self, locs): # docstring inherited - self.locs = locs - if len(self.locs) > 0: + self._locs = locs + if len(self._locs) > 0: vmin, vmax = sorted(self.axis.get_view_interval()) if self._useOffset: self._compute_offset() @@ -1514,17 +1519,17 @@ def set_locs(self, locs): # value: self.offset = round((vmin + vmax)/2, 3) # Use log1000 to use engineers' oom standards - self.orderOfMagnitude = math.floor(math.log(vmax - vmin, 1000))*3 + self._orderOfMagnitude = math.floor(math.log(vmax - vmin, 1000))*3 self._set_format() # Simplify a bit ScalarFormatter.get_offset: We always want to use # self.format_data. Also we want to return a non-empty string only if there - # is an offset, no matter what is self.orderOfMagnitude. If there _is_ an - # offset, self.orderOfMagnitude is consulted. This behavior is verified + # is an offset, no matter what is self._orderOfMagnitude. If there _is_ an + # offset, self._orderOfMagnitude is consulted. This behavior is verified # in `test_ticker.py`. def get_offset(self): # docstring inherited - if len(self.locs) == 0: + if len(self._locs) == 0: return '' if self.offset: offsetStr = '' @@ -1532,7 +1537,7 @@ def get_offset(self): offsetStr = self.format_data(self.offset) if self.offset > 0: offsetStr = '+' + offsetStr - sciNotStr = self.format_data(10 ** self.orderOfMagnitude) + sciNotStr = self.format_data(10 ** self._orderOfMagnitude) if self._useMathText or self._usetex: if sciNotStr != '': sciNotStr = r'\times%s' % sciNotStr diff --git a/lib/matplotlib/ticker.pyi b/lib/matplotlib/ticker.pyi index bed288658909..6590faee7b10 100644 --- a/lib/matplotlib/ticker.pyi +++ b/lib/matplotlib/ticker.pyi @@ -24,6 +24,7 @@ class TickHelper: class Formatter(TickHelper): locs: list[float] + _locs: list[float] def __call__(self, x: float, pos: int | None = ...) -> str: ... def format_ticks(self, values: list[float]) -> list[str]: ... def format_data(self, value: float) -> str: ... @@ -58,7 +59,9 @@ class StrMethodFormatter(Formatter): class ScalarFormatter(Formatter): orderOfMagnitude: int + _orderOfMagnitude: int format: str + _format: str def __init__( self, useOffset: bool | float | None = ..., 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; \ } \