From dba5ae4846290ef5ef516be0f333e832f83e7c15 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Mon, 19 Oct 2026 11:56:08 +0200 Subject: [PATCH 1/4] fix: move Tableau colormaps to their own namespace, fix catalog metadata - tableau colormaps were mistakenly in the 'seaborn' namespace (a copy-paste of the seaborn record, including its placeholder info); they're now 'tableau:*', and the old 'seaborn:tab*' names still work as aliases - describe each Tableau palette and where it comes from; tab20b/tab20c are d3's, not Tableau's - seaborn: real author and description instead of placeholders - cmocean, cubehelix: fill in empty authors - ibm, paraview, petroff: add their licenses (matching REUSE.toml / upstream) - catalog: a short name that's already an alias for the same colormap is not a conflict Co-Authored-By: Claude Opus 5.5 --- REUSE.toml | 8 +++++--- src/cmap/_catalog.py | 7 +++++-- src/cmap/data/cmocean/record.json | 3 +-- src/cmap/data/cubehelix/record.json | 3 ++- src/cmap/data/ibm/record.json | 1 + src/cmap/data/paraview/record.json | 4 +++- src/cmap/data/petroff/record.json | 1 + src/cmap/data/seaborn/record.json | 31 +++++++++++++++++++++++++++-- src/cmap/data/tableau/record.json | 25 +++++++++++++++++------ tests/test_catalog.py | 9 +++++++++ 10 files changed, 75 insertions(+), 17 deletions(-) diff --git a/REUSE.toml b/REUSE.toml index 70bb47e46..5c3f296af 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -178,7 +178,9 @@ precedence = "override" SPDX-FileCopyrightText = "Matthew A. Petroff" SPDX-License-Identifier = "CC0-1.0" -# NOTE: okabeito and matlab have no formal license. Okabe-Ito color values are -# freely shared with an attribution request (not a formal license). The matlab +# NOTE: okabeito, matlab and tableau have no formal license. Okabe-Ito color values +# are freely shared with an attribution request (not a formal license). The matlab # colormaps are trivial mathematical formulas independently re-implemented across -# many projects. Both fall through to the project default for now. +# many projects. Tableau has not published a license for its palettes (which are +# also redistributed by d3, Vega and matplotlib). All fall through to the project +# default for now. diff --git a/src/cmap/_catalog.py b/src/cmap/_catalog.py index 093a8f502..64b17ae71 100644 --- a/src/cmap/_catalog.py +++ b/src/cmap/_catalog.py @@ -183,9 +183,12 @@ def _build_catalog(records: Iterable[FileDescriptorOrPath]) -> CatalogDict: ctlg[name] = {"alias": namespaced, "conflicts": []} else: # if the short name is already in the catalog, we have a conflict. - # add the fully namespaced name to the conflicts list. + # add the fully namespaced name to the conflicts list (unless the + # short name is already an alias for this very colormap, e.g. from + # another namespace's backwards-compatibility alias). entry = cast("UnloadedCatalogAlias", ctlg[name]) - entry.setdefault("conflicts", []).append(namespaced) + if entry.get("alias") != namespaced: + entry.setdefault("conflicts", []).append(namespaced) # lastly, the `aliases` key of a colormap refers to aliases within the # namespace. These are keys that *must* be accessed using the fully diff --git a/src/cmap/data/cmocean/record.json b/src/cmap/data/cmocean/record.json index c899a33e1..fa37c9583 100644 --- a/src/cmap/data/cmocean/record.json +++ b/src/cmap/data/cmocean/record.json @@ -1,6 +1,6 @@ { "authors": [ - "" + "Kristen M. Thyng" ], "colormaps": { "algae": { @@ -104,7 +104,6 @@ "data": "cmap.data.cmocean.turbid:turbid" } }, - "info": "", "license": "MIT", "namespace": "cmocean", "source": "https://github.com/matplotlib/cmocean" diff --git a/src/cmap/data/cubehelix/record.json b/src/cmap/data/cubehelix/record.json index 1700f844c..bd6aad8d2 100644 --- a/src/cmap/data/cubehelix/record.json +++ b/src/cmap/data/cubehelix/record.json @@ -1,6 +1,7 @@ { "authors": [ - "" + "Dave Green", + "James Davenport" ], "colormaps": { "cubehelix": { diff --git a/src/cmap/data/ibm/record.json b/src/cmap/data/ibm/record.json index 83c8fee68..fa73bdfe3 100644 --- a/src/cmap/data/ibm/record.json +++ b/src/cmap/data/ibm/record.json @@ -12,6 +12,7 @@ } }, "info": "Color palettes for data visualizations from the IBM Design Language. Designed to maximize accessibility and harmony within a page.", + "license": "Apache-2.0", "namespace": "ibm", "source": "https://carbondesignsystem.com/data-visualization/color-palettes/#categorical-palettes" } diff --git a/src/cmap/data/paraview/record.json b/src/cmap/data/paraview/record.json index 82097b861..52f70f370 100644 --- a/src/cmap/data/paraview/record.json +++ b/src/cmap/data/paraview/record.json @@ -44,5 +44,7 @@ } }, "info": "Color maps of the ParaView platform for interactive, scientific visualization.", - "namespace": "paraview" + "license": "BSD-3-Clause", + "namespace": "paraview", + "source": "https://github.com/Kitware/ParaView" } diff --git a/src/cmap/data/petroff/record.json b/src/cmap/data/petroff/record.json index 1bdff6f55..9293257d3 100644 --- a/src/cmap/data/petroff/record.json +++ b/src/cmap/data/petroff/record.json @@ -18,6 +18,7 @@ } }, "info": "Color sequences generated using a data-driven aesthetic-preference model and accessibility constraints.", + "license": "CC0", "namespace": "petroff", "source": "https://arxiv.org/abs/2107.02270" } diff --git a/src/cmap/data/seaborn/record.json b/src/cmap/data/seaborn/record.json index 999cfa2dd..c50b24372 100644 --- a/src/cmap/data/seaborn/record.json +++ b/src/cmap/data/seaborn/record.json @@ -1,6 +1,6 @@ { "authors": [ - "John Doe" + "Michael Waskom" ], "colormaps": { "crest": { @@ -23,12 +23,39 @@ "category": "sequential", "data": "cmap.data.seaborn:rocket" }, + "tab10": { + "alias": "tableau:tab10" + }, + "tab10_colorblind": { + "alias": "tableau:tab10_colorblind" + }, + "tab10_light": { + "alias": "tableau:tab10_light" + }, + "tab10_medium": { + "alias": "tableau:tab10_medium" + }, + "tab10_new": { + "alias": "tableau:tab10_new" + }, + "tab20": { + "alias": "tableau:tab20" + }, + "tab20b": { + "alias": "tableau:tab20b" + }, + "tab20c": { + "alias": "tableau:tab20c" + }, + "tab_traffic_light": { + "alias": "tableau:tab_traffic_light" + }, "vlag": { "category": "diverging", "data": "cmap.data.seaborn:vlag" } }, - "info": "This is a test dataset", + "info": "Colormaps from seaborn, the statistical data visualization library.", "license": "BSD-3-Clause", "namespace": "seaborn", "source": "https://github.com/mwaskom/seaborn" diff --git a/src/cmap/data/tableau/record.json b/src/cmap/data/tableau/record.json index cc256863c..c06ad1c53 100644 --- a/src/cmap/data/tableau/record.json +++ b/src/cmap/data/tableau/record.json @@ -7,43 +7,56 @@ "colormaps": { "tab10": { "data": "cmap.data.tableau:Tableau10", + "info": "Tableau's classic \"Tableau 10\" palette, designed by Maureen Stone for Tableau 1.5. Also d3's `category10` and matplotlib's `tab10` (its default color cycle).", "interpolation": false }, "tab10_colorblind": { "data": "cmap.data.tableau:ColorBlind10", + "info": "Tableau's \"Color Blind 10\" palette, designed to be distinguishable with color vision deficiencies. Also matplotlib's `tableau-colorblind10` style.", "interpolation": false }, "tab10_light": { "data": "cmap.data.tableau:Tableau10_Light", + "info": "Tableau's classic \"Tableau 10 Light\" palette.", "interpolation": false }, "tab10_medium": { "data": "cmap.data.tableau:Tableau10_Medium", + "info": "Tableau's classic \"Tableau 10 Medium\" palette.", "interpolation": false }, "tab10_new": { "data": "cmap.data.tableau:Tableau10_New", + "info": "The \"Tableau 10\" palette as redesigned for Tableau 10 (2016) by Maureen Stone and Cristy Miller. Also d3's `schemeTableau10`.", "interpolation": false }, "tab20": { "data": "cmap.data.tableau:Tableau20", + "info": "Tableau's classic \"Tableau 20\" palette: light and dark pairs of the Tableau 10 hues. Also d3's `category20` and matplotlib's `tab20`.", "interpolation": false }, "tab20b": { + "authors": [], "data": "cmap.data.tableau:Tableau20b", - "interpolation": false + "info": "d3's `category20b` palette (also matplotlib's `tab20b`).", + "interpolation": false, + "source": "https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md" }, "tab20c": { + "authors": [], "data": "cmap.data.tableau:Tableau20c", - "interpolation": false + "info": "d3's `category20c` palette (also matplotlib's `tab20c`).", + "interpolation": false, + "source": "https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md" }, "tab_traffic_light": { "data": "cmap.data.tableau:Traffic_Light", + "info": "Tableau's \"Traffic Light\" palette.", "interpolation": false } }, - "info": "This is a test dataset", - "license": "BSD-3-Clause", - "namespace": "seaborn", - "source": "https://public.tableau.com/views/TableauColors/ColorPaletteswithRGBValues" + "info": "Categorical palettes from Tableau. The classic palettes date from Tableau 1.5 (2004); \"tab10_new\" is the current \"Tableau 10\", redesigned for Tableau 10 (2016): https://www.tableau.com/blog/colors-upgrade-tableau-10-56782", + "license": "UNKNOWN", + "namespace": "tableau", + "source": "https://public.tableau.com/app/profile/chris.gerrard/viz/TableauColors/ColorPaletteswithRGBValues" } diff --git a/tests/test_catalog.py b/tests/test_catalog.py index e83d3dd1c..8fee8f64a 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -69,3 +69,12 @@ def test_catalog_names() -> None: assert "viridis" in catalog.unique_keys(categories=("sequential",)) assert "viridis" not in catalog.unique_keys(interpolation="nearest") assert "viridis" in catalog.unique_keys(interpolation="linear") + + +@pytest.mark.parametrize("name", ["tab10", "seaborn:tab10", "tableau:tab10"]) +def test_tableau_names(name: str, recwarn: pytest.WarningsRecorder) -> None: + # the tableau colormaps used to be (mistakenly) in the seaborn namespace: + # the old names still work, without any ambiguity warning + assert catalog.resolve(name) == "tableau:tab10" + assert Colormap(name).name == "tableau:tab10" + assert not recwarn.list From 3518fed43b88771924426f8c793d515c54c4e832 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Mon, 19 Oct 2026 12:01:01 +0200 Subject: [PATCH 2/4] fix: don't over-claim Tableau palette provenance Only the Tableau 10 / Tableau 20 attributions are backed by Tableau's own sources; all of Tableau's palettes were redesigned in Tableau 10 (2016), so it's not known which version the others are. Co-Authored-By: Claude Opus 5.5 --- src/cmap/data/tableau/record.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cmap/data/tableau/record.json b/src/cmap/data/tableau/record.json index c06ad1c53..6f6f0c4d1 100644 --- a/src/cmap/data/tableau/record.json +++ b/src/cmap/data/tableau/record.json @@ -12,17 +12,17 @@ }, "tab10_colorblind": { "data": "cmap.data.tableau:ColorBlind10", - "info": "Tableau's \"Color Blind 10\" palette, designed to be distinguishable with color vision deficiencies. Also matplotlib's `tableau-colorblind10` style.", + "info": "Tableau's \"Color Blind 10\" palette. Also matplotlib's `tableau-colorblind10` style.", "interpolation": false }, "tab10_light": { "data": "cmap.data.tableau:Tableau10_Light", - "info": "Tableau's classic \"Tableau 10 Light\" palette.", + "info": "Tableau's \"Tableau 10 Light\" palette.", "interpolation": false }, "tab10_medium": { "data": "cmap.data.tableau:Tableau10_Medium", - "info": "Tableau's classic \"Tableau 10 Medium\" palette.", + "info": "Tableau's \"Tableau 10 Medium\" palette.", "interpolation": false }, "tab10_new": { From 2725be1d5b50133b1066c4926d0024809e8f659a Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Mon, 19 Oct 2026 12:01:10 +0200 Subject: [PATCH 3/4] fix: tableau record description matches its source Co-Authored-By: Claude Opus 5.5 --- src/cmap/data/tableau/record.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmap/data/tableau/record.json b/src/cmap/data/tableau/record.json index 6f6f0c4d1..d1524a09d 100644 --- a/src/cmap/data/tableau/record.json +++ b/src/cmap/data/tableau/record.json @@ -55,7 +55,7 @@ "interpolation": false } }, - "info": "Categorical palettes from Tableau. The classic palettes date from Tableau 1.5 (2004); \"tab10_new\" is the current \"Tableau 10\", redesigned for Tableau 10 (2016): https://www.tableau.com/blog/colors-upgrade-tableau-10-56782", + "info": "Categorical palettes from Tableau. The basic palettes (like Tableau 10 and Tableau 20) date from Tableau 1.5 (2004), and all of them were redesigned for Tableau 10 (2016): https://www.tableau.com/blog/colors-upgrade-tableau-10-56782", "license": "UNKNOWN", "namespace": "tableau", "source": "https://public.tableau.com/app/profile/chris.gerrard/viz/TableauColors/ColorPaletteswithRGBValues" From b4c4762bf860dfcf1608b3bf73715aef63fb2ce0 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Mon, 19 Oct 2026 12:11:14 +0200 Subject: [PATCH 4/4] fix: address review of tableau metadata - REUSE.toml: drop the BSD-3-Clause override for tableau (no known license) - per-colormap tableau authors (only where known); 2004 is when the palettes were designed, not when Tableau 1.5 shipped; 'nearly identical to' where the d3/matplotlib values differ slightly - test that actually catches the alias self-conflict (it's logged, not warned) - don't share one dict between an alias's short and namespaced names Co-Authored-By: Claude Opus 5.5 --- REUSE.toml | 6 ------ src/cmap/_catalog.py | 2 +- src/cmap/data/tableau/record.json | 22 ++++++++++++++-------- tests/test_catalog.py | 12 +++++++----- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/REUSE.toml b/REUSE.toml index 5c3f296af..2dc433370 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -136,12 +136,6 @@ precedence = "override" SPDX-FileCopyrightText = "Michael Waskom" SPDX-License-Identifier = "BSD-3-Clause" -[[annotations]] -path = "src/cmap/data/tableau/**" -precedence = "override" -SPDX-FileCopyrightText = "Maureen Stone, Cristy Miller" -SPDX-License-Identifier = "BSD-3-Clause" - [[annotations]] path = "src/cmap/data/tol/**" precedence = "override" diff --git a/src/cmap/_catalog.py b/src/cmap/_catalog.py index 64b17ae71..c1c160a2a 100644 --- a/src/cmap/_catalog.py +++ b/src/cmap/_catalog.py @@ -166,7 +166,7 @@ def _build_catalog(records: Iterable[FileDescriptorOrPath]) -> CatalogDict: if NAMESPACE_DELIMITER not in v["alias"]: # pragma: no cover raise ValueError(f"{namespaced!r} alias is not namespaced") ctlg[namespaced] = v - ctlg[name] = v # FIXME + ctlg[name] = {**v} # FIXME continue # here we add any global keys to the colormap that are not already there. diff --git a/src/cmap/data/tableau/record.json b/src/cmap/data/tableau/record.json index d1524a09d..93700f127 100644 --- a/src/cmap/data/tableau/record.json +++ b/src/cmap/data/tableau/record.json @@ -1,18 +1,17 @@ { - "authors": [ - "Maureen Stone", - "Cristy Miller" - ], "category": "qualitative", "colormaps": { "tab10": { + "authors": [ + "Maureen Stone" + ], "data": "cmap.data.tableau:Tableau10", - "info": "Tableau's classic \"Tableau 10\" palette, designed by Maureen Stone for Tableau 1.5. Also d3's `category10` and matplotlib's `tab10` (its default color cycle).", + "info": "Tableau's classic \"Tableau 10\" palette, designed by Maureen Stone in 2004 for Tableau 1.5. Also d3's `category10` and matplotlib's `tab10` (its default color cycle).", "interpolation": false }, "tab10_colorblind": { "data": "cmap.data.tableau:ColorBlind10", - "info": "Tableau's \"Color Blind 10\" palette. Also matplotlib's `tableau-colorblind10` style.", + "info": "Tableau's \"Color Blind 10\" palette. Nearly identical to matplotlib's `tableau-colorblind10` style.", "interpolation": false }, "tab10_light": { @@ -26,11 +25,18 @@ "interpolation": false }, "tab10_new": { + "authors": [ + "Maureen Stone", + "Cristy Miller" + ], "data": "cmap.data.tableau:Tableau10_New", - "info": "The \"Tableau 10\" palette as redesigned for Tableau 10 (2016) by Maureen Stone and Cristy Miller. Also d3's `schemeTableau10`.", + "info": "The \"Tableau 10\" palette as redesigned for Tableau 10 (2016) by Maureen Stone and Cristy Miller. Nearly identical to d3's `schemeTableau10`.", "interpolation": false }, "tab20": { + "authors": [ + "Maureen Stone" + ], "data": "cmap.data.tableau:Tableau20", "info": "Tableau's classic \"Tableau 20\" palette: light and dark pairs of the Tableau 10 hues. Also d3's `category20` and matplotlib's `tab20`.", "interpolation": false @@ -55,7 +61,7 @@ "interpolation": false } }, - "info": "Categorical palettes from Tableau. The basic palettes (like Tableau 10 and Tableau 20) date from Tableau 1.5 (2004), and all of them were redesigned for Tableau 10 (2016): https://www.tableau.com/blog/colors-upgrade-tableau-10-56782", + "info": "Categorical palettes from Tableau. The basic palettes (like Tableau 10 and Tableau 20) were designed by Maureen Stone in 2004 for Tableau 1.5 (https://www.tableau.com/research/people/maureen-stone), and all of them were redesigned for Tableau 10 in 2016 (https://www.tableau.com/blog/colors-upgrade-tableau-10-56782).", "license": "UNKNOWN", "namespace": "tableau", "source": "https://public.tableau.com/app/profile/chris.gerrard/viz/TableauColors/ColorPaletteswithRGBValues" diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 8fee8f64a..626da8688 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -72,9 +72,11 @@ def test_catalog_names() -> None: @pytest.mark.parametrize("name", ["tab10", "seaborn:tab10", "tableau:tab10"]) -def test_tableau_names(name: str, recwarn: pytest.WarningsRecorder) -> None: +def test_tableau_names(name: str, caplog: pytest.LogCaptureFixture) -> None: # the tableau colormaps used to be (mistakenly) in the seaborn namespace: - # the old names still work, without any ambiguity warning - assert catalog.resolve(name) == "tableau:tab10" - assert Colormap(name).name == "tableau:tab10" - assert not recwarn.list + # the old names still work, without any ambiguity warning. + # (a fresh catalog: the ambiguity warning is logged on the first load only) + cat = Catalog() + assert cat.resolve(name) == "tableau:tab10" + assert cat[name].qualified_name == "tableau:tab10" + assert not caplog.records