From 5b0ad9970e85ef840ac880f821486a84f9817b6e Mon Sep 17 00:00:00 2001 From: Xylar Asay-Davis Date: Thu, 6 Aug 2026 08:18:10 +0200 Subject: [PATCH 1/2] Don't pass N to ListedColormap Passing `N` to `ListedColormap` is deprecated since matplotlib 3.11 and will be removed in 3.13. In each case here `N` was already equal to the number of colors being passed in, which is what matplotlib uses by default, so the argument can simply be dropped without any change in the resulting colormaps. Co-Authored-By: Claude Opus 5 --- cmocean/cm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmocean/cm.py b/cmocean/cm.py index 7ffe563..6c03393 100644 --- a/cmocean/cm.py +++ b/cmocean/cm.py @@ -53,11 +53,11 @@ def _register_cmap(cmap, *, name): rgb_with_alpha = np.zeros((rgb.shape[0],4)) rgb_with_alpha[:,:3] = rgb rgb_with_alpha[:,3] = 1. #set alpha channel to 1 - reg_map = colors.ListedColormap(rgb_with_alpha, N=rgb.shape[0]) + reg_map = colors.ListedColormap(rgb_with_alpha) _register_cmap(reg_map, name=f'cmo.{cmapname}') # Register the reversed map - reg_map_r = colors.ListedColormap(rgb_with_alpha[::-1,:], N=rgb.shape[0]) + reg_map_r = colors.ListedColormap(rgb_with_alpha[::-1,:]) _register_cmap(reg_map_r, name=f'cmo.{cmapname}_r') # Load inverted cmaps @@ -71,9 +71,9 @@ def _register_cmap(cmap, *, name): rgb_with_alpha[:,3] = 1. #set alpha channel to 1 # Register inverted cmaps - reg_map_i = colors.ListedColormap(rgb_with_alpha, N=rgb_i.shape[0]) + reg_map_i = colors.ListedColormap(rgb_with_alpha) _register_cmap(reg_map_i, name=f'cmo.{cmapname}_i') - reg_map_r_i = colors.ListedColormap(rgb_with_alpha[::-1,:], N=rgb_i.shape[0]) + reg_map_r_i = colors.ListedColormap(rgb_with_alpha[::-1,:]) _register_cmap(reg_map_r_i, name=f'cmo.{cmapname}_r_i') # order shouldn't matter From 16fbae4f2ba8847b71a8fc2a6a6e837cb9bc5468 Mon Sep 17 00:00:00 2001 From: Xylar Asay-Davis Date: Thu, 6 Aug 2026 08:34:38 +0200 Subject: [PATCH 2/2] Fix the matplotlib<3.5 fallback and modernize the build config The `matplotlib<3.5` branch of `_register_cmap()` has never actually worked: `cmocean/cm.py` does `import matplotlib as mpl`, which does not make the `matplotlib.cm` submodule available, so the branch raised `AttributeError: module 'matplotlib' has no attribute 'cm'`. A bare `import cmocean` therefore failed outright on matplotlib<3.5. The test suite missed this because `tests/test_cmocean.py` imports `matplotlib.pyplot` first, which pulls in `matplotlib.cm` as a side effect and makes the attribute resolve. Import the submodule explicitly so the fallback works on its own. With that fixed, cmocean imports cleanly and registers all 110 `cmo.*` colormaps with identical values on every matplotlib from 3.1.2 (the oldest with a CPython 3.8 wheel, matching `python_requires=">=3.8"`) through 3.11.1, and on every numpy from 1.17.3 through 2.5.1. Neither dependency imposes a lower bound of its own, so none is added; where old numpy does fail it is matplotlib's own `numpy>=1.20` check that rejects it, which is matplotlib's constraint to express, not cmocean's. The same goes for `packaging`, whose `Version` class behaves identically back to at least 16.8. The one constraint that is worth stating is on setuptools, and `setup_requires` is the wrong place for it: it is deprecated and warns about `fetch_build_eggs`. Declare it in a PEP 517 `[build-system]` table instead and drop `setup_requires`. `setuptools>=64` is what gets editable installs the real PEP 660 path rather than the deprecated `setup.py develop` fallback. Removing the unused `PyTest` command class (it was never wired up through `cmdclass`) drops the last reference to `setuptools.command.test`, which setuptools has disabled and warns about. The likewise ignored `tests_require` is replaced by a `test` extra, so `pip install cmocean[test]` pulls in pytest; a `[project.optional-dependencies]` table in `pyproject.toml` would have been the more modern home, but a partial `[project]` table is rejected, so that would mean moving all metadata out of `setup.py`. Co-Authored-By: Claude Opus 5 --- cmocean/cm.py | 7 +++++-- pyproject.toml | 3 +++ setup.py | 15 +-------------- 3 files changed, 9 insertions(+), 16 deletions(-) create mode 100644 pyproject.toml diff --git a/cmocean/cm.py b/cmocean/cm.py index 6c03393..e70984c 100644 --- a/cmocean/cm.py +++ b/cmocean/cm.py @@ -38,8 +38,11 @@ def _register_cmap(cmap, *, name): if MPL_VERSION >= Version("3.5"): mpl.colormaps.register(cmap, name=name) else: - # deprecated API - mpl.cm.register_cmap(name=name, cmap=cmap) + # deprecated API. `matplotlib.cm` is imported here because importing + # `matplotlib` alone does not make the submodule available, so this + # branch used to raise an AttributeError. + import matplotlib.cm + matplotlib.cm.register_cmap(name=name, cmap=cmap) # add colormaps and reversed to dictionary for cmapname in cmapnames: diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b2fcc25 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=64"] +build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 95d68bc..5c2c25b 100644 --- a/setup.py +++ b/setup.py @@ -4,22 +4,11 @@ setup.py for cmocean """ -import sys from setuptools import setup # to support "develop" mode -from setuptools.command.test import test as TestCommand - -class PyTest(TestCommand): - def finalize_options(self): - TestCommand.finalize_options(self) - self.verbose = True - - def run_tests(self): - import pytest - errno = pytest.main(self.test_args) - sys.exit(errno) extras_require={ 'plots': ["colorspacious", "viscm"], + 'test': ["pytest"], } # # in case I add more later # extras_require['complete'] = sorted(set(sum(extras_require.values(), []))) @@ -44,9 +33,7 @@ def run_tests(self): ext_package='cmocean', scripts = [], keywords = ['colormaps', 'oceanography', 'plotting', 'visualization'], - setup_requires=['setuptools'], install_requires=['matplotlib', 'numpy', 'packaging'], - tests_require=['pytest'], python_requires=">=3.8", extras_require=extras_require )