From 7d77bb96707f10032d8fedf2bd225491f9a28afe Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Mon, 14 Jun 2021 19:08:30 -0400 Subject: [PATCH] Use packaging to do version comparisons. ... as `distutils` is deprecated in Python 3.10. The minimum is ~a year ago, which appears to be available in all distros that are close to current in Matplotlib. --- .github/workflows/tests.yml | 3 ++- examples/units/basic_units.py | 4 ++-- lib/matplotlib/__init__.py | 24 ++++++++++++------------ lib/matplotlib/backends/qt_compat.py | 7 ++++--- lib/matplotlib/testing/compare.py | 2 +- lib/matplotlib/testing/decorators.py | 15 ++++++++------- lib/matplotlib/tests/test_backend_pgf.py | 4 +++- lib/matplotlib/tests/tinypages/conf.py | 4 ++-- lib/matplotlib/texmanager.py | 4 +++- requirements/testing/minver.txt | 1 + setup.py | 1 + 11 files changed, 39 insertions(+), 30 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f38999bcb960..01fb18e06579 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -145,7 +145,8 @@ jobs: # Install dependencies from PyPI. python -m pip install --upgrade $PRE \ - cycler kiwisolver numpy pillow pyparsing python-dateutil setuptools-scm \ + cycler kiwisolver numpy packaging pillow pyparsing python-dateutil \ + setuptools-scm \ -r requirements/testing/all.txt \ ${{ matrix.extra-requirements }} diff --git a/examples/units/basic_units.py b/examples/units/basic_units.py index 3d08a7690f49..39b8a5f1f899 100644 --- a/examples/units/basic_units.py +++ b/examples/units/basic_units.py @@ -5,10 +5,10 @@ """ -from distutils.version import LooseVersion import math import numpy as np +from packaging.version import parse as parse_version import matplotlib.units as units import matplotlib.ticker as ticker @@ -155,7 +155,7 @@ def __str__(self): def __len__(self): return len(self.value) - if LooseVersion(np.__version__) >= '1.20': + if parse_version(np.__version__) >= parse_version('1.20'): def __getitem__(self, key): return TaggedValue(self.value[key], self.unit) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index e66b7f2a7f5d..7bd5ed2410ea 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -85,7 +85,6 @@ from collections import namedtuple from collections.abc import MutableMapping import contextlib -from distutils.version import LooseVersion import functools import importlib import inspect @@ -103,6 +102,7 @@ import warnings import numpy +from packaging.version import parse as parse_version # cbook must import matplotlib only within function # definitions, so it is safe to import from it here. @@ -165,9 +165,9 @@ def _check_versions(): ("pyparsing", "2.2.1"), ]: module = importlib.import_module(modname) - if LooseVersion(module.__version__) < minver: - raise ImportError("Matplotlib requires {}>={}; you have {}" - .format(modname, minver, module.__version__)) + if parse_version(module.__version__) < parse_version(minver): + raise ImportError(f"Matplotlib requires {modname}>={minver}; " + f"you have {module.__version__}") _check_versions() @@ -274,8 +274,7 @@ def _get_executable_info(name): ------- tuple A namedtuple with fields ``executable`` (`str`) and ``version`` - (`distutils.version.LooseVersion`, or ``None`` if the version cannot be - determined). + (`packaging.Version`, or ``None`` if the version cannot be determined). Raises ------ @@ -305,8 +304,8 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False): raise ExecutableNotFoundError(str(_ose)) from _ose match = re.search(regex, output) if match: - version = LooseVersion(match.group(1)) - if min_ver is not None and version < min_ver: + version = parse_version(match.group(1)) + if min_ver is not None and version < parse_version(min_ver): raise ExecutableNotFoundError( f"You have {args[0]} version {version} but the minimum " f"version supported by Matplotlib is {min_ver}") @@ -367,7 +366,7 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False): else: path = "convert" info = impl([path, "--version"], r"^Version: ImageMagick (\S*)") - if info.version == "7.0.10-34": + if info.version == parse_version("7.0.10-34"): # https://github.com/ImageMagick/ImageMagick/issues/2720 raise ExecutableNotFoundError( f"You have ImageMagick {info.version}, which is unsupported") @@ -375,9 +374,10 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False): elif name == "pdftops": info = impl(["pdftops", "-v"], "^pdftops version (.*)", ignore_exit_code=True) - if info and not ("3.0" <= info.version - # poppler version numbers. - or "0.9" <= info.version <= "1.0"): + if info and not ( + 3 <= info.version.major or + # poppler version numbers. + parse_version("0.9") <= info.version < parse_version("1.0")): raise ExecutableNotFoundError( f"You have pdftops version {info.version} but the minimum " f"version supported by Matplotlib is 3.0") diff --git a/lib/matplotlib/backends/qt_compat.py b/lib/matplotlib/backends/qt_compat.py index c43779de67bb..f31db2e98fc6 100644 --- a/lib/matplotlib/backends/qt_compat.py +++ b/lib/matplotlib/backends/qt_compat.py @@ -11,11 +11,12 @@ - otherwise, use whatever the rcParams indicate. """ -from distutils.version import LooseVersion import os import platform import sys +from packaging.version import parse as parse_version + import matplotlib as mpl @@ -102,8 +103,8 @@ def _isdeleted(obj): return not shiboken2.isValid(obj) # Fixes issues with Big Sur # https://bugreports.qt.io/browse/QTBUG-87014, fixed in qt 5.15.2 if (sys.platform == 'darwin' and - LooseVersion(platform.mac_ver()[0]) >= LooseVersion("10.16") and - LooseVersion(QtCore.qVersion()) < LooseVersion("5.15.2") and + parse_version(platform.mac_ver()[0]) >= parse_version("10.16") and + parse_version(QtCore.qVersion()) < parse_version("5.15.2") and "QT_MAC_WANTS_LAYER" not in os.environ): os.environ["QT_MAC_WANTS_LAYER"] = "1" diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index afb7edb8c828..3957188b3e2d 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -167,7 +167,7 @@ def encode_and_escape(name): class _SVGConverter(_Converter): def __call__(self, orig, dest): - old_inkscape = mpl._get_executable_info("inkscape").version < "1" + old_inkscape = mpl._get_executable_info("inkscape").version.major < 1 terminator = b"\n>" if old_inkscape else b"> " if not hasattr(self, "_tmpdir"): self._tmpdir = TemporaryDirectory() diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index 4f032aca2de9..6989640ca770 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -1,5 +1,4 @@ import contextlib -from distutils.version import StrictVersion import functools import inspect import os @@ -10,6 +9,8 @@ import unittest import warnings +from packaging.version import parse as parse_version + import matplotlib.style import matplotlib.units import matplotlib.testing @@ -92,20 +93,20 @@ def check_freetype_version(ver): if isinstance(ver, str): ver = (ver, ver) - ver = [StrictVersion(x) for x in ver] - found = StrictVersion(ft2font.__freetype_version__) + ver = [parse_version(x) for x in ver] + found = parse_version(ft2font.__freetype_version__) return ver[0] <= found <= ver[1] def _checked_on_freetype_version(required_freetype_version): import pytest - reason = ("Mismatched version of freetype. " - "Test requires '%s', you have '%s'" % - (required_freetype_version, ft2font.__freetype_version__)) return pytest.mark.xfail( not check_freetype_version(required_freetype_version), - reason=reason, raises=ImageComparisonFailure, strict=False) + reason=f"Mismatched version of freetype. " + f"Test requires '{required_freetype_version}', " + f"you have '{ft2font.__freetype_version__}'", + raises=ImageComparisonFailure, strict=False) def remove_ticks_and_titles(figure): diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index a608ad421809..55e375311e3c 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -4,6 +4,7 @@ import shutil import numpy as np +from packaging.version import parse as parse_version import pytest import matplotlib as mpl @@ -88,7 +89,8 @@ def test_xelatex(): try: - _old_gs_version = mpl._get_executable_info('gs').version < '9.50' + _old_gs_version = \ + mpl._get_executable_info('gs').version < parse_version('9.50') except mpl.ExecutableNotFoundError: _old_gs_version = True diff --git a/lib/matplotlib/tests/tinypages/conf.py b/lib/matplotlib/tests/tinypages/conf.py index 244eeb6a7b5e..08d59fa87ff9 100644 --- a/lib/matplotlib/tests/tinypages/conf.py +++ b/lib/matplotlib/tests/tinypages/conf.py @@ -1,5 +1,5 @@ import sphinx -from distutils.version import LooseVersion +from packaging.version import parse as parse_version # -- General configuration ------------------------------------------------ @@ -16,7 +16,7 @@ # -- Options for HTML output ---------------------------------------------- -if LooseVersion(sphinx.__version__) >= LooseVersion('1.3'): +if parse_version(sphinx.__version__) >= parse_version('1.3'): html_theme = 'classic' else: html_theme = 'default' diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index 965c72f8c6cf..6ebdfea02614 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -29,6 +29,7 @@ from tempfile import TemporaryDirectory import numpy as np +from packaging.version import parse as parse_version import matplotlib as mpl from matplotlib import _api, cbook, dviread, rcParams @@ -270,8 +271,9 @@ def make_png(self, tex, fontsize, dpi): # dvipng 1.16 has a bug (fixed in f3ff241) that breaks --freetype0 # mode, so for it we keep FreeType enabled; the image will be # slightly off. + bad_ver = parse_version("1.16") if (getattr(mpl, "_called_from_pytest", False) - and mpl._get_executable_info("dvipng").version != "1.16"): + and mpl._get_executable_info("dvipng").version != bad_ver): cmd.insert(1, "--freetype0") self._run_checked_subprocess(cmd, tex) return pngfile diff --git a/requirements/testing/minver.txt b/requirements/testing/minver.txt index db19a5648119..395a1d3e26c0 100644 --- a/requirements/testing/minver.txt +++ b/requirements/testing/minver.txt @@ -3,6 +3,7 @@ cycler==0.10 kiwisolver==1.0.1 numpy==1.17.0 +packaging==20.0 pillow==6.2.0 pyparsing==2.2.1 python-dateutil==2.7 diff --git a/setup.py b/setup.py index 54a5e4531a97..8fcc3ad9bceb 100644 --- a/setup.py +++ b/setup.py @@ -327,6 +327,7 @@ def make_release_tree(self, base_dir, files): "cycler>=0.10", "kiwisolver>=1.0.1", "numpy>=1.17", + "packaging>=20.0", "pillow>=6.2.0", "pyparsing>=2.2.1", "python-dateutil>=2.7",