From 02250bfb3a61cdb3eeedee983676f63397081dd6 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 12 Feb 2026 07:53:12 +1000 Subject: [PATCH 1/9] Refactor rcsetup by extracting plot-type defaults into dedicated module --- ultraplot/internals/rc/__init__.py | 8 +++ ultraplot/internals/rc/plot_types.py | 92 ++++++++++++++++++++++++++++ ultraplot/internals/rcsetup.py | 69 +++------------------ ultraplot/tests/test_config.py | 12 ++++ 4 files changed, 120 insertions(+), 61 deletions(-) create mode 100644 ultraplot/internals/rc/__init__.py create mode 100644 ultraplot/internals/rc/plot_types.py diff --git a/ultraplot/internals/rc/__init__.py b/ultraplot/internals/rc/__init__.py new file mode 100644 index 000000000..b83b68ee0 --- /dev/null +++ b/ultraplot/internals/rc/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +""" +Helpers for composing rc parameter tables. +""" + +from .plot_types import build_plot_type_rc_table + +__all__ = ["build_plot_type_rc_table"] diff --git a/ultraplot/internals/rc/plot_types.py b/ultraplot/internals/rc/plot_types.py new file mode 100644 index 000000000..4472b5c40 --- /dev/null +++ b/ultraplot/internals/rc/plot_types.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +rc defaults for plot-type specific settings. +""" + +from __future__ import annotations + +from typing import Any, Callable + +RcValidator = Callable[[Any], Any] +RcEntry = tuple[Any, RcValidator, str] +RcTable = dict[str, RcEntry] + + +def build_plot_type_rc_table( + *, + validate_bool: RcValidator, + validate_color: RcValidator, + validate_float: RcValidator, + validate_int: RcValidator, + validate_string: RcValidator, +) -> RcTable: + """ + Return rc table entries scoped to plot types. + + Validators are passed from rcsetup to avoid import cycles and keep + validation behavior centralized. + """ + return { + # Curved quiver settings + "curved_quiver.arrowsize": ( + 1.0, + validate_float, + "Default size scaling for arrows in curved quiver plots.", + ), + "curved_quiver.arrowstyle": ( + "-|>", + validate_string, + "Default arrow style for curved quiver plots.", + ), + "curved_quiver.scale": ( + 1.0, + validate_float, + "Default scale factor for curved quiver plots.", + ), + "curved_quiver.grains": ( + 15, + validate_int, + "Default number of grains (segments) for curved quiver arrows.", + ), + "curved_quiver.density": ( + 10, + validate_int, + "Default density of arrows for curved quiver plots.", + ), + "curved_quiver.arrows_at_end": ( + True, + validate_bool, + "Whether to draw arrows at the end of curved quiver lines by default.", + ), + # Sankey settings + "sankey.nodepad": ( + 0.02, + validate_float, + "Vertical padding between nodes in layered sankey diagrams.", + ), + "sankey.nodewidth": ( + 0.03, + validate_float, + "Node width for layered sankey diagrams (axes-relative units).", + ), + "sankey.margin": ( + 0.05, + validate_float, + "Margin around layered sankey diagrams (axes-relative units).", + ), + "sankey.flow.alpha": ( + 0.75, + validate_float, + "Flow transparency for layered sankey diagrams.", + ), + "sankey.flow.curvature": ( + 0.5, + validate_float, + "Flow curvature for layered sankey diagrams.", + ), + "sankey.node.facecolor": ( + "0.75", + validate_color, + "Default node facecolor for layered sankey diagrams.", + ), + } diff --git a/ultraplot/internals/rcsetup.py b/ultraplot/internals/rcsetup.py index a5b0f0836..3e7e3e7d5 100644 --- a/ultraplot/internals/rcsetup.py +++ b/ultraplot/internals/rcsetup.py @@ -26,6 +26,7 @@ ic, # noqa: F401 warnings, ) +from .rc import build_plot_type_rc_table from .versions import _version_mpl # Regex for "probable" unregistered named colors. Try to retain warning message for @@ -972,73 +973,19 @@ def _validator_accepts(validator, value): "interpreted by `~ultraplot.utils.units`. Numeric units are points." ) _rc_ultraplot_table = { - # Curved quiver settings - "curved_quiver.arrowsize": ( - 1.0, - _validate_float, - "Default size scaling for arrows in curved quiver plots.", - ), - "curved_quiver.arrowstyle": ( - "-|>", - _validate_string, - "Default arrow style for curved quiver plots.", - ), - "curved_quiver.scale": ( - 1.0, - _validate_float, - "Default scale factor for curved quiver plots.", - ), - "curved_quiver.grains": ( - 15, - _validate_int, - "Default number of grains (segments) for curved quiver arrows.", - ), - "curved_quiver.density": ( - 10, - _validate_int, - "Default density of arrows for curved quiver plots.", - ), - "curved_quiver.arrows_at_end": ( - True, - _validate_bool, - "Whether to draw arrows at the end of curved quiver lines by default.", + # Plot-type settings are grouped in internals/rc/plot_types.py + **build_plot_type_rc_table( + validate_bool=_validate_bool, + validate_color=_validate_color, + validate_float=_validate_float, + validate_int=_validate_int, + validate_string=_validate_string, ), "external.shrink": ( 0.9, _validate_float, "Default shrink factor for external axes containers.", ), - # Sankey settings - "sankey.nodepad": ( - 0.02, - _validate_float, - "Vertical padding between nodes in layered sankey diagrams.", - ), - "sankey.nodewidth": ( - 0.03, - _validate_float, - "Node width for layered sankey diagrams (axes-relative units).", - ), - "sankey.margin": ( - 0.05, - _validate_float, - "Margin around layered sankey diagrams (axes-relative units).", - ), - "sankey.flow.alpha": ( - 0.75, - _validate_float, - "Flow transparency for layered sankey diagrams.", - ), - "sankey.flow.curvature": ( - 0.5, - _validate_float, - "Flow curvature for layered sankey diagrams.", - ), - "sankey.node.facecolor": ( - "0.75", - _validate_color, - "Default node facecolor for layered sankey diagrams.", - ), # Stylesheet "style": ( None, diff --git a/ultraplot/tests/test_config.py b/ultraplot/tests/test_config.py index 064808850..02d62b369 100644 --- a/ultraplot/tests/test_config.py +++ b/ultraplot/tests/test_config.py @@ -55,6 +55,18 @@ def test_sankey_rc_defaults(): assert uplt.rc["sankey.node.facecolor"] == "0.75" +def test_curved_quiver_rc_defaults(): + """ + Sanity check curved_quiver defaults in rc. + """ + assert uplt.rc["curved_quiver.arrowsize"] == 1.0 + assert uplt.rc["curved_quiver.arrowstyle"] == "-|>" + assert uplt.rc["curved_quiver.scale"] == 1.0 + assert uplt.rc["curved_quiver.grains"] == 15 + assert uplt.rc["curved_quiver.density"] == 10 + assert uplt.rc["curved_quiver.arrows_at_end"] is True + + import io from importlib.metadata import PackageNotFoundError from unittest.mock import MagicMock, patch From 7d1fabadd38a3fabbb4fbfab4300187c8e39f64d Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 12 Feb 2026 08:22:25 +1000 Subject: [PATCH 2/9] Complete rcsetup modularization with registry composition and duplicate-key guards --- ultraplot/internals/rc/__init__.py | 4 +- ultraplot/internals/rc/core.py | 1367 ++++++++++++++++++++++++++++ ultraplot/internals/rc/registry.py | 26 + ultraplot/internals/rcsetup.py | 1294 +------------------------- ultraplot/tests/test_config.py | 25 + 5 files changed, 1426 insertions(+), 1290 deletions(-) create mode 100644 ultraplot/internals/rc/core.py create mode 100644 ultraplot/internals/rc/registry.py diff --git a/ultraplot/internals/rc/__init__.py b/ultraplot/internals/rc/__init__.py index b83b68ee0..93fe2f96f 100644 --- a/ultraplot/internals/rc/__init__.py +++ b/ultraplot/internals/rc/__init__.py @@ -3,6 +3,8 @@ Helpers for composing rc parameter tables. """ +from .core import build_core_rc_table from .plot_types import build_plot_type_rc_table +from .registry import merge_rc_tables -__all__ = ["build_plot_type_rc_table"] +__all__ = ["build_core_rc_table", "build_plot_type_rc_table", "merge_rc_tables"] diff --git a/ultraplot/internals/rc/core.py b/ultraplot/internals/rc/core.py new file mode 100644 index 000000000..35a7087d1 --- /dev/null +++ b/ultraplot/internals/rc/core.py @@ -0,0 +1,1367 @@ +#!/usr/bin/env python3 +""" +Core rc defaults extracted from rcsetup. +""" + +from __future__ import annotations + +from typing import Any, Callable, Mapping + +RcValidator = Callable[[Any], Any] +RcEntry = tuple[Any, RcValidator, str] +RcTable = dict[str, RcEntry] + + +def build_core_rc_table(ns: Mapping[str, Any]) -> RcTable: + """Build the core ultraplot rc table from rcsetup namespace symbols.""" + BLACK = ns["BLACK"] + CMAPCAT = ns["CMAPCAT"] + CMAPCYC = ns["CMAPCYC"] + CMAPDIV = ns["CMAPDIV"] + CMAPSEQ = ns["CMAPSEQ"] + COLORBAR_LOCS = ns["COLORBAR_LOCS"] + CYCLE = ns["CYCLE"] + FONTNAME = ns["FONTNAME"] + FRAMEALPHA = ns["FRAMEALPHA"] + GRIDALPHA = ns["GRIDALPHA"] + GRIDBELOW = ns["GRIDBELOW"] + GRIDPAD = ns["GRIDPAD"] + GRIDRATIO = ns["GRIDRATIO"] + GRIDSTYLE = ns["GRIDSTYLE"] + LABELPAD = ns["LABELPAD"] + LARGESIZE = ns["LARGESIZE"] + LINEWIDTH = ns["LINEWIDTH"] + MARGIN = ns["MARGIN"] + MATHTEXT = ns["MATHTEXT"] + SMALLSIZE = ns["SMALLSIZE"] + TEXT_LOCS = ns["TEXT_LOCS"] + TICKDIR = ns["TICKDIR"] + TICKLEN = ns["TICKLEN"] + TICKLENRATIO = ns["TICKLENRATIO"] + TICKMINOR = ns["TICKMINOR"] + TICKPAD = ns["TICKPAD"] + TICKWIDTHRATIO = ns["TICKWIDTHRATIO"] + TITLEPAD = ns["TITLEPAD"] + WHITE = ns["WHITE"] + ZLINES = ns["ZLINES"] + ZPATCHES = ns["ZPATCHES"] + _addendum_em = ns["_addendum_em"] + _addendum_font = ns["_addendum_font"] + _addendum_in = ns["_addendum_in"] + _addendum_pt = ns["_addendum_pt"] + _addendum_rotation = ns["_addendum_rotation"] + _validate = ns["_validate"] + _validate_abc = ns["_validate_abc"] + _validate_belongs = ns["_validate_belongs"] + _validate_bool = ns["_validate_bool"] + _validate_bool_or_iterable = ns["_validate_bool_or_iterable"] + _validate_bool_or_string = ns["_validate_bool_or_string"] + _validate_boxstyle = ns["_validate_boxstyle"] + _validate_cftime_resolution = ns["_validate_cftime_resolution"] + _validate_cftime_resolution_format = ns["_validate_cftime_resolution_format"] + _validate_cmap = ns["_validate_cmap"] + _validate_color = ns["_validate_color"] + _validate_em = ns["_validate_em"] + _validate_float = ns["_validate_float"] + _validate_float_or_auto = ns["_validate_float_or_auto"] + _validate_float_or_iterable = ns["_validate_float_or_iterable"] + _validate_fontname = ns["_validate_fontname"] + _validate_fontsize = ns["_validate_fontsize"] + _validate_fontweight = ns["_validate_fontweight"] + _validate_in = ns["_validate_in"] + _validate_int = ns["_validate_int"] + _validate_joinstyle = ns["_validate_joinstyle"] + _validate_linestyle = ns["_validate_linestyle"] + _validate_or_none = ns["_validate_or_none"] + _validate_pt = ns["_validate_pt"] + _validate_rotation = ns["_validate_rotation"] + _validate_string = ns["_validate_string"] + _validate_string_or_iterable = ns["_validate_string_or_iterable"] + _validate_tuple_float_2 = ns["_validate_tuple_float_2"] + _validate_tuple_int_2 = ns["_validate_tuple_int_2"] + return { + "external.shrink": ( + 0.9, + _validate_float, + "Default shrink factor for external axes containers.", + ), + # Stylesheet + "style": ( + None, + _validate_or_none(_validate_string), + "The default matplotlib `stylesheet " + "`__ " # noqa: E501 + "name. If ``None``, a custom ultraplot style is used. " + "If ``'default'``, the default matplotlib style is used.", + ), + # A-b-c labels + "abc": ( + False, + _validate_abc, + "If ``False`` then a-b-c labels are disabled. If ``True`` the default label " + "style `a` is used. If string this indicates the style and must contain the " + "character `a` or ``A``, for example ``'a.'`` or ``'(A)'``.", + ), + "abc.border": ( + True, + _validate_bool, + "Whether to draw a white border around a-b-c labels " + "when :rcraw:`abc.loc` is inside the axes.", + ), + "abc.borderwidth": ( + 1.5, + _validate_pt, + "Width of the white border around a-b-c labels.", + ), + "text.borderstyle": ( + "bevel", + _validate_joinstyle, + "Join style for text border strokes. Must be one of " + "``'miter'``, ``'round'``, or ``'bevel'``.", + ), + "text.curved.upright": ( + True, + _validate_bool, + "Whether curved text is flipped to remain upright by default.", + ), + "text.curved.ellipsis": ( + False, + _validate_bool, + "Whether to show ellipses when curved text exceeds path length.", + ), + "text.curved.avoid_overlap": ( + True, + _validate_bool, + "Whether curved text hides overlapping glyphs by default.", + ), + "text.curved.overlap_tol": ( + 0.1, + _validate_float, + "Overlap threshold used when hiding curved-text glyphs.", + ), + "text.curved.curvature_pad": ( + 2.0, + _validate_float, + "Extra curved-text glyph spacing per radian of local curvature.", + ), + "text.curved.min_advance": ( + 1.0, + _validate_float, + "Minimum extra curved-text glyph spacing in pixels.", + ), + "abc.bbox": ( + False, + _validate_bool, + "Whether to draw semi-transparent bounding boxes around a-b-c labels " + "when :rcraw:`abc.loc` is inside the axes.", + ), + "abc.bboxcolor": (WHITE, _validate_color, "a-b-c label bounding box color."), + "abc.bboxstyle": ("square", _validate_boxstyle, "a-b-c label bounding box style."), + "abc.bboxalpha": (0.5, _validate_float, "a-b-c label bounding box opacity."), + "abc.bboxpad": ( + None, + _validate_or_none(_validate_pt), + "Padding for the a-b-c label bounding box. By default this is scaled " + "to make the box flush against the subplot edge." + _addendum_pt, + ), + "abc.color": (BLACK, _validate_color, "a-b-c label color."), + "abc.loc": ( + "left", # left side above the axes + _validate_belongs(*TEXT_LOCS), + "a-b-c label position. " + "For options see the :ref:`location table `.", + ), + "abc.size": ( + LARGESIZE, + _validate_fontsize, + "a-b-c label font size." + _addendum_font, + ), + "abc.titlepad": ( + LABELPAD, + _validate_pt, + "Padding separating the title and a-b-c label when in the same location." + + _addendum_pt, + ), + "abc.weight": ("bold", _validate_fontweight, "a-b-c label font weight."), + # Autoformatting + "autoformat": ( + True, + _validate_bool, + "Whether to automatically apply labels from `pandas.Series`, " + "`pandas.DataFrame`, and `xarray.DataArray` objects passed to " + "plotting functions. See also :rcraw:`unitformat`.", + ), + # Axes additions + "axes.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity of the background axes patch.", + ), + "axes.inbounds": ( + True, + _validate_bool, + "Whether to exclude out-of-bounds data when determining the default *y* (*x*) " + "axis limits and the *x* (*y*) axis limits have been locked.", + ), + "axes.margin": ( + MARGIN, + _validate_float, + "The fractional *x* and *y* axis margins when limits are unset.", + ), + "bar.bar_labels": ( + False, + _validate_bool, + "Add value of the bars to the bar labels", + ), + # Country borders + "borders": (False, _validate_bool, "Toggles country border lines on and off."), + "borders.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for country border lines.", + ), + "borders.color": (BLACK, _validate_color, "Line color for country border lines."), + "borders.linewidth": ( + LINEWIDTH, + _validate_pt, + "Line width for country border lines.", + ), + "borders.zorder": (ZLINES, _validate_float, "Z-order for country border lines."), + "borders.rasterized": ( + False, + _validate_bool, + "Toggles rasterization on or off for border feature in GeoAxes.", + ), + # Bottom subplot labels + "bottomlabel.color": ( + BLACK, + _validate_color, + "Font color for column labels on the bottom of the figure.", + ), + "bottomlabel.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and column labels on the bottom of the figure." + + _addendum_pt, + ), + "bottomlabel.rotation": ( + "horizontal", + _validate_rotation, + "Rotation for column labels at the bottom of the figure." + _addendum_rotation, + ), + "bottomlabel.size": ( + LARGESIZE, + _validate_fontsize, + "Font size for column labels on the bottom of the figure." + _addendum_font, + ), + "bottomlabel.weight": ( + "bold", + _validate_fontweight, + "Font weight for column labels on the bottom of the figure.", + ), + "cftime.time_unit": ( + "days since 2000-01-01", + _validate_string, + "Time unit for non-Gregorian calendars.", + ), + "cftime.resolution": ( + "DAILY", + _validate_cftime_resolution, + "Default time resolution for non-Gregorian calendars.", + ), + "cftime.time_resolution_format": ( + { + "SECONDLY": "%S", + "MINUTELY": "%M", + "HOURLY": "%H", + "DAILY": "%d", + "MONTHLY": "%m", + "YEARLY": "%Y", + }, + _validate_cftime_resolution_format, + "Dict used for formatting non-Gregorian calendars.", + ), + "cftime.max_display_ticks": ( + 7, + _validate_int, + "Number of ticks to display for cftime units.", + ), + # Coastlines + "coast": (False, _validate_bool, "Toggles coastline lines on and off."), + "coast.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for coast lines", + ), + "coast.color": (BLACK, _validate_color, "Line color for coast lines."), + "coast.linewidth": (LINEWIDTH, _validate_pt, "Line width for coast lines."), + "coast.zorder": (ZLINES, _validate_float, "Z-order for coast lines."), + "coast.rasterized": ( + False, + _validate_bool, + "Toggles the rasterization of the coastlines feature for GeoAxes.", + ), + # Colorbars + "colorbar.center_levels": ( + False, + _validate_bool, + "Center the ticks in the center of each segment.", + ), + "colorbar.edgecolor": ( + BLACK, + _validate_color, + "Color for the inset colorbar frame edge.", + ), + "colorbar.extend": ( + 1.3, + _validate_em, + 'Length of rectangular or triangular "extensions" for panel colorbars.' + + _addendum_em, + ), + "colorbar.outline": ( + True, + _validate_bool, + "Whether to draw a frame around the colorbar.", + ), + "colorbar.labelrotation": ( + "auto", + _validate_float_or_auto, + "Rotation of colorbar labels.", + ), + "colorbar.fancybox": ( + False, + _validate_bool, + 'Whether to use a "fancy" round bounding box for inset colorbar frames.', + ), + "colorbar.framealpha": ( + FRAMEALPHA, + _validate_float, + "Opacity for inset colorbar frames.", + ), + "colorbar.facecolor": ( + WHITE, + _validate_color, + "Color for the inset colorbar frame.", + ), + "colorbar.frameon": ( + True, + _validate_bool, + "Whether to draw a frame behind inset colorbars.", + ), + "colorbar.grid": ( + False, + _validate_bool, + "Whether to draw borders between each level of the colorbar.", + ), + "colorbar.insetextend": ( + 0.9, + _validate_em, + 'Length of rectangular or triangular "extensions" for inset colorbars.' + + _addendum_em, + ), + "colorbar.insetlength": ( + 8, + _validate_em, + "Length of inset colorbars." + _addendum_em, + ), + "colorbar.insetpad": ( + 0.7, + _validate_em, + "Padding between axes edge and inset colorbars." + _addendum_em, + ), + "colorbar.insetwidth": ( + 1.2, + _validate_em, + "Width of inset colorbars." + _addendum_em, + ), + "colorbar.length": (1, _validate_em, "Length of outer colorbars."), + "colorbar.loc": ( + "right", + _validate_belongs(*COLORBAR_LOCS), + "Inset colorbar location. " + "For options see the :ref:`location table `.", + ), + "colorbar.width": (0.2, _validate_in, "Width of outer colorbars." + _addendum_in), + "colorbar.rasterized": ( + False, + _validate_bool, + "Whether to use rasterization for colorbar solids.", + ), + "colorbar.shadow": ( + False, + _validate_bool, + "Whether to add a shadow underneath inset colorbar frames.", + ), + # Color cycle additions + "cycle": ( + CYCLE, + _validate_cmap("discrete", cycle=True), + "Name of the color cycle assigned to :rcraw:`axes.prop_cycle`.", + ), + # Colormap additions + "cmap": ( + CMAPSEQ, + _validate_cmap("continuous"), + "Alias for :rcraw:`cmap.sequential` and :rcraw:`image.cmap`.", + ), + "cmap.autodiverging": ( + True, + _validate_bool, + "Whether to automatically apply a diverging colormap and " + "normalizer based on the data.", + ), + "cmap.qualitative": ( + CMAPCAT, + _validate_cmap("discrete"), + "Default colormap for qualitative datasets.", + ), + "cmap.cyclic": ( + CMAPCYC, + _validate_cmap("continuous"), + "Default colormap for cyclic datasets.", + ), + "cmap.discrete": ( + None, + _validate_or_none(_validate_bool), + "If ``True``, `~ultraplot.colors.DiscreteNorm` is used for every colormap plot. " + "If ``False``, it is never used. If ``None``, it is used for all plot types " + "except `imshow`, `matshow`, `spy`, `hexbin`, and `hist2d`.", + ), + "cmap.diverging": ( + CMAPDIV, + _validate_cmap("continuous"), + "Default colormap for diverging datasets.", + ), + "cmap.inbounds": ( + True, + _validate_bool, + "If ``True`` and the *x* and *y* axis limits are fixed, only in-bounds data " + "is considered when determining the default colormap `vmin` and `vmax`.", + ), + "cmap.levels": ( + 11, + _validate_int, + "Default number of `~ultraplot.colors.DiscreteNorm` levels for plotting " + "commands that use colormaps.", + ), + "cmap.listedthresh": ( + 64, + _validate_int, + "Native `~matplotlib.colors.ListedColormap`\\ s with more colors than " + "this are converted to :class:`~ultraplot.colors.ContinuousColormap` rather than " + ":class:`~ultraplot.colors.DiscreteColormap`. This helps translate continuous " + "colormaps from external projects.", + ), + "cmap.lut": ( + 256, + _validate_int, + "Number of colors in the colormap lookup table. " + "Alias for :rcraw:`image.lut`.", + ), + "cmap.robust": ( + False, + _validate_bool, + "If ``True``, the default colormap `vmin` and `vmax` are chosen using the " + "2nd to 98th percentiles rather than the minimum and maximum.", + ), + "cmap.sequential": ( + CMAPSEQ, + _validate_cmap("continuous"), + "Default colormap for sequential datasets. Alias for :rcraw:`image.cmap`.", + ), + # Special setting + "edgefix": ( + True, + _validate_bool, + 'Whether to fix issues with "white lines" appearing between patches ' + "in saved vector graphics and with vector graphic backends. Applies " + "to colorbar levels and bar, area, pcolor, and contour plots.", + ), + # Font settings + "font.name": (FONTNAME, _validate_fontname, "Alias for :rcraw:`font.family`."), + "font.small": (SMALLSIZE, _validate_fontsize, "Alias for :rcraw:`font.smallsize`."), + "font.smallsize": ( + SMALLSIZE, + _validate_fontsize, + "Meta setting that changes the label-like sizes ``axes.labelsize``, " + "``legend.fontsize``, ``tick.labelsize``, and ``grid.labelsize``. Default is " + "``'medium'`` (equivalent to :rcraw:`font.size`)." + _addendum_font, + ), + "font.large": (LARGESIZE, _validate_fontsize, "Alias for :rcraw:`font.largesize`."), + "font.largesize": ( + LARGESIZE, + _validate_fontsize, + "Meta setting that changes the title-like sizes ``abc.size``, ``title.size``, " + "``suptitle.size``, ``leftlabel.size``, ``rightlabel.size``, etc. Default is " + "``'med-large'`` (i.e. 1.1 times :rcraw:`font.size`)." + _addendum_font, + ), + # Formatter settings + "formatter.timerotation": ( + "vertical", + _validate_rotation, + "Rotation for *x* axis datetime tick labels." + _addendum_rotation, + ), + "formatter.zerotrim": ( + True, + _validate_bool, + "Whether to trim trailing decimal zeros on tick labels.", + ), + "formatter.log": ( + False, + _validate_bool, + "Whether to use log formatting (e.g., $10^{4}$) for " + "logarithmically scaled axis tick labels.", + ), + "formatter.limits": ( + [-5, 6], # must be list or else validated + _validate["axes.formatter.limits"], + "Alias for :rcraw:`axes.formatter.limits`.", + ), + "formatter.min_exponent": ( + 0, + _validate["axes.formatter.min_exponent"], + "Alias for :rcraw:`axes.formatter.min_exponent`.", + ), + "formatter.offset_threshold": ( + 4, + _validate["axes.formatter.offset_threshold"], + "Alias for :rcraw:`axes.formatter.offset_threshold`.", + ), + "formatter.use_locale": ( + False, + _validate_bool, + "Alias for :rcraw:`axes.formatter.use_locale`.", + ), + "formatter.use_mathtext": ( + MATHTEXT, + _validate_bool, + "Alias for :rcraw:`axes.formatter.use_mathtext`.", + ), + "formatter.use_offset": ( + True, + _validate_bool, + "Alias for :rcraw:`axes.formatter.useOffset`.", + ), + # Geographic axes settings + "geo.backend": ( + "cartopy", + _validate_belongs("cartopy", "basemap"), + "The backend used for `~ultraplot.axes.GeoAxes`. Must be " + "either 'cartopy' or 'basemap'.", + ), + "geo.extent": ( + "globe", + _validate_belongs("globe", "auto"), + "If ``'globe'``, the extent of cartopy `~ultraplot.axes.GeoAxes` is always " + "global. If ``'auto'``, the extent is automatically adjusted based on " + "plotted content. Default is ``'globe'``.", + ), + "geo.round": ( + True, + _validate_bool, + "If ``True`` (the default), polar `~ultraplot.axes.GeoAxes` like ``'npstere'`` " + "and ``'spstere'`` are bounded with circles rather than squares.", + ), + # Graphs + "graph.draw_nodes": ( + True, + _validate_bool_or_iterable, + "If ``True`` draws the nodes for all the nodes, otherwise only the nodes that are in the iterable.", + ), + "graph.draw_edges": ( + True, + _validate_bool_or_iterable, + "If ``True`` draws the edges for all the edges, otherwise only the edges that are in the iterable.", + ), + "graph.draw_labels": ( + False, + _validate_bool_or_iterable, + "If ``True`` draws the labels for all the nodes, otherwise only the nodes that are in the iterable.", + ), + "graph.draw_grid": ( + False, + _validate_bool, + "If ``True`` draws the grid for all the edges, otherwise only the edges that are in the iterable.", + ), + "graph.aspect": ( + "equal", + _validate_belongs("equal", "auto"), + "The aspect ratio of the graph.", + ), + "graph.facecolor": ("none", _validate_color, "The facecolor of the graph."), + "graph.draw_spines": ( + False, + _validate_bool_or_iterable, + "If ``True`` draws the spines for all the edges, otherwise only the edges that are in the iterable.", + ), + "graph.rescale": ( + True, + _validate_bool, + "If ``True`` rescales the graph to fit the data.", + ), + # Gridlines + # NOTE: Here 'grid' and 'gridminor' or *not* aliases for native 'axes.grid' and + # invented 'axes.gridminor' because native 'axes.grid' controls both major *and* + # minor gridlines. Must handle it independently from these settings. + "grid": (True, _validate_bool, "Toggle major gridlines on and off."), + "grid.below": ( + GRIDBELOW, # like axes.axisbelow + _validate_belongs(False, "line", True), + "Alias for :rcraw:`axes.axisbelow`. If ``True``, draw gridlines below " + "everything. If ``True``, draw them above everything. If ``'line'``, " + "draw them above patches but below lines and markers.", + ), + "grid.checkoverlap": ( + True, + _validate_bool, + "Whether to have cartopy automatically check for and remove overlapping " + "`~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.dmslabels": ( + True, + _validate_bool, + "Whether to use degrees-minutes-seconds rather than decimals for " + "cartopy `~ultraplot.axes.GeoAxes` gridlines.", + ), + "grid.geolabels": ( + True, + _validate_bool, + "Whether to include the ``'geo'`` spine in cartopy >= 0.20 when otherwise " + "toggling left, right, bottom, or top `~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.inlinelabels": ( + False, + _validate_bool, + "Whether to add inline labels for cartopy `~ultraplot.axes.GeoAxes` gridlines.", + ), + "grid.labels": ( + False, + _validate_bool, + "Whether to add outer labels for `~ultraplot.axes.GeoAxes` gridlines.", + ), + "grid.labelcolor": ( + BLACK, + _validate_color, + "Font color for `~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.labelpad": ( + GRIDPAD, + _validate_pt, + "Padding between the map boundary and cartopy `~ultraplot.axes.GeoAxes` " + "gridline labels." + _addendum_pt, + ), + "grid.labelsize": ( + SMALLSIZE, + _validate_fontsize, + "Font size for `~ultraplot.axes.GeoAxes` gridline labels." + _addendum_font, + ), + "grid.labelweight": ( + "normal", + _validate_fontweight, + "Font weight for `~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.nsteps": ( + 250, + _validate_int, + "Number of points used to draw cartopy `~ultraplot.axes.GeoAxes` gridlines.", + ), + "grid.pad": (GRIDPAD, _validate_pt, "Alias for :rcraw:`grid.labelpad`."), + "grid.rotatelabels": ( + False, # False limits projections where labels are available + _validate_bool, + "Whether to rotate cartopy `~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.style": ( + "-", + _validate_linestyle, + "Major gridline style. Alias for :rcraw:`grid.linestyle`.", + ), + "grid.width": ( + LINEWIDTH, + _validate_pt, + "Major gridline width. Alias for :rcraw:`grid.linewidth`.", + ), + "grid.widthratio": ( + GRIDRATIO, + _validate_float, + "Ratio of minor gridline width to major gridline width.", + ), + # Minor gridlines + "gridminor": (False, _validate_bool, "Toggle minor gridlines on and off."), + "gridminor.alpha": (GRIDALPHA, _validate_float, "Minor gridline opacity."), + "gridminor.color": (BLACK, _validate_color, "Minor gridline color."), + "gridminor.linestyle": (GRIDSTYLE, _validate_linestyle, "Minor gridline style."), + "gridminor.linewidth": ( + GRIDRATIO * LINEWIDTH, + _validate_pt, + "Minor gridline width.", + ), + "gridminor.style": ( + GRIDSTYLE, + _validate_linestyle, + "Minor gridline style. Alias for :rcraw:`gridminor.linestyle`.", + ), + "gridminor.width": ( + GRIDRATIO * LINEWIDTH, + _validate_pt, + "Minor gridline width. Alias for :rcraw:`gridminor.linewidth`.", + ), + # Backend stuff + "inlineformat": ( + "retina", + _validate_belongs("svg", "pdf", "retina", "png", "jpeg"), + "The inline backend figure format. Valid formats include " + "``'svg'``, ``'pdf'``, ``'retina'``, ``'png'``, and ``jpeg``.", + ), + # Inner borders + "innerborders": ( + False, + _validate_bool, + "Toggles internal political border lines (e.g. states and provinces) " + "on and off.", + ), + "innerborders.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for internal political border lines", + ), + "innerborders.color": ( + BLACK, + _validate_color, + "Line color for internal political border lines.", + ), + "innerborders.linewidth": ( + LINEWIDTH, + _validate_pt, + "Line width for internal political border lines.", + ), + "innerborders.zorder": ( + ZLINES, + _validate_float, + "Z-order for internal political border lines.", + ), + # Axis label settings + "label.color": (BLACK, _validate_color, "Alias for :rcraw:`axes.labelcolor`."), + "label.pad": ( + LABELPAD, + _validate_pt, + "Alias for :rcraw:`axes.labelpad`." + _addendum_pt, + ), + "label.size": ( + SMALLSIZE, + _validate_fontsize, + "Alias for :rcraw:`axes.labelsize`." + _addendum_font, + ), + "label.weight": ( + "normal", + _validate_fontweight, + "Alias for :rcraw:`axes.labelweight`.", + ), + # Lake patches + "lakes": (False, _validate_bool, "Toggles lake patches on and off."), + "lakes.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for lake patches", + ), + "lakes.color": (WHITE, _validate_color, "Face color for lake patches."), + "lakes.zorder": (ZPATCHES, _validate_float, "Z-order for lake patches."), + "lakes.rasterized": ( + False, + _validate_bool, + "Toggles rasterization on or off for lake feature", + ), + # Land patches + "land": (False, _validate_bool, "Toggles land patches on and off."), + "land.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for land patches", + ), + "land.color": (BLACK, _validate_color, "Face color for land patches."), + "land.zorder": (ZPATCHES, _validate_float, "Z-order for land patches."), + "land.rasterized": ( + False, + _validate_bool, + "Toggles the rasterization of the land feature.", + ), + # Left subplot labels + "leftlabel.color": ( + BLACK, + _validate_color, + "Font color for row labels on the left-hand side.", + ), + "leftlabel.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and row labels on the left-hand side." + + _addendum_pt, + ), + "leftlabel.rotation": ( + "vertical", + _validate_rotation, + "Rotation for row labels on the left-hand side." + _addendum_rotation, + ), + "leftlabel.size": ( + LARGESIZE, + _validate_fontsize, + "Font size for row labels on the left-hand side." + _addendum_font, + ), + "lollipop.markersize": ( + 36, + _validate_float, + "Size of lollipops in the lollipop plot.", + ), + "lollipop.stemcolor": ( + BLACK, + _validate_color, + "Color of lollipop lines.", + ), + "lollipop.stemwidth": ( + LINEWIDTH, + _validate_pt, + "Width of the stem", + ), + "lollipop.stemlinestyle": ( + "-", + _validate_linestyle, + "Line style of lollipop lines.", + ), + "leftlabel.weight": ( + "bold", + _validate_fontweight, + "Font weight for row labels on the left-hand side.", + ), + # Meta settings + "margin": ( + MARGIN, + _validate_float, + "The fractional *x* and *y* axis data margins when limits are unset. " + "Alias for :rcraw:`axes.margin`.", + ), + "meta.edgecolor": ( + BLACK, + _validate_color, + "Color of axis spines, tick marks, tick labels, and labels.", + ), + "meta.color": ( + BLACK, + _validate_color, + "Color of axis spines, tick marks, tick labels, and labels. " + "Alias for :rcraw:`meta.edgecolor`.", + ), + "meta.linewidth": ( + LINEWIDTH, + _validate_pt, + "Thickness of axis spines and major tick lines.", + ), + "meta.width": ( + LINEWIDTH, + _validate_pt, + "Thickness of axis spines and major tick lines. " + "Alias for :rcraw:`meta.linewidth`.", + ), + # For negative positive patches + "negcolor": ( + "blue7", + _validate_color, + "Color for negative bars and shaded areas when using ``negpos=True``. " + "See also :rcraw:`poscolor`.", + ), + "poscolor": ( + "red7", + _validate_color, + "Color for positive bars and shaded areas when using ``negpos=True``. " + "See also :rcraw:`negcolor`.", + ), + # Ocean patches + "ocean": (False, _validate_bool, "Toggles ocean patches on and off."), + "ocean.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for ocean patches", + ), + "ocean.color": (WHITE, _validate_color, "Face color for ocean patches."), + "ocean.zorder": (ZPATCHES, _validate_float, "Z-order for ocean patches."), + "ocean.rasterized": ( + False, + _validate_bool, + "Turns rasterization on or off for the oceans feature for GeoAxes.", + ), + # Geographic resolution + "reso": ( + "lo", + _validate_belongs("lo", "med", "hi", "x-hi", "xx-hi"), + "Resolution for `~ultraplot.axes.GeoAxes` geographic features. " + "Must be one of ``'lo'``, ``'med'``, ``'hi'``, ``'x-hi'``, or ``'xx-hi'``.", + ), + # Right subplot labels + "rightlabel.color": ( + BLACK, + _validate_color, + "Font color for row labels on the right-hand side.", + ), + "rightlabel.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and row labels on the right-hand side." + + _addendum_pt, + ), + "rightlabel.rotation": ( + "vertical", + _validate_rotation, + "Rotation for row labels on the right-hand side." + _addendum_rotation, + ), + "rightlabel.size": ( + LARGESIZE, + _validate_fontsize, + "Font size for row labels on the right-hand side." + _addendum_font, + ), + "rightlabel.weight": ( + "bold", + _validate_fontweight, + "Font weight for row labels on the right-hand side.", + ), + # River lines + "rivers": (False, _validate_bool, "Toggles river lines on and off."), + "rivers.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for river lines.", + ), + "rivers.color": (BLACK, _validate_color, "Line color for river lines."), + "rivers.linewidth": (LINEWIDTH, _validate_pt, "Line width for river lines."), + "rivers.zorder": (ZLINES, _validate_float, "Z-order for river lines."), + "rivers.rasterized": ( + False, + _validate_bool, + "Toggles rasterization on or off for rivers feature for GeoAxes.", + ), + # Circlize settings + "chord.start": ( + 0.0, + _validate_float, + "Start angle for chord diagrams.", + ), + "chord.end": ( + 360.0, + _validate_float, + "End angle for chord diagrams.", + ), + "chord.space": ( + 0.0, + _validate_float_or_iterable, + "Inter-sector spacing for chord diagrams.", + ), + "chord.endspace": ( + True, + _validate_bool, + "Whether to add an ending space gap for chord diagrams.", + ), + "chord.r_lim": ( + (97.0, 100.0), + _validate_tuple_float_2, + "Radial limits for chord diagrams.", + ), + "chord.ticks_interval": ( + None, + _validate_or_none(_validate_int), + "Tick interval for chord diagrams.", + ), + "chord.order": ( + None, + _validate_or_none(_validate_string_or_iterable), + "Ordering of sectors for chord diagrams.", + ), + "radar.r_lim": ( + (0.0, 100.0), + _validate_tuple_float_2, + "Radial limits for radar charts.", + ), + "radar.vmin": ( + 0.0, + _validate_float, + "Minimum value for radar charts.", + ), + "radar.vmax": ( + 100.0, + _validate_float, + "Maximum value for radar charts.", + ), + "radar.fill": ( + True, + _validate_bool, + "Whether to fill radar chart polygons.", + ), + "radar.marker_size": ( + 0, + _validate_int, + "Marker size for radar charts.", + ), + "radar.bg_color": ( + "#eeeeee80", + _validate_or_none(_validate_color), + "Background color for radar charts.", + ), + "radar.circular": ( + False, + _validate_bool, + "Whether to use circular radar charts.", + ), + "radar.show_grid_label": ( + True, + _validate_bool, + "Whether to show grid labels on radar charts.", + ), + "radar.grid_interval_ratio": ( + 0.2, + _validate_or_none(_validate_float), + "Grid interval ratio for radar charts.", + ), + "phylogeny.start": ( + 0.0, + _validate_float, + "Start angle for phylogeny plots.", + ), + "phylogeny.end": ( + 360.0, + _validate_float, + "End angle for phylogeny plots.", + ), + "phylogeny.r_lim": ( + (50.0, 100.0), + _validate_tuple_float_2, + "Radial limits for phylogeny plots.", + ), + "phylogeny.format": ( + "newick", + _validate_string, + "Input format for phylogeny plots.", + ), + "phylogeny.outer": ( + True, + _validate_bool, + "Whether to place phylogeny leaves on the outer edge.", + ), + "phylogeny.align_leaf_label": ( + True, + _validate_bool, + "Whether to align phylogeny leaf labels.", + ), + "phylogeny.ignore_branch_length": ( + False, + _validate_bool, + "Whether to ignore branch lengths in phylogeny plots.", + ), + "phylogeny.leaf_label_size": ( + None, + _validate_or_none(_validate_float), + "Leaf label font size for phylogeny plots.", + ), + "phylogeny.leaf_label_rmargin": ( + 2.0, + _validate_float, + "Radial margin for phylogeny leaf labels.", + ), + "phylogeny.reverse": ( + False, + _validate_bool, + "Whether to reverse phylogeny orientation.", + ), + "phylogeny.ladderize": ( + False, + _validate_bool, + "Whether to ladderize phylogeny branches.", + ), + # Sankey diagrams + "sankey.align": ( + "center", + _validate_belongs("center", "left", "right", "justify"), + "Horizontal alignment of nodes.", + ), + "sankey.connect": ( + (0, 0), + _validate_tuple_int_2, + "Connection path for Sankey diagram.", + ), + "sankey.flow_labels": ( + False, + _validate_bool, + "Whether to draw flow labels.", + ), + "sankey.flow_label_pos": ( + 0.5, + _validate_float, + "Position of flow labels along the flow.", + ), + "sankey.flow_sort": ( + True, + _validate_bool, + "Whether to sort flows.", + ), + "sankey.node_labels": ( + True, + _validate_bool, + "Whether to draw node labels.", + ), + "sankey.node_label_offset": ( + 0.01, + _validate_float, + "Offset for node labels.", + ), + "sankey.node_label_outside": ( + "auto", + _validate_bool_or_string, + "Position of node labels relative to the node.", + ), + "sankey.other_label": ( + "Other", + _validate_string, + "Label for 'other' category in Sankey diagram.", + ), + "sankey.pathlabel": ( + "", + _validate_string, + "Label for the patch.", + ), + "sankey.pathlengths": ( + 0.25, + _validate_float, + "Path lengths for Sankey diagram.", + ), + "sankey.rotation": ( + 0.0, + _validate_float, + "Rotation of the Sankey diagram.", + ), + "sankey.trunklength": ( + 1.0, + _validate_float, + "Trunk length for Sankey diagram.", + ), + # Subplots settings + "subplots.align": ( + False, + _validate_bool, + "Whether to align axis labels during draw. See `aligning labels " + "`__.", # noqa: E501 + ), + "subplots.equalspace": ( + False, + _validate_bool, + "Whether to make the tight layout algorithm assign the same space for " + "every row and the same space for every column.", + ), + "subplots.groupspace": ( + True, + _validate_bool, + "Whether to make the tight layout algorithm consider space between only " + 'adjacent subplot "groups" rather than every subplot in the row or column.', + ), + "subplots.innerpad": ( + 1, + _validate_em, + "Padding between adjacent subplots." + _addendum_em, + ), + "subplots.outerpad": ( + 0.5, + _validate_em, + "Padding around figure edge." + _addendum_em, + ), + "subplots.panelpad": ( + 0.5, + _validate_em, + "Padding between subplots and panels, and between stacked panels." + + _addendum_em, + ), + "subplots.panelwidth": (0.5, _validate_in, "Width of side panels." + _addendum_in), + "subplots.refwidth": ( + 2.5, + _validate_in, + "Default width of the reference subplot." + _addendum_in, + ), + "subplots.share": ( + "auto", + _validate_belongs( + 0, 1, 2, 3, 4, False, "labels", "limits", True, "all", "auto" + ), + "The axis sharing level, one of ``0``, ``1``, ``2``, or ``3``, or the " + "more intuitive aliases ``False``, ``'labels'``, ``'limits'``, ``True``, " + "or ``'auto'``. See `~ultraplot.figure.Figure` for details.", + ), + "subplots.span": ( + True, + _validate_bool, + "Toggles spanning axis labels. See `~ultraplot.ui.subplots` for details.", + ), + "subplots.tight": ( + True, + _validate_bool, + "Whether to auto-adjust the subplot spaces and figure margins.", + ), + "subplots.pixelsnap": ( + False, + _validate_bool, + "Whether to snap subplot bounds to the renderer pixel grid during draw.", + ), + # Super title settings + "suptitle.color": (BLACK, _validate_color, "Figure title color."), + "suptitle.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and the figure super title." + _addendum_pt, + ), + "suptitle.size": ( + LARGESIZE, + _validate_fontsize, + "Figure title font size." + _addendum_font, + ), + "suptitle.weight": ("bold", _validate_fontweight, "Figure title font weight."), + # Tick settings + "tick.color": (BLACK, _validate_color, "Major and minor tick color."), + "tick.dir": ( + TICKDIR, + _validate_belongs("in", "out", "inout"), + "Major and minor tick direction. Must be one of " + "``'out'``, ``'in'``, or ``'inout'``.", + ), + "tick.labelcolor": (BLACK, _validate_color, "Axis tick label color."), + "tick.labelpad": ( + TICKPAD, + _validate_pt, + "Padding between ticks and tick labels." + _addendum_pt, + ), + "tick.labelsize": ( + SMALLSIZE, + _validate_fontsize, + "Axis tick label font size." + _addendum_font, + ), + "tick.labelweight": ( + "normal", + _validate_fontweight, + "Axis tick label font weight.", + ), + "tick.len": (TICKLEN, _validate_pt, "Length of major ticks in points."), + "tick.lenratio": ( + TICKLENRATIO, + _validate_float, + "Ratio of minor tickline length to major tickline length.", + ), + "tick.linewidth": (LINEWIDTH, _validate_pt, "Major tickline width."), + "tick.minor": ( + TICKMINOR, + _validate_bool, + "Toggles minor ticks on and off.", + ), + "tick.pad": (TICKPAD, _validate_pt, "Alias for :rcraw:`tick.labelpad`."), + "tick.width": ( + LINEWIDTH, + _validate_pt, + "Major tickline width. Alias for :rcraw:`tick.linewidth`.", + ), + "tick.widthratio": ( + TICKWIDTHRATIO, + _validate_float, + "Ratio of minor tickline width to major tickline width.", + ), + # Title settings + "title.above": ( + True, + _validate_belongs(False, True, "panels"), + "Whether to move outer titles and a-b-c labels above panels, colorbars, or " + "legends that are above the axes. If the string 'panels' then text is only " + "redirected above axes panels. Otherwise should be boolean.", + ), + "title.border": ( + True, + _validate_bool, + "Whether to draw a white border around titles " + "when :rcraw:`title.loc` is inside the axes.", + ), + "title.borderwidth": (1.5, _validate_pt, "Width of the border around titles."), + "title.bbox": ( + False, + _validate_bool, + "Whether to draw semi-transparent bounding boxes around titles " + "when :rcraw:`title.loc` is inside the axes.", + ), + "title.bboxcolor": (WHITE, _validate_color, "Axes title bounding box color."), + "title.bboxstyle": ("square", _validate_boxstyle, "Axes title bounding box style."), + "title.bboxalpha": (0.5, _validate_float, "Axes title bounding box opacity."), + "title.bboxpad": ( + None, + _validate_or_none(_validate_pt), + "Padding for the title bounding box. By default this is scaled " + "to make the box flush against the axes edge." + _addendum_pt, + ), + "title.color": ( + BLACK, + _validate_color, + "Axes title color. Alias for :rcraw:`axes.titlecolor`.", + ), + "title.loc": ( + "center", + _validate_belongs(*TEXT_LOCS), + "Title position. For options see the :ref:`location table `.", + ), + "title.pad": ( + TITLEPAD, + _validate_pt, + "Padding between the axes edge and the inner and outer titles and " + "a-b-c labels. Alias for :rcraw:`axes.titlepad`." + _addendum_pt, + ), + "title.size": ( + LARGESIZE, + _validate_fontsize, + "Axes title font size. Alias for :rcraw:`axes.titlesize`." + _addendum_font, + ), + "title.weight": ( + "normal", + _validate_fontweight, + "Axes title font weight. Alias for :rcraw:`axes.titleweight`.", + ), + # Top subplot label settings + "toplabel.color": ( + BLACK, + _validate_color, + "Font color for column labels on the top of the figure.", + ), + "toplabel.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and column labels on the top of the figure." + + _addendum_pt, + ), + "toplabel.rotation": ( + "horizontal", + _validate_rotation, + "Rotation for column labels at the top of the figure." + _addendum_rotation, + ), + "toplabel.size": ( + LARGESIZE, + _validate_fontsize, + "Font size for column labels on the top of the figure." + _addendum_font, + ), + "toplabel.weight": ( + "bold", + _validate_fontweight, + "Font weight for column labels on the top of the figure.", + ), + # Unit formatting + "unitformat": ( + "L", + _validate_string, + "The format string used to format `pint.Quantity` default unit labels " + "using ``format(units, unitformat)``. See also :rcraw:`autoformat`.", + ), + "ultraplot.check_for_latest_version": ( + False, + _validate_bool, + "Whether to check for the latest version of UltraPlot on PyPI when importing", + ), + "ultraplot.eager_import": ( + False, + _validate_bool, + "Whether to import the full public API during setup instead of lazily.", + ), + } diff --git a/ultraplot/internals/rc/registry.py b/ultraplot/internals/rc/registry.py new file mode 100644 index 000000000..196f009ef --- /dev/null +++ b/ultraplot/internals/rc/registry.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +""" +Utilities for composing rc tables from modular providers. +""" + +from __future__ import annotations + +from typing import Any, Callable + +RcValidator = Callable[[Any], Any] +RcEntry = tuple[Any, RcValidator, str] +RcTable = dict[str, RcEntry] + + +def merge_rc_tables(*tables: RcTable) -> RcTable: + """ + Merge rc tables and fail on duplicate keys. + """ + merged: RcTable = {} + for table in tables: + overlap = merged.keys() & table.keys() + if overlap: + overlap_str = ", ".join(sorted(overlap)) + raise ValueError(f"Duplicate rc keys while merging tables: {overlap_str}") + merged.update(table) + return merged diff --git a/ultraplot/internals/rcsetup.py b/ultraplot/internals/rcsetup.py index 3e7e3e7d5..4fb2197a5 100644 --- a/ultraplot/internals/rcsetup.py +++ b/ultraplot/internals/rcsetup.py @@ -26,7 +26,7 @@ ic, # noqa: F401 warnings, ) -from .rc import build_plot_type_rc_table +from .rc import build_core_rc_table, build_plot_type_rc_table, merge_rc_tables from .versions import _version_mpl # Regex for "probable" unregistered named colors. Try to retain warning message for @@ -972,1300 +972,16 @@ def _validator_accepts(validator, value): " Must be a :ref:`relative font size ` or unit string " "interpreted by `~ultraplot.utils.units`. Numeric units are points." ) -_rc_ultraplot_table = { - # Plot-type settings are grouped in internals/rc/plot_types.py - **build_plot_type_rc_table( +_rc_ultraplot_table = merge_rc_tables( + build_plot_type_rc_table( validate_bool=_validate_bool, validate_color=_validate_color, validate_float=_validate_float, validate_int=_validate_int, validate_string=_validate_string, ), - "external.shrink": ( - 0.9, - _validate_float, - "Default shrink factor for external axes containers.", - ), - # Stylesheet - "style": ( - None, - _validate_or_none(_validate_string), - "The default matplotlib `stylesheet " - "`__ " # noqa: E501 - "name. If ``None``, a custom ultraplot style is used. " - "If ``'default'``, the default matplotlib style is used.", - ), - # A-b-c labels - "abc": ( - False, - _validate_abc, - "If ``False`` then a-b-c labels are disabled. If ``True`` the default label " - "style `a` is used. If string this indicates the style and must contain the " - "character `a` or ``A``, for example ``'a.'`` or ``'(A)'``.", - ), - "abc.border": ( - True, - _validate_bool, - "Whether to draw a white border around a-b-c labels " - "when :rcraw:`abc.loc` is inside the axes.", - ), - "abc.borderwidth": ( - 1.5, - _validate_pt, - "Width of the white border around a-b-c labels.", - ), - "text.borderstyle": ( - "bevel", - _validate_joinstyle, - "Join style for text border strokes. Must be one of " - "``'miter'``, ``'round'``, or ``'bevel'``.", - ), - "text.curved.upright": ( - True, - _validate_bool, - "Whether curved text is flipped to remain upright by default.", - ), - "text.curved.ellipsis": ( - False, - _validate_bool, - "Whether to show ellipses when curved text exceeds path length.", - ), - "text.curved.avoid_overlap": ( - True, - _validate_bool, - "Whether curved text hides overlapping glyphs by default.", - ), - "text.curved.overlap_tol": ( - 0.1, - _validate_float, - "Overlap threshold used when hiding curved-text glyphs.", - ), - "text.curved.curvature_pad": ( - 2.0, - _validate_float, - "Extra curved-text glyph spacing per radian of local curvature.", - ), - "text.curved.min_advance": ( - 1.0, - _validate_float, - "Minimum extra curved-text glyph spacing in pixels.", - ), - "abc.bbox": ( - False, - _validate_bool, - "Whether to draw semi-transparent bounding boxes around a-b-c labels " - "when :rcraw:`abc.loc` is inside the axes.", - ), - "abc.bboxcolor": (WHITE, _validate_color, "a-b-c label bounding box color."), - "abc.bboxstyle": ("square", _validate_boxstyle, "a-b-c label bounding box style."), - "abc.bboxalpha": (0.5, _validate_float, "a-b-c label bounding box opacity."), - "abc.bboxpad": ( - None, - _validate_or_none(_validate_pt), - "Padding for the a-b-c label bounding box. By default this is scaled " - "to make the box flush against the subplot edge." + _addendum_pt, - ), - "abc.color": (BLACK, _validate_color, "a-b-c label color."), - "abc.loc": ( - "left", # left side above the axes - _validate_belongs(*TEXT_LOCS), - "a-b-c label position. " - "For options see the :ref:`location table `.", - ), - "abc.size": ( - LARGESIZE, - _validate_fontsize, - "a-b-c label font size." + _addendum_font, - ), - "abc.titlepad": ( - LABELPAD, - _validate_pt, - "Padding separating the title and a-b-c label when in the same location." - + _addendum_pt, - ), - "abc.weight": ("bold", _validate_fontweight, "a-b-c label font weight."), - # Autoformatting - "autoformat": ( - True, - _validate_bool, - "Whether to automatically apply labels from `pandas.Series`, " - "`pandas.DataFrame`, and `xarray.DataArray` objects passed to " - "plotting functions. See also :rcraw:`unitformat`.", - ), - # Axes additions - "axes.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity of the background axes patch.", - ), - "axes.inbounds": ( - True, - _validate_bool, - "Whether to exclude out-of-bounds data when determining the default *y* (*x*) " - "axis limits and the *x* (*y*) axis limits have been locked.", - ), - "axes.margin": ( - MARGIN, - _validate_float, - "The fractional *x* and *y* axis margins when limits are unset.", - ), - "bar.bar_labels": ( - False, - _validate_bool, - "Add value of the bars to the bar labels", - ), - # Country borders - "borders": (False, _validate_bool, "Toggles country border lines on and off."), - "borders.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for country border lines.", - ), - "borders.color": (BLACK, _validate_color, "Line color for country border lines."), - "borders.linewidth": ( - LINEWIDTH, - _validate_pt, - "Line width for country border lines.", - ), - "borders.zorder": (ZLINES, _validate_float, "Z-order for country border lines."), - "borders.rasterized": ( - False, - _validate_bool, - "Toggles rasterization on or off for border feature in GeoAxes.", - ), - # Bottom subplot labels - "bottomlabel.color": ( - BLACK, - _validate_color, - "Font color for column labels on the bottom of the figure.", - ), - "bottomlabel.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and column labels on the bottom of the figure." - + _addendum_pt, - ), - "bottomlabel.rotation": ( - "horizontal", - _validate_rotation, - "Rotation for column labels at the bottom of the figure." + _addendum_rotation, - ), - "bottomlabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for column labels on the bottom of the figure." + _addendum_font, - ), - "bottomlabel.weight": ( - "bold", - _validate_fontweight, - "Font weight for column labels on the bottom of the figure.", - ), - "cftime.time_unit": ( - "days since 2000-01-01", - _validate_string, - "Time unit for non-Gregorian calendars.", - ), - "cftime.resolution": ( - "DAILY", - _validate_cftime_resolution, - "Default time resolution for non-Gregorian calendars.", - ), - "cftime.time_resolution_format": ( - { - "SECONDLY": "%S", - "MINUTELY": "%M", - "HOURLY": "%H", - "DAILY": "%d", - "MONTHLY": "%m", - "YEARLY": "%Y", - }, - _validate_cftime_resolution_format, - "Dict used for formatting non-Gregorian calendars.", - ), - "cftime.max_display_ticks": ( - 7, - _validate_int, - "Number of ticks to display for cftime units.", - ), - # Coastlines - "coast": (False, _validate_bool, "Toggles coastline lines on and off."), - "coast.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for coast lines", - ), - "coast.color": (BLACK, _validate_color, "Line color for coast lines."), - "coast.linewidth": (LINEWIDTH, _validate_pt, "Line width for coast lines."), - "coast.zorder": (ZLINES, _validate_float, "Z-order for coast lines."), - "coast.rasterized": ( - False, - _validate_bool, - "Toggles the rasterization of the coastlines feature for GeoAxes.", - ), - # Colorbars - "colorbar.center_levels": ( - False, - _validate_bool, - "Center the ticks in the center of each segment.", - ), - "colorbar.edgecolor": ( - BLACK, - _validate_color, - "Color for the inset colorbar frame edge.", - ), - "colorbar.extend": ( - 1.3, - _validate_em, - 'Length of rectangular or triangular "extensions" for panel colorbars.' - + _addendum_em, - ), - "colorbar.outline": ( - True, - _validate_bool, - "Whether to draw a frame around the colorbar.", - ), - "colorbar.labelrotation": ( - "auto", - _validate_float_or_auto, - "Rotation of colorbar labels.", - ), - "colorbar.fancybox": ( - False, - _validate_bool, - 'Whether to use a "fancy" round bounding box for inset colorbar frames.', - ), - "colorbar.framealpha": ( - FRAMEALPHA, - _validate_float, - "Opacity for inset colorbar frames.", - ), - "colorbar.facecolor": ( - WHITE, - _validate_color, - "Color for the inset colorbar frame.", - ), - "colorbar.frameon": ( - True, - _validate_bool, - "Whether to draw a frame behind inset colorbars.", - ), - "colorbar.grid": ( - False, - _validate_bool, - "Whether to draw borders between each level of the colorbar.", - ), - "colorbar.insetextend": ( - 0.9, - _validate_em, - 'Length of rectangular or triangular "extensions" for inset colorbars.' - + _addendum_em, - ), - "colorbar.insetlength": ( - 8, - _validate_em, - "Length of inset colorbars." + _addendum_em, - ), - "colorbar.insetpad": ( - 0.7, - _validate_em, - "Padding between axes edge and inset colorbars." + _addendum_em, - ), - "colorbar.insetwidth": ( - 1.2, - _validate_em, - "Width of inset colorbars." + _addendum_em, - ), - "colorbar.length": (1, _validate_em, "Length of outer colorbars."), - "colorbar.loc": ( - "right", - _validate_belongs(*COLORBAR_LOCS), - "Inset colorbar location. " - "For options see the :ref:`location table `.", - ), - "colorbar.width": (0.2, _validate_in, "Width of outer colorbars." + _addendum_in), - "colorbar.rasterized": ( - False, - _validate_bool, - "Whether to use rasterization for colorbar solids.", - ), - "colorbar.shadow": ( - False, - _validate_bool, - "Whether to add a shadow underneath inset colorbar frames.", - ), - # Color cycle additions - "cycle": ( - CYCLE, - _validate_cmap("discrete", cycle=True), - "Name of the color cycle assigned to :rcraw:`axes.prop_cycle`.", - ), - # Colormap additions - "cmap": ( - CMAPSEQ, - _validate_cmap("continuous"), - "Alias for :rcraw:`cmap.sequential` and :rcraw:`image.cmap`.", - ), - "cmap.autodiverging": ( - True, - _validate_bool, - "Whether to automatically apply a diverging colormap and " - "normalizer based on the data.", - ), - "cmap.qualitative": ( - CMAPCAT, - _validate_cmap("discrete"), - "Default colormap for qualitative datasets.", - ), - "cmap.cyclic": ( - CMAPCYC, - _validate_cmap("continuous"), - "Default colormap for cyclic datasets.", - ), - "cmap.discrete": ( - None, - _validate_or_none(_validate_bool), - "If ``True``, `~ultraplot.colors.DiscreteNorm` is used for every colormap plot. " - "If ``False``, it is never used. If ``None``, it is used for all plot types " - "except `imshow`, `matshow`, `spy`, `hexbin`, and `hist2d`.", - ), - "cmap.diverging": ( - CMAPDIV, - _validate_cmap("continuous"), - "Default colormap for diverging datasets.", - ), - "cmap.inbounds": ( - True, - _validate_bool, - "If ``True`` and the *x* and *y* axis limits are fixed, only in-bounds data " - "is considered when determining the default colormap `vmin` and `vmax`.", - ), - "cmap.levels": ( - 11, - _validate_int, - "Default number of `~ultraplot.colors.DiscreteNorm` levels for plotting " - "commands that use colormaps.", - ), - "cmap.listedthresh": ( - 64, - _validate_int, - "Native `~matplotlib.colors.ListedColormap`\\ s with more colors than " - "this are converted to :class:`~ultraplot.colors.ContinuousColormap` rather than " - ":class:`~ultraplot.colors.DiscreteColormap`. This helps translate continuous " - "colormaps from external projects.", - ), - "cmap.lut": ( - 256, - _validate_int, - "Number of colors in the colormap lookup table. " - "Alias for :rcraw:`image.lut`.", - ), - "cmap.robust": ( - False, - _validate_bool, - "If ``True``, the default colormap `vmin` and `vmax` are chosen using the " - "2nd to 98th percentiles rather than the minimum and maximum.", - ), - "cmap.sequential": ( - CMAPSEQ, - _validate_cmap("continuous"), - "Default colormap for sequential datasets. Alias for :rcraw:`image.cmap`.", - ), - # Special setting - "edgefix": ( - True, - _validate_bool, - 'Whether to fix issues with "white lines" appearing between patches ' - "in saved vector graphics and with vector graphic backends. Applies " - "to colorbar levels and bar, area, pcolor, and contour plots.", - ), - # Font settings - "font.name": (FONTNAME, _validate_fontname, "Alias for :rcraw:`font.family`."), - "font.small": (SMALLSIZE, _validate_fontsize, "Alias for :rcraw:`font.smallsize`."), - "font.smallsize": ( - SMALLSIZE, - _validate_fontsize, - "Meta setting that changes the label-like sizes ``axes.labelsize``, " - "``legend.fontsize``, ``tick.labelsize``, and ``grid.labelsize``. Default is " - "``'medium'`` (equivalent to :rcraw:`font.size`)." + _addendum_font, - ), - "font.large": (LARGESIZE, _validate_fontsize, "Alias for :rcraw:`font.largesize`."), - "font.largesize": ( - LARGESIZE, - _validate_fontsize, - "Meta setting that changes the title-like sizes ``abc.size``, ``title.size``, " - "``suptitle.size``, ``leftlabel.size``, ``rightlabel.size``, etc. Default is " - "``'med-large'`` (i.e. 1.1 times :rcraw:`font.size`)." + _addendum_font, - ), - # Formatter settings - "formatter.timerotation": ( - "vertical", - _validate_rotation, - "Rotation for *x* axis datetime tick labels." + _addendum_rotation, - ), - "formatter.zerotrim": ( - True, - _validate_bool, - "Whether to trim trailing decimal zeros on tick labels.", - ), - "formatter.log": ( - False, - _validate_bool, - "Whether to use log formatting (e.g., $10^{4}$) for " - "logarithmically scaled axis tick labels.", - ), - "formatter.limits": ( - [-5, 6], # must be list or else validated - _validate["axes.formatter.limits"], - "Alias for :rcraw:`axes.formatter.limits`.", - ), - "formatter.min_exponent": ( - 0, - _validate["axes.formatter.min_exponent"], - "Alias for :rcraw:`axes.formatter.min_exponent`.", - ), - "formatter.offset_threshold": ( - 4, - _validate["axes.formatter.offset_threshold"], - "Alias for :rcraw:`axes.formatter.offset_threshold`.", - ), - "formatter.use_locale": ( - False, - _validate_bool, - "Alias for :rcraw:`axes.formatter.use_locale`.", - ), - "formatter.use_mathtext": ( - MATHTEXT, - _validate_bool, - "Alias for :rcraw:`axes.formatter.use_mathtext`.", - ), - "formatter.use_offset": ( - True, - _validate_bool, - "Alias for :rcraw:`axes.formatter.useOffset`.", - ), - # Geographic axes settings - "geo.backend": ( - "cartopy", - _validate_belongs("cartopy", "basemap"), - "The backend used for `~ultraplot.axes.GeoAxes`. Must be " - "either 'cartopy' or 'basemap'.", - ), - "geo.extent": ( - "globe", - _validate_belongs("globe", "auto"), - "If ``'globe'``, the extent of cartopy `~ultraplot.axes.GeoAxes` is always " - "global. If ``'auto'``, the extent is automatically adjusted based on " - "plotted content. Default is ``'globe'``.", - ), - "geo.round": ( - True, - _validate_bool, - "If ``True`` (the default), polar `~ultraplot.axes.GeoAxes` like ``'npstere'`` " - "and ``'spstere'`` are bounded with circles rather than squares.", - ), - # Graphs - "graph.draw_nodes": ( - True, - _validate_bool_or_iterable, - "If ``True`` draws the nodes for all the nodes, otherwise only the nodes that are in the iterable.", - ), - "graph.draw_edges": ( - True, - _validate_bool_or_iterable, - "If ``True`` draws the edges for all the edges, otherwise only the edges that are in the iterable.", - ), - "graph.draw_labels": ( - False, - _validate_bool_or_iterable, - "If ``True`` draws the labels for all the nodes, otherwise only the nodes that are in the iterable.", - ), - "graph.draw_grid": ( - False, - _validate_bool, - "If ``True`` draws the grid for all the edges, otherwise only the edges that are in the iterable.", - ), - "graph.aspect": ( - "equal", - _validate_belongs("equal", "auto"), - "The aspect ratio of the graph.", - ), - "graph.facecolor": ("none", _validate_color, "The facecolor of the graph."), - "graph.draw_spines": ( - False, - _validate_bool_or_iterable, - "If ``True`` draws the spines for all the edges, otherwise only the edges that are in the iterable.", - ), - "graph.rescale": ( - True, - _validate_bool, - "If ``True`` rescales the graph to fit the data.", - ), - # Gridlines - # NOTE: Here 'grid' and 'gridminor' or *not* aliases for native 'axes.grid' and - # invented 'axes.gridminor' because native 'axes.grid' controls both major *and* - # minor gridlines. Must handle it independently from these settings. - "grid": (True, _validate_bool, "Toggle major gridlines on and off."), - "grid.below": ( - GRIDBELOW, # like axes.axisbelow - _validate_belongs(False, "line", True), - "Alias for :rcraw:`axes.axisbelow`. If ``True``, draw gridlines below " - "everything. If ``True``, draw them above everything. If ``'line'``, " - "draw them above patches but below lines and markers.", - ), - "grid.checkoverlap": ( - True, - _validate_bool, - "Whether to have cartopy automatically check for and remove overlapping " - "`~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.dmslabels": ( - True, - _validate_bool, - "Whether to use degrees-minutes-seconds rather than decimals for " - "cartopy `~ultraplot.axes.GeoAxes` gridlines.", - ), - "grid.geolabels": ( - True, - _validate_bool, - "Whether to include the ``'geo'`` spine in cartopy >= 0.20 when otherwise " - "toggling left, right, bottom, or top `~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.inlinelabels": ( - False, - _validate_bool, - "Whether to add inline labels for cartopy `~ultraplot.axes.GeoAxes` gridlines.", - ), - "grid.labels": ( - False, - _validate_bool, - "Whether to add outer labels for `~ultraplot.axes.GeoAxes` gridlines.", - ), - "grid.labelcolor": ( - BLACK, - _validate_color, - "Font color for `~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.labelpad": ( - GRIDPAD, - _validate_pt, - "Padding between the map boundary and cartopy `~ultraplot.axes.GeoAxes` " - "gridline labels." + _addendum_pt, - ), - "grid.labelsize": ( - SMALLSIZE, - _validate_fontsize, - "Font size for `~ultraplot.axes.GeoAxes` gridline labels." + _addendum_font, - ), - "grid.labelweight": ( - "normal", - _validate_fontweight, - "Font weight for `~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.nsteps": ( - 250, - _validate_int, - "Number of points used to draw cartopy `~ultraplot.axes.GeoAxes` gridlines.", - ), - "grid.pad": (GRIDPAD, _validate_pt, "Alias for :rcraw:`grid.labelpad`."), - "grid.rotatelabels": ( - False, # False limits projections where labels are available - _validate_bool, - "Whether to rotate cartopy `~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.style": ( - "-", - _validate_linestyle, - "Major gridline style. Alias for :rcraw:`grid.linestyle`.", - ), - "grid.width": ( - LINEWIDTH, - _validate_pt, - "Major gridline width. Alias for :rcraw:`grid.linewidth`.", - ), - "grid.widthratio": ( - GRIDRATIO, - _validate_float, - "Ratio of minor gridline width to major gridline width.", - ), - # Minor gridlines - "gridminor": (False, _validate_bool, "Toggle minor gridlines on and off."), - "gridminor.alpha": (GRIDALPHA, _validate_float, "Minor gridline opacity."), - "gridminor.color": (BLACK, _validate_color, "Minor gridline color."), - "gridminor.linestyle": (GRIDSTYLE, _validate_linestyle, "Minor gridline style."), - "gridminor.linewidth": ( - GRIDRATIO * LINEWIDTH, - _validate_pt, - "Minor gridline width.", - ), - "gridminor.style": ( - GRIDSTYLE, - _validate_linestyle, - "Minor gridline style. Alias for :rcraw:`gridminor.linestyle`.", - ), - "gridminor.width": ( - GRIDRATIO * LINEWIDTH, - _validate_pt, - "Minor gridline width. Alias for :rcraw:`gridminor.linewidth`.", - ), - # Backend stuff - "inlineformat": ( - "retina", - _validate_belongs("svg", "pdf", "retina", "png", "jpeg"), - "The inline backend figure format. Valid formats include " - "``'svg'``, ``'pdf'``, ``'retina'``, ``'png'``, and ``jpeg``.", - ), - # Inner borders - "innerborders": ( - False, - _validate_bool, - "Toggles internal political border lines (e.g. states and provinces) " - "on and off.", - ), - "innerborders.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for internal political border lines", - ), - "innerborders.color": ( - BLACK, - _validate_color, - "Line color for internal political border lines.", - ), - "innerborders.linewidth": ( - LINEWIDTH, - _validate_pt, - "Line width for internal political border lines.", - ), - "innerborders.zorder": ( - ZLINES, - _validate_float, - "Z-order for internal political border lines.", - ), - # Axis label settings - "label.color": (BLACK, _validate_color, "Alias for :rcraw:`axes.labelcolor`."), - "label.pad": ( - LABELPAD, - _validate_pt, - "Alias for :rcraw:`axes.labelpad`." + _addendum_pt, - ), - "label.size": ( - SMALLSIZE, - _validate_fontsize, - "Alias for :rcraw:`axes.labelsize`." + _addendum_font, - ), - "label.weight": ( - "normal", - _validate_fontweight, - "Alias for :rcraw:`axes.labelweight`.", - ), - # Lake patches - "lakes": (False, _validate_bool, "Toggles lake patches on and off."), - "lakes.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for lake patches", - ), - "lakes.color": (WHITE, _validate_color, "Face color for lake patches."), - "lakes.zorder": (ZPATCHES, _validate_float, "Z-order for lake patches."), - "lakes.rasterized": ( - False, - _validate_bool, - "Toggles rasterization on or off for lake feature", - ), - # Land patches - "land": (False, _validate_bool, "Toggles land patches on and off."), - "land.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for land patches", - ), - "land.color": (BLACK, _validate_color, "Face color for land patches."), - "land.zorder": (ZPATCHES, _validate_float, "Z-order for land patches."), - "land.rasterized": ( - False, - _validate_bool, - "Toggles the rasterization of the land feature.", - ), - # Left subplot labels - "leftlabel.color": ( - BLACK, - _validate_color, - "Font color for row labels on the left-hand side.", - ), - "leftlabel.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and row labels on the left-hand side." - + _addendum_pt, - ), - "leftlabel.rotation": ( - "vertical", - _validate_rotation, - "Rotation for row labels on the left-hand side." + _addendum_rotation, - ), - "leftlabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for row labels on the left-hand side." + _addendum_font, - ), - "lollipop.markersize": ( - 36, - _validate_float, - "Size of lollipops in the lollipop plot.", - ), - "lollipop.stemcolor": ( - BLACK, - _validate_color, - "Color of lollipop lines.", - ), - "lollipop.stemwidth": ( - LINEWIDTH, - _validate_pt, - "Width of the stem", - ), - "lollipop.stemlinestyle": ( - "-", - _validate_linestyle, - "Line style of lollipop lines.", - ), - "leftlabel.weight": ( - "bold", - _validate_fontweight, - "Font weight for row labels on the left-hand side.", - ), - # Meta settings - "margin": ( - MARGIN, - _validate_float, - "The fractional *x* and *y* axis data margins when limits are unset. " - "Alias for :rcraw:`axes.margin`.", - ), - "meta.edgecolor": ( - BLACK, - _validate_color, - "Color of axis spines, tick marks, tick labels, and labels.", - ), - "meta.color": ( - BLACK, - _validate_color, - "Color of axis spines, tick marks, tick labels, and labels. " - "Alias for :rcraw:`meta.edgecolor`.", - ), - "meta.linewidth": ( - LINEWIDTH, - _validate_pt, - "Thickness of axis spines and major tick lines.", - ), - "meta.width": ( - LINEWIDTH, - _validate_pt, - "Thickness of axis spines and major tick lines. " - "Alias for :rcraw:`meta.linewidth`.", - ), - # For negative positive patches - "negcolor": ( - "blue7", - _validate_color, - "Color for negative bars and shaded areas when using ``negpos=True``. " - "See also :rcraw:`poscolor`.", - ), - "poscolor": ( - "red7", - _validate_color, - "Color for positive bars and shaded areas when using ``negpos=True``. " - "See also :rcraw:`negcolor`.", - ), - # Ocean patches - "ocean": (False, _validate_bool, "Toggles ocean patches on and off."), - "ocean.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for ocean patches", - ), - "ocean.color": (WHITE, _validate_color, "Face color for ocean patches."), - "ocean.zorder": (ZPATCHES, _validate_float, "Z-order for ocean patches."), - "ocean.rasterized": ( - False, - _validate_bool, - "Turns rasterization on or off for the oceans feature for GeoAxes.", - ), - # Geographic resolution - "reso": ( - "lo", - _validate_belongs("lo", "med", "hi", "x-hi", "xx-hi"), - "Resolution for `~ultraplot.axes.GeoAxes` geographic features. " - "Must be one of ``'lo'``, ``'med'``, ``'hi'``, ``'x-hi'``, or ``'xx-hi'``.", - ), - # Right subplot labels - "rightlabel.color": ( - BLACK, - _validate_color, - "Font color for row labels on the right-hand side.", - ), - "rightlabel.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and row labels on the right-hand side." - + _addendum_pt, - ), - "rightlabel.rotation": ( - "vertical", - _validate_rotation, - "Rotation for row labels on the right-hand side." + _addendum_rotation, - ), - "rightlabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for row labels on the right-hand side." + _addendum_font, - ), - "rightlabel.weight": ( - "bold", - _validate_fontweight, - "Font weight for row labels on the right-hand side.", - ), - # River lines - "rivers": (False, _validate_bool, "Toggles river lines on and off."), - "rivers.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for river lines.", - ), - "rivers.color": (BLACK, _validate_color, "Line color for river lines."), - "rivers.linewidth": (LINEWIDTH, _validate_pt, "Line width for river lines."), - "rivers.zorder": (ZLINES, _validate_float, "Z-order for river lines."), - "rivers.rasterized": ( - False, - _validate_bool, - "Toggles rasterization on or off for rivers feature for GeoAxes.", - ), - # Circlize settings - "chord.start": ( - 0.0, - _validate_float, - "Start angle for chord diagrams.", - ), - "chord.end": ( - 360.0, - _validate_float, - "End angle for chord diagrams.", - ), - "chord.space": ( - 0.0, - _validate_float_or_iterable, - "Inter-sector spacing for chord diagrams.", - ), - "chord.endspace": ( - True, - _validate_bool, - "Whether to add an ending space gap for chord diagrams.", - ), - "chord.r_lim": ( - (97.0, 100.0), - _validate_tuple_float_2, - "Radial limits for chord diagrams.", - ), - "chord.ticks_interval": ( - None, - _validate_or_none(_validate_int), - "Tick interval for chord diagrams.", - ), - "chord.order": ( - None, - _validate_or_none(_validate_string_or_iterable), - "Ordering of sectors for chord diagrams.", - ), - "radar.r_lim": ( - (0.0, 100.0), - _validate_tuple_float_2, - "Radial limits for radar charts.", - ), - "radar.vmin": ( - 0.0, - _validate_float, - "Minimum value for radar charts.", - ), - "radar.vmax": ( - 100.0, - _validate_float, - "Maximum value for radar charts.", - ), - "radar.fill": ( - True, - _validate_bool, - "Whether to fill radar chart polygons.", - ), - "radar.marker_size": ( - 0, - _validate_int, - "Marker size for radar charts.", - ), - "radar.bg_color": ( - "#eeeeee80", - _validate_or_none(_validate_color), - "Background color for radar charts.", - ), - "radar.circular": ( - False, - _validate_bool, - "Whether to use circular radar charts.", - ), - "radar.show_grid_label": ( - True, - _validate_bool, - "Whether to show grid labels on radar charts.", - ), - "radar.grid_interval_ratio": ( - 0.2, - _validate_or_none(_validate_float), - "Grid interval ratio for radar charts.", - ), - "phylogeny.start": ( - 0.0, - _validate_float, - "Start angle for phylogeny plots.", - ), - "phylogeny.end": ( - 360.0, - _validate_float, - "End angle for phylogeny plots.", - ), - "phylogeny.r_lim": ( - (50.0, 100.0), - _validate_tuple_float_2, - "Radial limits for phylogeny plots.", - ), - "phylogeny.format": ( - "newick", - _validate_string, - "Input format for phylogeny plots.", - ), - "phylogeny.outer": ( - True, - _validate_bool, - "Whether to place phylogeny leaves on the outer edge.", - ), - "phylogeny.align_leaf_label": ( - True, - _validate_bool, - "Whether to align phylogeny leaf labels.", - ), - "phylogeny.ignore_branch_length": ( - False, - _validate_bool, - "Whether to ignore branch lengths in phylogeny plots.", - ), - "phylogeny.leaf_label_size": ( - None, - _validate_or_none(_validate_float), - "Leaf label font size for phylogeny plots.", - ), - "phylogeny.leaf_label_rmargin": ( - 2.0, - _validate_float, - "Radial margin for phylogeny leaf labels.", - ), - "phylogeny.reverse": ( - False, - _validate_bool, - "Whether to reverse phylogeny orientation.", - ), - "phylogeny.ladderize": ( - False, - _validate_bool, - "Whether to ladderize phylogeny branches.", - ), - # Sankey diagrams - "sankey.align": ( - "center", - _validate_belongs("center", "left", "right", "justify"), - "Horizontal alignment of nodes.", - ), - "sankey.connect": ( - (0, 0), - _validate_tuple_int_2, - "Connection path for Sankey diagram.", - ), - "sankey.flow_labels": ( - False, - _validate_bool, - "Whether to draw flow labels.", - ), - "sankey.flow_label_pos": ( - 0.5, - _validate_float, - "Position of flow labels along the flow.", - ), - "sankey.flow_sort": ( - True, - _validate_bool, - "Whether to sort flows.", - ), - "sankey.node_labels": ( - True, - _validate_bool, - "Whether to draw node labels.", - ), - "sankey.node_label_offset": ( - 0.01, - _validate_float, - "Offset for node labels.", - ), - "sankey.node_label_outside": ( - "auto", - _validate_bool_or_string, - "Position of node labels relative to the node.", - ), - "sankey.other_label": ( - "Other", - _validate_string, - "Label for 'other' category in Sankey diagram.", - ), - "sankey.pathlabel": ( - "", - _validate_string, - "Label for the patch.", - ), - "sankey.pathlengths": ( - 0.25, - _validate_float, - "Path lengths for Sankey diagram.", - ), - "sankey.rotation": ( - 0.0, - _validate_float, - "Rotation of the Sankey diagram.", - ), - "sankey.trunklength": ( - 1.0, - _validate_float, - "Trunk length for Sankey diagram.", - ), - # Subplots settings - "subplots.align": ( - False, - _validate_bool, - "Whether to align axis labels during draw. See `aligning labels " - "`__.", # noqa: E501 - ), - "subplots.equalspace": ( - False, - _validate_bool, - "Whether to make the tight layout algorithm assign the same space for " - "every row and the same space for every column.", - ), - "subplots.groupspace": ( - True, - _validate_bool, - "Whether to make the tight layout algorithm consider space between only " - 'adjacent subplot "groups" rather than every subplot in the row or column.', - ), - "subplots.innerpad": ( - 1, - _validate_em, - "Padding between adjacent subplots." + _addendum_em, - ), - "subplots.outerpad": ( - 0.5, - _validate_em, - "Padding around figure edge." + _addendum_em, - ), - "subplots.panelpad": ( - 0.5, - _validate_em, - "Padding between subplots and panels, and between stacked panels." - + _addendum_em, - ), - "subplots.panelwidth": (0.5, _validate_in, "Width of side panels." + _addendum_in), - "subplots.refwidth": ( - 2.5, - _validate_in, - "Default width of the reference subplot." + _addendum_in, - ), - "subplots.share": ( - "auto", - _validate_belongs( - 0, 1, 2, 3, 4, False, "labels", "limits", True, "all", "auto" - ), - "The axis sharing level, one of ``0``, ``1``, ``2``, or ``3``, or the " - "more intuitive aliases ``False``, ``'labels'``, ``'limits'``, ``True``, " - "or ``'auto'``. See `~ultraplot.figure.Figure` for details.", - ), - "subplots.span": ( - True, - _validate_bool, - "Toggles spanning axis labels. See `~ultraplot.ui.subplots` for details.", - ), - "subplots.tight": ( - True, - _validate_bool, - "Whether to auto-adjust the subplot spaces and figure margins.", - ), - "subplots.pixelsnap": ( - False, - _validate_bool, - "Whether to snap subplot bounds to the renderer pixel grid during draw.", - ), - # Super title settings - "suptitle.color": (BLACK, _validate_color, "Figure title color."), - "suptitle.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and the figure super title." + _addendum_pt, - ), - "suptitle.size": ( - LARGESIZE, - _validate_fontsize, - "Figure title font size." + _addendum_font, - ), - "suptitle.weight": ("bold", _validate_fontweight, "Figure title font weight."), - # Tick settings - "tick.color": (BLACK, _validate_color, "Major and minor tick color."), - "tick.dir": ( - TICKDIR, - _validate_belongs("in", "out", "inout"), - "Major and minor tick direction. Must be one of " - "``'out'``, ``'in'``, or ``'inout'``.", - ), - "tick.labelcolor": (BLACK, _validate_color, "Axis tick label color."), - "tick.labelpad": ( - TICKPAD, - _validate_pt, - "Padding between ticks and tick labels." + _addendum_pt, - ), - "tick.labelsize": ( - SMALLSIZE, - _validate_fontsize, - "Axis tick label font size." + _addendum_font, - ), - "tick.labelweight": ( - "normal", - _validate_fontweight, - "Axis tick label font weight.", - ), - "tick.len": (TICKLEN, _validate_pt, "Length of major ticks in points."), - "tick.lenratio": ( - TICKLENRATIO, - _validate_float, - "Ratio of minor tickline length to major tickline length.", - ), - "tick.linewidth": (LINEWIDTH, _validate_pt, "Major tickline width."), - "tick.minor": ( - TICKMINOR, - _validate_bool, - "Toggles minor ticks on and off.", - ), - "tick.pad": (TICKPAD, _validate_pt, "Alias for :rcraw:`tick.labelpad`."), - "tick.width": ( - LINEWIDTH, - _validate_pt, - "Major tickline width. Alias for :rcraw:`tick.linewidth`.", - ), - "tick.widthratio": ( - TICKWIDTHRATIO, - _validate_float, - "Ratio of minor tickline width to major tickline width.", - ), - # Title settings - "title.above": ( - True, - _validate_belongs(False, True, "panels"), - "Whether to move outer titles and a-b-c labels above panels, colorbars, or " - "legends that are above the axes. If the string 'panels' then text is only " - "redirected above axes panels. Otherwise should be boolean.", - ), - "title.border": ( - True, - _validate_bool, - "Whether to draw a white border around titles " - "when :rcraw:`title.loc` is inside the axes.", - ), - "title.borderwidth": (1.5, _validate_pt, "Width of the border around titles."), - "title.bbox": ( - False, - _validate_bool, - "Whether to draw semi-transparent bounding boxes around titles " - "when :rcraw:`title.loc` is inside the axes.", - ), - "title.bboxcolor": (WHITE, _validate_color, "Axes title bounding box color."), - "title.bboxstyle": ("square", _validate_boxstyle, "Axes title bounding box style."), - "title.bboxalpha": (0.5, _validate_float, "Axes title bounding box opacity."), - "title.bboxpad": ( - None, - _validate_or_none(_validate_pt), - "Padding for the title bounding box. By default this is scaled " - "to make the box flush against the axes edge." + _addendum_pt, - ), - "title.color": ( - BLACK, - _validate_color, - "Axes title color. Alias for :rcraw:`axes.titlecolor`.", - ), - "title.loc": ( - "center", - _validate_belongs(*TEXT_LOCS), - "Title position. For options see the :ref:`location table `.", - ), - "title.pad": ( - TITLEPAD, - _validate_pt, - "Padding between the axes edge and the inner and outer titles and " - "a-b-c labels. Alias for :rcraw:`axes.titlepad`." + _addendum_pt, - ), - "title.size": ( - LARGESIZE, - _validate_fontsize, - "Axes title font size. Alias for :rcraw:`axes.titlesize`." + _addendum_font, - ), - "title.weight": ( - "normal", - _validate_fontweight, - "Axes title font weight. Alias for :rcraw:`axes.titleweight`.", - ), - # Top subplot label settings - "toplabel.color": ( - BLACK, - _validate_color, - "Font color for column labels on the top of the figure.", - ), - "toplabel.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and column labels on the top of the figure." - + _addendum_pt, - ), - "toplabel.rotation": ( - "horizontal", - _validate_rotation, - "Rotation for column labels at the top of the figure." + _addendum_rotation, - ), - "toplabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for column labels on the top of the figure." + _addendum_font, - ), - "toplabel.weight": ( - "bold", - _validate_fontweight, - "Font weight for column labels on the top of the figure.", - ), - # Unit formatting - "unitformat": ( - "L", - _validate_string, - "The format string used to format `pint.Quantity` default unit labels " - "using ``format(units, unitformat)``. See also :rcraw:`autoformat`.", - ), - "ultraplot.check_for_latest_version": ( - False, - _validate_bool, - "Whether to check for the latest version of UltraPlot on PyPI when importing", - ), - "ultraplot.eager_import": ( - False, - _validate_bool, - "Whether to import the full public API during setup instead of lazily.", - ), -} + build_core_rc_table(globals()), +) # Child settings. Changing the parent changes all the children, but # changing any of the children does not change the parent. diff --git a/ultraplot/tests/test_config.py b/ultraplot/tests/test_config.py index 02d62b369..192db2506 100644 --- a/ultraplot/tests/test_config.py +++ b/ultraplot/tests/test_config.py @@ -230,6 +230,31 @@ def _reader(): assert all(value in allowed for value in observed) +def test_rc_registry_merge_disjoint_tables(): + """ + Registry merge should combine disjoint rc tables. + """ + from ultraplot.internals.rc.registry import merge_rc_tables + + left = {"a.b": (1, lambda x: x, "left")} + right = {"c.d": (2, lambda x: x, "right")} + merged = merge_rc_tables(left, right) + assert merged["a.b"][0] == 1 + assert merged["c.d"][0] == 2 + + +def test_rc_registry_merge_duplicate_keys_raises(): + """ + Registry merge should fail fast on duplicate keys. + """ + from ultraplot.internals.rc.registry import merge_rc_tables + + table1 = {"a.b": (1, lambda x: x, "first")} + table2 = {"a.b": (2, lambda x: x, "second")} + with pytest.raises(ValueError, match="Duplicate rc keys"): + merge_rc_tables(table1, table2) + + def _run_in_subprocess(code): code = ( "import pathlib\n" From 2116d9f0e788fa464aa902150e13b7fa57d86905 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 12 Feb 2026 08:29:08 +1000 Subject: [PATCH 3/9] Split rcsetup composition into domain modules and extract deprecation maps --- ultraplot/internals/rc/__init__.py | 19 +++++- ultraplot/internals/rc/axes.py | 61 +++++++++++++++++++ ultraplot/internals/rc/deprecations.py | 80 ++++++++++++++++++++++++ ultraplot/internals/rc/ribbon.py | 35 +++++++++++ ultraplot/internals/rc/sankey.py | 28 +++++++++ ultraplot/internals/rc/subplots.py | 22 +++++++ ultraplot/internals/rc/text.py | 34 +++++++++++ ultraplot/internals/rcsetup.py | 84 ++++++-------------------- ultraplot/tests/test_config.py | 48 +++++++++++++++ 9 files changed, 343 insertions(+), 68 deletions(-) create mode 100644 ultraplot/internals/rc/axes.py create mode 100644 ultraplot/internals/rc/deprecations.py create mode 100644 ultraplot/internals/rc/ribbon.py create mode 100644 ultraplot/internals/rc/sankey.py create mode 100644 ultraplot/internals/rc/subplots.py create mode 100644 ultraplot/internals/rc/text.py diff --git a/ultraplot/internals/rc/__init__.py b/ultraplot/internals/rc/__init__.py index 93fe2f96f..c0f553019 100644 --- a/ultraplot/internals/rc/__init__.py +++ b/ultraplot/internals/rc/__init__.py @@ -3,8 +3,25 @@ Helpers for composing rc parameter tables. """ +from .axes import build_axes_rc_table from .core import build_core_rc_table +from .deprecations import get_rc_removed, get_rc_renamed from .plot_types import build_plot_type_rc_table +from .ribbon import build_ribbon_rc_table from .registry import merge_rc_tables +from .sankey import build_sankey_rc_table +from .subplots import build_subplots_rc_table +from .text import build_text_rc_table -__all__ = ["build_core_rc_table", "build_plot_type_rc_table", "merge_rc_tables"] +__all__ = [ + "build_axes_rc_table", + "build_core_rc_table", + "build_plot_type_rc_table", + "build_ribbon_rc_table", + "build_sankey_rc_table", + "build_subplots_rc_table", + "build_text_rc_table", + "get_rc_removed", + "get_rc_renamed", + "merge_rc_tables", +] diff --git a/ultraplot/internals/rc/axes.py b/ultraplot/internals/rc/axes.py new file mode 100644 index 000000000..f9d147b22 --- /dev/null +++ b/ultraplot/internals/rc/axes.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +""" +Axes-domain rc defaults. +""" + +from __future__ import annotations + +from typing import Any, Callable, Mapping + +from .core import build_core_rc_table +from .plot_types import build_plot_type_rc_table + +RcValidator = Callable[[Any], Any] +RcEntry = tuple[Any, RcValidator, str] +RcTable = dict[str, RcEntry] + +_TEXT_PREFIXES = ( + "abc.", + "text.", + "title.", + "suptitle.", + "leftlabel.", + "rightlabel.", + "toplabel.", + "bottomlabel.", + "font.", +) + + +def _plot_type_validators(ns: Mapping[str, Any]) -> dict[str, RcValidator]: + return { + "validate_bool": ns["_validate_bool"], + "validate_color": ns["_validate_color"], + "validate_float": ns["_validate_float"], + "validate_int": ns["_validate_int"], + "validate_string": ns["_validate_string"], + } + + +def build_axes_rc_table(ns: Mapping[str, Any]) -> RcTable: + """ + Build axes-domain rc entries. + + This includes all core settings except text/subplots domains and adds + curved-quiver plot-type defaults. + """ + core = build_core_rc_table(ns) + table = { + key: value + for key, value in core.items() + if not key.startswith("subplots.") and not key.startswith(_TEXT_PREFIXES) + } + plot_types = build_plot_type_rc_table(**_plot_type_validators(ns)) + table.update( + { + key: value + for key, value in plot_types.items() + if key.startswith("curved_quiver.") + } + ) + return table diff --git a/ultraplot/internals/rc/deprecations.py b/ultraplot/internals/rc/deprecations.py new file mode 100644 index 000000000..edd5f1e57 --- /dev/null +++ b/ultraplot/internals/rc/deprecations.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +""" +Deprecated and removed rc keys. +""" + +from __future__ import annotations + +_RC_REMOVED = { # {key: (alternative, version)} dictionary + "rgbcycle": ("", "0.6.0"), # no alternative, we no longer offer this feature + "geogrid.latmax": ("Please use ax.format(latmax=N) instead.", "0.6.0"), + "geogrid.latstep": ("Please use ax.format(latlines=N) instead.", "0.6.0"), + "geogrid.lonstep": ("Please use ax.format(lonlines=N) instead.", "0.6.0"), + "gridminor.latstep": ("Please use ax.format(latminorlines=N) instead.", "0.6.0"), + "gridminor.lonstep": ("Please use ax.format(lonminorlines=N) instead.", "0.6.0"), +} + +_RC_RENAMED = { # {old_key: (new_key, version)} dictionary + "abc.format": ("abc", "0.5.0"), + "align": ("subplots.align", "0.6.0"), + "axes.facealpha": ("axes.alpha", "0.6.0"), + "geoaxes.edgecolor": ("axes.edgecolor", "0.6.0"), + "geoaxes.facealpha": ("axes.alpha", "0.6.0"), + "geoaxes.facecolor": ("axes.facecolor", "0.6.0"), + "geoaxes.linewidth": ("axes.linewidth", "0.6.0"), + "geogrid.alpha": ("grid.alpha", "0.6.0"), + "geogrid.color": ("grid.color", "0.6.0"), + "geogrid.labels": ("grid.labels", "0.6.0"), + "geogrid.labelpad": ("grid.pad", "0.6.0"), + "geogrid.labelsize": ("grid.labelsize", "0.6.0"), + "geogrid.linestyle": ("grid.linestyle", "0.6.0"), + "geogrid.linewidth": ("grid.linewidth", "0.6.0"), + "share": ("subplots.share", "0.6.0"), + "small": ("font.smallsize", "0.6.0"), + "large": ("font.largesize", "0.6.0"), + "span": ("subplots.span", "0.6.0"), + "tight": ("subplots.tight", "0.6.0"), + "axes.formatter.timerotation": ("formatter.timerotation", "0.6.0"), + "axes.formatter.zerotrim": ("formatter.zerotrim", "0.6.0"), + "abovetop": ("title.above", "0.7.0"), + "subplots.pad": ("subplots.outerpad", "0.7.0"), + "subplots.axpad": ("subplots.innerpad", "0.7.0"), + "subplots.axwidth": ("subplots.refwidth", "0.7.0"), + "text.labelsize": ("font.smallsize", "0.8.0"), + "text.titlesize": ("font.largesize", "0.8.0"), + "alpha": ("axes.alpha", "0.8.0"), + "facecolor": ("axes.facecolor", "0.8.0"), + "edgecolor": ("meta.color", "0.8.0"), + "color": ("meta.color", "0.8.0"), + "linewidth": ("meta.width", "0.8.0"), + "lut": ("cmap.lut", "0.8.0"), + "image.levels": ("cmap.levels", "0.8.0"), + "image.inbounds": ("cmap.inbounds", "0.8.0"), + "image.discrete": ("cmap.discrete", "0.8.0"), + "image.edgefix": ("edgefix", "0.8.0"), + "tick.ratio": ("tick.widthratio", "0.8.0"), + "grid.ratio": ("grid.widthratio", "0.8.0"), + "abc.style": ("abc", "0.8.0"), + "grid.loninline": ("grid.inlinelabels", "0.8.0"), + "grid.latinline": ("grid.inlinelabels", "0.8.0"), + "cmap.edgefix": ("edgefix", "0.9.0"), + "basemap": ("geo.backend", "0.10.0"), + "inlinefmt": ("inlineformat", "0.10.0"), + "cartopy.circular": ("geo.round", "0.10.0"), + "cartopy.autoextent": ("geo.extent", "0.10.0"), + "colorbar.rasterize": ("colorbar.rasterized", "0.10.0"), +} + + +def get_rc_removed(): + """ + Return removed rc settings. + """ + return _RC_REMOVED.copy() + + +def get_rc_renamed(): + """ + Return renamed rc settings. + """ + return _RC_RENAMED.copy() diff --git a/ultraplot/internals/rc/ribbon.py b/ultraplot/internals/rc/ribbon.py new file mode 100644 index 000000000..59d9a8d2a --- /dev/null +++ b/ultraplot/internals/rc/ribbon.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +""" +Ribbon-domain rc defaults. +""" + +from __future__ import annotations + +from typing import Any, Callable, Mapping + +from .core import build_core_rc_table +from .plot_types import build_plot_type_rc_table +from .registry import merge_rc_tables + +RcValidator = Callable[[Any], Any] +RcEntry = tuple[Any, RcValidator, str] +RcTable = dict[str, RcEntry] + + +def build_ribbon_rc_table(ns: Mapping[str, Any]) -> RcTable: + """ + Build ribbon-domain rc entries. + + Ribbon keys may be absent on branches where ribbon rc defaults have not yet + landed on main; this function safely returns an empty table in that case. + """ + core = build_core_rc_table(ns) + plot_types = build_plot_type_rc_table( + validate_bool=ns["_validate_bool"], + validate_color=ns["_validate_color"], + validate_float=ns["_validate_float"], + validate_int=ns["_validate_int"], + validate_string=ns["_validate_string"], + ) + combined = merge_rc_tables(core, plot_types) + return {key: value for key, value in combined.items() if key.startswith("ribbon.")} diff --git a/ultraplot/internals/rc/sankey.py b/ultraplot/internals/rc/sankey.py new file mode 100644 index 000000000..cfb29e12e --- /dev/null +++ b/ultraplot/internals/rc/sankey.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +""" +Sankey-domain rc defaults. +""" + +from __future__ import annotations + +from typing import Any, Callable, Mapping + +from .plot_types import build_plot_type_rc_table + +RcValidator = Callable[[Any], Any] +RcEntry = tuple[Any, RcValidator, str] +RcTable = dict[str, RcEntry] + + +def build_sankey_rc_table(ns: Mapping[str, Any]) -> RcTable: + """ + Build sankey-domain rc entries. + """ + table = build_plot_type_rc_table( + validate_bool=ns["_validate_bool"], + validate_color=ns["_validate_color"], + validate_float=ns["_validate_float"], + validate_int=ns["_validate_int"], + validate_string=ns["_validate_string"], + ) + return {key: value for key, value in table.items() if key.startswith("sankey.")} diff --git a/ultraplot/internals/rc/subplots.py b/ultraplot/internals/rc/subplots.py new file mode 100644 index 000000000..975db4e60 --- /dev/null +++ b/ultraplot/internals/rc/subplots.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +""" +Subplots-domain rc defaults. +""" + +from __future__ import annotations + +from typing import Any, Callable, Mapping + +from .core import build_core_rc_table + +RcValidator = Callable[[Any], Any] +RcEntry = tuple[Any, RcValidator, str] +RcTable = dict[str, RcEntry] + + +def build_subplots_rc_table(ns: Mapping[str, Any]) -> RcTable: + """ + Build subplots-domain rc entries. + """ + core = build_core_rc_table(ns) + return {key: value for key, value in core.items() if key.startswith("subplots.")} diff --git a/ultraplot/internals/rc/text.py b/ultraplot/internals/rc/text.py new file mode 100644 index 000000000..08f9ee3bc --- /dev/null +++ b/ultraplot/internals/rc/text.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +Text-domain rc defaults. +""" + +from __future__ import annotations + +from typing import Any, Callable, Mapping + +from .core import build_core_rc_table + +RcValidator = Callable[[Any], Any] +RcEntry = tuple[Any, RcValidator, str] +RcTable = dict[str, RcEntry] + +_TEXT_PREFIXES = ( + "abc.", + "text.", + "title.", + "suptitle.", + "leftlabel.", + "rightlabel.", + "toplabel.", + "bottomlabel.", + "font.", +) + + +def build_text_rc_table(ns: Mapping[str, Any]) -> RcTable: + """ + Build text-domain rc entries. + """ + core = build_core_rc_table(ns) + return {key: value for key, value in core.items() if key.startswith(_TEXT_PREFIXES)} diff --git a/ultraplot/internals/rcsetup.py b/ultraplot/internals/rcsetup.py index 4fb2197a5..ee5467ca0 100644 --- a/ultraplot/internals/rcsetup.py +++ b/ultraplot/internals/rcsetup.py @@ -26,7 +26,16 @@ ic, # noqa: F401 warnings, ) -from .rc import build_core_rc_table, build_plot_type_rc_table, merge_rc_tables +from .rc import ( + build_axes_rc_table, + build_ribbon_rc_table, + build_sankey_rc_table, + build_subplots_rc_table, + build_text_rc_table, + get_rc_removed, + get_rc_renamed, + merge_rc_tables, +) from .versions import _version_mpl # Regex for "probable" unregistered named colors. Try to retain warning message for @@ -973,14 +982,11 @@ def _validator_accepts(validator, value): "interpreted by `~ultraplot.utils.units`. Numeric units are points." ) _rc_ultraplot_table = merge_rc_tables( - build_plot_type_rc_table( - validate_bool=_validate_bool, - validate_color=_validate_color, - validate_float=_validate_float, - validate_int=_validate_int, - validate_string=_validate_string, - ), - build_core_rc_table(globals()), + build_axes_rc_table(globals()), + build_text_rc_table(globals()), + build_subplots_rc_table(globals()), + build_sankey_rc_table(globals()), + build_ribbon_rc_table(globals()), ) # Child settings. Changing the parent changes all the children, but @@ -1105,64 +1111,8 @@ def _validator_accepts(validator, value): # validate values before e.g. applying 'tick.lenratio'. So the renamed parameters # do not have to be added as _rc_children, since Configurator translates before # retrieving the list of children in _get_item_dicts. -_rc_removed = { # {key: (alternative, version)} dictionary - "rgbcycle": ("", "0.6.0"), # no alternative, we no longer offer this feature - "geogrid.latmax": ("Please use ax.format(latmax=N) instead.", "0.6.0"), - "geogrid.latstep": ("Please use ax.format(latlines=N) instead.", "0.6.0"), - "geogrid.lonstep": ("Please use ax.format(lonlines=N) instead.", "0.6.0"), - "gridminor.latstep": ("Please use ax.format(latminorlines=N) instead.", "0.6.0"), - "gridminor.lonstep": ("Please use ax.format(lonminorlines=N) instead.", "0.6.0"), -} -_rc_renamed = { # {old_key: (new_key, version)} dictionary - "abc.format": ("abc", "0.5.0"), - "align": ("subplots.align", "0.6.0"), - "axes.facealpha": ("axes.alpha", "0.6.0"), - "geoaxes.edgecolor": ("axes.edgecolor", "0.6.0"), - "geoaxes.facealpha": ("axes.alpha", "0.6.0"), - "geoaxes.facecolor": ("axes.facecolor", "0.6.0"), - "geoaxes.linewidth": ("axes.linewidth", "0.6.0"), - "geogrid.alpha": ("grid.alpha", "0.6.0"), - "geogrid.color": ("grid.color", "0.6.0"), - "geogrid.labels": ("grid.labels", "0.6.0"), - "geogrid.labelpad": ("grid.pad", "0.6.0"), - "geogrid.labelsize": ("grid.labelsize", "0.6.0"), - "geogrid.linestyle": ("grid.linestyle", "0.6.0"), - "geogrid.linewidth": ("grid.linewidth", "0.6.0"), - "share": ("subplots.share", "0.6.0"), - "small": ("font.smallsize", "0.6.0"), - "large": ("font.largesize", "0.6.0"), - "span": ("subplots.span", "0.6.0"), - "tight": ("subplots.tight", "0.6.0"), - "axes.formatter.timerotation": ("formatter.timerotation", "0.6.0"), - "axes.formatter.zerotrim": ("formatter.zerotrim", "0.6.0"), - "abovetop": ("title.above", "0.7.0"), - "subplots.pad": ("subplots.outerpad", "0.7.0"), - "subplots.axpad": ("subplots.innerpad", "0.7.0"), - "subplots.axwidth": ("subplots.refwidth", "0.7.0"), - "text.labelsize": ("font.smallsize", "0.8.0"), - "text.titlesize": ("font.largesize", "0.8.0"), - "alpha": ("axes.alpha", "0.8.0"), - "facecolor": ("axes.facecolor", "0.8.0"), - "edgecolor": ("meta.color", "0.8.0"), - "color": ("meta.color", "0.8.0"), - "linewidth": ("meta.width", "0.8.0"), - "lut": ("cmap.lut", "0.8.0"), - "image.levels": ("cmap.levels", "0.8.0"), - "image.inbounds": ("cmap.inbounds", "0.8.0"), - "image.discrete": ("cmap.discrete", "0.8.0"), - "image.edgefix": ("edgefix", "0.8.0"), - "tick.ratio": ("tick.widthratio", "0.8.0"), - "grid.ratio": ("grid.widthratio", "0.8.0"), - "abc.style": ("abc", "0.8.0"), - "grid.loninline": ("grid.inlinelabels", "0.8.0"), - "grid.latinline": ("grid.inlinelabels", "0.8.0"), - "cmap.edgefix": ("edgefix", "0.9.0"), - "basemap": ("geo.backend", "0.10.0"), - "inlinefmt": ("inlineformat", "0.10.0"), - "cartopy.circular": ("geo.round", "0.10.0"), - "cartopy.autoextent": ("geo.extent", "0.10.0"), - "colorbar.rasterize": ("colorbar.rasterized", "0.10.0"), -} +_rc_removed = get_rc_removed() +_rc_renamed = get_rc_renamed() # Validate the default settings dictionaries using a custom ultraplot _RcParams # and the original matplotlib RcParams. Also surreptitiously add ultraplot diff --git a/ultraplot/tests/test_config.py b/ultraplot/tests/test_config.py index 192db2506..bbe277760 100644 --- a/ultraplot/tests/test_config.py +++ b/ultraplot/tests/test_config.py @@ -255,6 +255,54 @@ def test_rc_registry_merge_duplicate_keys_raises(): merge_rc_tables(table1, table2) +def test_rc_domain_tables_match_legacy_tables(): + """ + Domain split tables should match legacy composed rc table keys. + """ + from ultraplot.internals import rcsetup + from ultraplot.internals.rc import ( + build_axes_rc_table, + build_core_rc_table, + build_plot_type_rc_table, + build_ribbon_rc_table, + build_sankey_rc_table, + build_subplots_rc_table, + build_text_rc_table, + merge_rc_tables, + ) + + ns = vars(rcsetup) + legacy = merge_rc_tables( + build_core_rc_table(ns), + build_plot_type_rc_table( + validate_bool=ns["_validate_bool"], + validate_color=ns["_validate_color"], + validate_float=ns["_validate_float"], + validate_int=ns["_validate_int"], + validate_string=ns["_validate_string"], + ), + ) + domain_split = merge_rc_tables( + build_axes_rc_table(ns), + build_text_rc_table(ns), + build_subplots_rc_table(ns), + build_sankey_rc_table(ns), + build_ribbon_rc_table(ns), + ) + assert set(domain_split) == set(legacy) + + +def test_rc_deprecations_module_matches_rcsetup(): + """ + Deprecation maps should be sourced from rc.deprecations. + """ + from ultraplot.internals import rcsetup + from ultraplot.internals.rc.deprecations import get_rc_removed, get_rc_renamed + + assert rcsetup._rc_removed == get_rc_removed() + assert rcsetup._rc_renamed == get_rc_renamed() + + def _run_in_subprocess(code): code = ( "import pathlib\n" From fb6ac524452e2614dc01755865b93aeea352030c Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 12 Feb 2026 08:44:37 +1000 Subject: [PATCH 4/9] black --- ultraplot/internals/rc/core.py | 2605 ++++++++++++++++---------------- 1 file changed, 1321 insertions(+), 1284 deletions(-) diff --git a/ultraplot/internals/rc/core.py b/ultraplot/internals/rc/core.py index 35a7087d1..f9e0bbc76 100644 --- a/ultraplot/internals/rc/core.py +++ b/ultraplot/internals/rc/core.py @@ -80,1288 +80,1325 @@ def build_core_rc_table(ns: Mapping[str, Any]) -> RcTable: _validate_tuple_float_2 = ns["_validate_tuple_float_2"] _validate_tuple_int_2 = ns["_validate_tuple_int_2"] return { - "external.shrink": ( - 0.9, - _validate_float, - "Default shrink factor for external axes containers.", - ), - # Stylesheet - "style": ( - None, - _validate_or_none(_validate_string), - "The default matplotlib `stylesheet " - "`__ " # noqa: E501 - "name. If ``None``, a custom ultraplot style is used. " - "If ``'default'``, the default matplotlib style is used.", - ), - # A-b-c labels - "abc": ( - False, - _validate_abc, - "If ``False`` then a-b-c labels are disabled. If ``True`` the default label " - "style `a` is used. If string this indicates the style and must contain the " - "character `a` or ``A``, for example ``'a.'`` or ``'(A)'``.", - ), - "abc.border": ( - True, - _validate_bool, - "Whether to draw a white border around a-b-c labels " - "when :rcraw:`abc.loc` is inside the axes.", - ), - "abc.borderwidth": ( - 1.5, - _validate_pt, - "Width of the white border around a-b-c labels.", - ), - "text.borderstyle": ( - "bevel", - _validate_joinstyle, - "Join style for text border strokes. Must be one of " - "``'miter'``, ``'round'``, or ``'bevel'``.", - ), - "text.curved.upright": ( - True, - _validate_bool, - "Whether curved text is flipped to remain upright by default.", - ), - "text.curved.ellipsis": ( - False, - _validate_bool, - "Whether to show ellipses when curved text exceeds path length.", - ), - "text.curved.avoid_overlap": ( - True, - _validate_bool, - "Whether curved text hides overlapping glyphs by default.", - ), - "text.curved.overlap_tol": ( - 0.1, - _validate_float, - "Overlap threshold used when hiding curved-text glyphs.", - ), - "text.curved.curvature_pad": ( - 2.0, - _validate_float, - "Extra curved-text glyph spacing per radian of local curvature.", - ), - "text.curved.min_advance": ( - 1.0, - _validate_float, - "Minimum extra curved-text glyph spacing in pixels.", - ), - "abc.bbox": ( - False, - _validate_bool, - "Whether to draw semi-transparent bounding boxes around a-b-c labels " - "when :rcraw:`abc.loc` is inside the axes.", - ), - "abc.bboxcolor": (WHITE, _validate_color, "a-b-c label bounding box color."), - "abc.bboxstyle": ("square", _validate_boxstyle, "a-b-c label bounding box style."), - "abc.bboxalpha": (0.5, _validate_float, "a-b-c label bounding box opacity."), - "abc.bboxpad": ( - None, - _validate_or_none(_validate_pt), - "Padding for the a-b-c label bounding box. By default this is scaled " - "to make the box flush against the subplot edge." + _addendum_pt, - ), - "abc.color": (BLACK, _validate_color, "a-b-c label color."), - "abc.loc": ( - "left", # left side above the axes - _validate_belongs(*TEXT_LOCS), - "a-b-c label position. " - "For options see the :ref:`location table `.", - ), - "abc.size": ( - LARGESIZE, - _validate_fontsize, - "a-b-c label font size." + _addendum_font, - ), - "abc.titlepad": ( - LABELPAD, - _validate_pt, - "Padding separating the title and a-b-c label when in the same location." - + _addendum_pt, - ), - "abc.weight": ("bold", _validate_fontweight, "a-b-c label font weight."), - # Autoformatting - "autoformat": ( - True, - _validate_bool, - "Whether to automatically apply labels from `pandas.Series`, " - "`pandas.DataFrame`, and `xarray.DataArray` objects passed to " - "plotting functions. See also :rcraw:`unitformat`.", - ), - # Axes additions - "axes.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity of the background axes patch.", - ), - "axes.inbounds": ( - True, - _validate_bool, - "Whether to exclude out-of-bounds data when determining the default *y* (*x*) " - "axis limits and the *x* (*y*) axis limits have been locked.", - ), - "axes.margin": ( - MARGIN, - _validate_float, - "The fractional *x* and *y* axis margins when limits are unset.", - ), - "bar.bar_labels": ( - False, - _validate_bool, - "Add value of the bars to the bar labels", - ), - # Country borders - "borders": (False, _validate_bool, "Toggles country border lines on and off."), - "borders.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for country border lines.", - ), - "borders.color": (BLACK, _validate_color, "Line color for country border lines."), - "borders.linewidth": ( - LINEWIDTH, - _validate_pt, - "Line width for country border lines.", - ), - "borders.zorder": (ZLINES, _validate_float, "Z-order for country border lines."), - "borders.rasterized": ( - False, - _validate_bool, - "Toggles rasterization on or off for border feature in GeoAxes.", - ), - # Bottom subplot labels - "bottomlabel.color": ( - BLACK, - _validate_color, - "Font color for column labels on the bottom of the figure.", - ), - "bottomlabel.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and column labels on the bottom of the figure." - + _addendum_pt, - ), - "bottomlabel.rotation": ( - "horizontal", - _validate_rotation, - "Rotation for column labels at the bottom of the figure." + _addendum_rotation, - ), - "bottomlabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for column labels on the bottom of the figure." + _addendum_font, - ), - "bottomlabel.weight": ( - "bold", - _validate_fontweight, - "Font weight for column labels on the bottom of the figure.", - ), - "cftime.time_unit": ( - "days since 2000-01-01", - _validate_string, - "Time unit for non-Gregorian calendars.", - ), - "cftime.resolution": ( - "DAILY", - _validate_cftime_resolution, - "Default time resolution for non-Gregorian calendars.", - ), - "cftime.time_resolution_format": ( - { - "SECONDLY": "%S", - "MINUTELY": "%M", - "HOURLY": "%H", - "DAILY": "%d", - "MONTHLY": "%m", - "YEARLY": "%Y", - }, - _validate_cftime_resolution_format, - "Dict used for formatting non-Gregorian calendars.", - ), - "cftime.max_display_ticks": ( - 7, - _validate_int, - "Number of ticks to display for cftime units.", - ), - # Coastlines - "coast": (False, _validate_bool, "Toggles coastline lines on and off."), - "coast.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for coast lines", - ), - "coast.color": (BLACK, _validate_color, "Line color for coast lines."), - "coast.linewidth": (LINEWIDTH, _validate_pt, "Line width for coast lines."), - "coast.zorder": (ZLINES, _validate_float, "Z-order for coast lines."), - "coast.rasterized": ( - False, - _validate_bool, - "Toggles the rasterization of the coastlines feature for GeoAxes.", - ), - # Colorbars - "colorbar.center_levels": ( - False, - _validate_bool, - "Center the ticks in the center of each segment.", - ), - "colorbar.edgecolor": ( - BLACK, - _validate_color, - "Color for the inset colorbar frame edge.", - ), - "colorbar.extend": ( - 1.3, - _validate_em, - 'Length of rectangular or triangular "extensions" for panel colorbars.' - + _addendum_em, - ), - "colorbar.outline": ( - True, - _validate_bool, - "Whether to draw a frame around the colorbar.", - ), - "colorbar.labelrotation": ( - "auto", - _validate_float_or_auto, - "Rotation of colorbar labels.", - ), - "colorbar.fancybox": ( - False, - _validate_bool, - 'Whether to use a "fancy" round bounding box for inset colorbar frames.', - ), - "colorbar.framealpha": ( - FRAMEALPHA, - _validate_float, - "Opacity for inset colorbar frames.", - ), - "colorbar.facecolor": ( - WHITE, - _validate_color, - "Color for the inset colorbar frame.", - ), - "colorbar.frameon": ( - True, - _validate_bool, - "Whether to draw a frame behind inset colorbars.", - ), - "colorbar.grid": ( - False, - _validate_bool, - "Whether to draw borders between each level of the colorbar.", - ), - "colorbar.insetextend": ( - 0.9, - _validate_em, - 'Length of rectangular or triangular "extensions" for inset colorbars.' - + _addendum_em, - ), - "colorbar.insetlength": ( - 8, - _validate_em, - "Length of inset colorbars." + _addendum_em, - ), - "colorbar.insetpad": ( - 0.7, - _validate_em, - "Padding between axes edge and inset colorbars." + _addendum_em, - ), - "colorbar.insetwidth": ( - 1.2, - _validate_em, - "Width of inset colorbars." + _addendum_em, - ), - "colorbar.length": (1, _validate_em, "Length of outer colorbars."), - "colorbar.loc": ( - "right", - _validate_belongs(*COLORBAR_LOCS), - "Inset colorbar location. " - "For options see the :ref:`location table `.", - ), - "colorbar.width": (0.2, _validate_in, "Width of outer colorbars." + _addendum_in), - "colorbar.rasterized": ( - False, - _validate_bool, - "Whether to use rasterization for colorbar solids.", - ), - "colorbar.shadow": ( - False, - _validate_bool, - "Whether to add a shadow underneath inset colorbar frames.", - ), - # Color cycle additions - "cycle": ( - CYCLE, - _validate_cmap("discrete", cycle=True), - "Name of the color cycle assigned to :rcraw:`axes.prop_cycle`.", - ), - # Colormap additions - "cmap": ( - CMAPSEQ, - _validate_cmap("continuous"), - "Alias for :rcraw:`cmap.sequential` and :rcraw:`image.cmap`.", - ), - "cmap.autodiverging": ( - True, - _validate_bool, - "Whether to automatically apply a diverging colormap and " - "normalizer based on the data.", - ), - "cmap.qualitative": ( - CMAPCAT, - _validate_cmap("discrete"), - "Default colormap for qualitative datasets.", - ), - "cmap.cyclic": ( - CMAPCYC, - _validate_cmap("continuous"), - "Default colormap for cyclic datasets.", - ), - "cmap.discrete": ( - None, - _validate_or_none(_validate_bool), - "If ``True``, `~ultraplot.colors.DiscreteNorm` is used for every colormap plot. " - "If ``False``, it is never used. If ``None``, it is used for all plot types " - "except `imshow`, `matshow`, `spy`, `hexbin`, and `hist2d`.", - ), - "cmap.diverging": ( - CMAPDIV, - _validate_cmap("continuous"), - "Default colormap for diverging datasets.", - ), - "cmap.inbounds": ( - True, - _validate_bool, - "If ``True`` and the *x* and *y* axis limits are fixed, only in-bounds data " - "is considered when determining the default colormap `vmin` and `vmax`.", - ), - "cmap.levels": ( - 11, - _validate_int, - "Default number of `~ultraplot.colors.DiscreteNorm` levels for plotting " - "commands that use colormaps.", - ), - "cmap.listedthresh": ( - 64, - _validate_int, - "Native `~matplotlib.colors.ListedColormap`\\ s with more colors than " - "this are converted to :class:`~ultraplot.colors.ContinuousColormap` rather than " - ":class:`~ultraplot.colors.DiscreteColormap`. This helps translate continuous " - "colormaps from external projects.", - ), - "cmap.lut": ( - 256, - _validate_int, - "Number of colors in the colormap lookup table. " - "Alias for :rcraw:`image.lut`.", - ), - "cmap.robust": ( - False, - _validate_bool, - "If ``True``, the default colormap `vmin` and `vmax` are chosen using the " - "2nd to 98th percentiles rather than the minimum and maximum.", - ), - "cmap.sequential": ( - CMAPSEQ, - _validate_cmap("continuous"), - "Default colormap for sequential datasets. Alias for :rcraw:`image.cmap`.", - ), - # Special setting - "edgefix": ( - True, - _validate_bool, - 'Whether to fix issues with "white lines" appearing between patches ' - "in saved vector graphics and with vector graphic backends. Applies " - "to colorbar levels and bar, area, pcolor, and contour plots.", - ), - # Font settings - "font.name": (FONTNAME, _validate_fontname, "Alias for :rcraw:`font.family`."), - "font.small": (SMALLSIZE, _validate_fontsize, "Alias for :rcraw:`font.smallsize`."), - "font.smallsize": ( - SMALLSIZE, - _validate_fontsize, - "Meta setting that changes the label-like sizes ``axes.labelsize``, " - "``legend.fontsize``, ``tick.labelsize``, and ``grid.labelsize``. Default is " - "``'medium'`` (equivalent to :rcraw:`font.size`)." + _addendum_font, - ), - "font.large": (LARGESIZE, _validate_fontsize, "Alias for :rcraw:`font.largesize`."), - "font.largesize": ( - LARGESIZE, - _validate_fontsize, - "Meta setting that changes the title-like sizes ``abc.size``, ``title.size``, " - "``suptitle.size``, ``leftlabel.size``, ``rightlabel.size``, etc. Default is " - "``'med-large'`` (i.e. 1.1 times :rcraw:`font.size`)." + _addendum_font, - ), - # Formatter settings - "formatter.timerotation": ( - "vertical", - _validate_rotation, - "Rotation for *x* axis datetime tick labels." + _addendum_rotation, - ), - "formatter.zerotrim": ( - True, - _validate_bool, - "Whether to trim trailing decimal zeros on tick labels.", - ), - "formatter.log": ( - False, - _validate_bool, - "Whether to use log formatting (e.g., $10^{4}$) for " - "logarithmically scaled axis tick labels.", - ), - "formatter.limits": ( - [-5, 6], # must be list or else validated - _validate["axes.formatter.limits"], - "Alias for :rcraw:`axes.formatter.limits`.", - ), - "formatter.min_exponent": ( - 0, - _validate["axes.formatter.min_exponent"], - "Alias for :rcraw:`axes.formatter.min_exponent`.", - ), - "formatter.offset_threshold": ( - 4, - _validate["axes.formatter.offset_threshold"], - "Alias for :rcraw:`axes.formatter.offset_threshold`.", - ), - "formatter.use_locale": ( - False, - _validate_bool, - "Alias for :rcraw:`axes.formatter.use_locale`.", - ), - "formatter.use_mathtext": ( - MATHTEXT, - _validate_bool, - "Alias for :rcraw:`axes.formatter.use_mathtext`.", - ), - "formatter.use_offset": ( - True, - _validate_bool, - "Alias for :rcraw:`axes.formatter.useOffset`.", - ), - # Geographic axes settings - "geo.backend": ( - "cartopy", - _validate_belongs("cartopy", "basemap"), - "The backend used for `~ultraplot.axes.GeoAxes`. Must be " - "either 'cartopy' or 'basemap'.", - ), - "geo.extent": ( - "globe", - _validate_belongs("globe", "auto"), - "If ``'globe'``, the extent of cartopy `~ultraplot.axes.GeoAxes` is always " - "global. If ``'auto'``, the extent is automatically adjusted based on " - "plotted content. Default is ``'globe'``.", - ), - "geo.round": ( - True, - _validate_bool, - "If ``True`` (the default), polar `~ultraplot.axes.GeoAxes` like ``'npstere'`` " - "and ``'spstere'`` are bounded with circles rather than squares.", - ), - # Graphs - "graph.draw_nodes": ( - True, - _validate_bool_or_iterable, - "If ``True`` draws the nodes for all the nodes, otherwise only the nodes that are in the iterable.", - ), - "graph.draw_edges": ( - True, - _validate_bool_or_iterable, - "If ``True`` draws the edges for all the edges, otherwise only the edges that are in the iterable.", - ), - "graph.draw_labels": ( - False, - _validate_bool_or_iterable, - "If ``True`` draws the labels for all the nodes, otherwise only the nodes that are in the iterable.", - ), - "graph.draw_grid": ( - False, - _validate_bool, - "If ``True`` draws the grid for all the edges, otherwise only the edges that are in the iterable.", - ), - "graph.aspect": ( - "equal", - _validate_belongs("equal", "auto"), - "The aspect ratio of the graph.", - ), - "graph.facecolor": ("none", _validate_color, "The facecolor of the graph."), - "graph.draw_spines": ( - False, - _validate_bool_or_iterable, - "If ``True`` draws the spines for all the edges, otherwise only the edges that are in the iterable.", - ), - "graph.rescale": ( - True, - _validate_bool, - "If ``True`` rescales the graph to fit the data.", - ), - # Gridlines - # NOTE: Here 'grid' and 'gridminor' or *not* aliases for native 'axes.grid' and - # invented 'axes.gridminor' because native 'axes.grid' controls both major *and* - # minor gridlines. Must handle it independently from these settings. - "grid": (True, _validate_bool, "Toggle major gridlines on and off."), - "grid.below": ( - GRIDBELOW, # like axes.axisbelow - _validate_belongs(False, "line", True), - "Alias for :rcraw:`axes.axisbelow`. If ``True``, draw gridlines below " - "everything. If ``True``, draw them above everything. If ``'line'``, " - "draw them above patches but below lines and markers.", - ), - "grid.checkoverlap": ( - True, - _validate_bool, - "Whether to have cartopy automatically check for and remove overlapping " - "`~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.dmslabels": ( - True, - _validate_bool, - "Whether to use degrees-minutes-seconds rather than decimals for " - "cartopy `~ultraplot.axes.GeoAxes` gridlines.", - ), - "grid.geolabels": ( - True, - _validate_bool, - "Whether to include the ``'geo'`` spine in cartopy >= 0.20 when otherwise " - "toggling left, right, bottom, or top `~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.inlinelabels": ( - False, - _validate_bool, - "Whether to add inline labels for cartopy `~ultraplot.axes.GeoAxes` gridlines.", - ), - "grid.labels": ( - False, - _validate_bool, - "Whether to add outer labels for `~ultraplot.axes.GeoAxes` gridlines.", - ), - "grid.labelcolor": ( - BLACK, - _validate_color, - "Font color for `~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.labelpad": ( - GRIDPAD, - _validate_pt, - "Padding between the map boundary and cartopy `~ultraplot.axes.GeoAxes` " - "gridline labels." + _addendum_pt, - ), - "grid.labelsize": ( - SMALLSIZE, - _validate_fontsize, - "Font size for `~ultraplot.axes.GeoAxes` gridline labels." + _addendum_font, - ), - "grid.labelweight": ( - "normal", - _validate_fontweight, - "Font weight for `~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.nsteps": ( - 250, - _validate_int, - "Number of points used to draw cartopy `~ultraplot.axes.GeoAxes` gridlines.", - ), - "grid.pad": (GRIDPAD, _validate_pt, "Alias for :rcraw:`grid.labelpad`."), - "grid.rotatelabels": ( - False, # False limits projections where labels are available - _validate_bool, - "Whether to rotate cartopy `~ultraplot.axes.GeoAxes` gridline labels.", - ), - "grid.style": ( - "-", - _validate_linestyle, - "Major gridline style. Alias for :rcraw:`grid.linestyle`.", - ), - "grid.width": ( - LINEWIDTH, - _validate_pt, - "Major gridline width. Alias for :rcraw:`grid.linewidth`.", - ), - "grid.widthratio": ( - GRIDRATIO, - _validate_float, - "Ratio of minor gridline width to major gridline width.", - ), - # Minor gridlines - "gridminor": (False, _validate_bool, "Toggle minor gridlines on and off."), - "gridminor.alpha": (GRIDALPHA, _validate_float, "Minor gridline opacity."), - "gridminor.color": (BLACK, _validate_color, "Minor gridline color."), - "gridminor.linestyle": (GRIDSTYLE, _validate_linestyle, "Minor gridline style."), - "gridminor.linewidth": ( - GRIDRATIO * LINEWIDTH, - _validate_pt, - "Minor gridline width.", - ), - "gridminor.style": ( - GRIDSTYLE, - _validate_linestyle, - "Minor gridline style. Alias for :rcraw:`gridminor.linestyle`.", - ), - "gridminor.width": ( - GRIDRATIO * LINEWIDTH, - _validate_pt, - "Minor gridline width. Alias for :rcraw:`gridminor.linewidth`.", - ), - # Backend stuff - "inlineformat": ( - "retina", - _validate_belongs("svg", "pdf", "retina", "png", "jpeg"), - "The inline backend figure format. Valid formats include " - "``'svg'``, ``'pdf'``, ``'retina'``, ``'png'``, and ``jpeg``.", - ), - # Inner borders - "innerborders": ( - False, - _validate_bool, - "Toggles internal political border lines (e.g. states and provinces) " - "on and off.", - ), - "innerborders.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for internal political border lines", - ), - "innerborders.color": ( - BLACK, - _validate_color, - "Line color for internal political border lines.", - ), - "innerborders.linewidth": ( - LINEWIDTH, - _validate_pt, - "Line width for internal political border lines.", - ), - "innerborders.zorder": ( - ZLINES, - _validate_float, - "Z-order for internal political border lines.", - ), - # Axis label settings - "label.color": (BLACK, _validate_color, "Alias for :rcraw:`axes.labelcolor`."), - "label.pad": ( - LABELPAD, - _validate_pt, - "Alias for :rcraw:`axes.labelpad`." + _addendum_pt, - ), - "label.size": ( - SMALLSIZE, - _validate_fontsize, - "Alias for :rcraw:`axes.labelsize`." + _addendum_font, - ), - "label.weight": ( - "normal", - _validate_fontweight, - "Alias for :rcraw:`axes.labelweight`.", - ), - # Lake patches - "lakes": (False, _validate_bool, "Toggles lake patches on and off."), - "lakes.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for lake patches", - ), - "lakes.color": (WHITE, _validate_color, "Face color for lake patches."), - "lakes.zorder": (ZPATCHES, _validate_float, "Z-order for lake patches."), - "lakes.rasterized": ( - False, - _validate_bool, - "Toggles rasterization on or off for lake feature", - ), - # Land patches - "land": (False, _validate_bool, "Toggles land patches on and off."), - "land.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for land patches", - ), - "land.color": (BLACK, _validate_color, "Face color for land patches."), - "land.zorder": (ZPATCHES, _validate_float, "Z-order for land patches."), - "land.rasterized": ( - False, - _validate_bool, - "Toggles the rasterization of the land feature.", - ), - # Left subplot labels - "leftlabel.color": ( - BLACK, - _validate_color, - "Font color for row labels on the left-hand side.", - ), - "leftlabel.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and row labels on the left-hand side." - + _addendum_pt, - ), - "leftlabel.rotation": ( - "vertical", - _validate_rotation, - "Rotation for row labels on the left-hand side." + _addendum_rotation, - ), - "leftlabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for row labels on the left-hand side." + _addendum_font, - ), - "lollipop.markersize": ( - 36, - _validate_float, - "Size of lollipops in the lollipop plot.", - ), - "lollipop.stemcolor": ( - BLACK, - _validate_color, - "Color of lollipop lines.", - ), - "lollipop.stemwidth": ( - LINEWIDTH, - _validate_pt, - "Width of the stem", - ), - "lollipop.stemlinestyle": ( - "-", - _validate_linestyle, - "Line style of lollipop lines.", - ), - "leftlabel.weight": ( - "bold", - _validate_fontweight, - "Font weight for row labels on the left-hand side.", - ), - # Meta settings - "margin": ( - MARGIN, - _validate_float, - "The fractional *x* and *y* axis data margins when limits are unset. " - "Alias for :rcraw:`axes.margin`.", - ), - "meta.edgecolor": ( - BLACK, - _validate_color, - "Color of axis spines, tick marks, tick labels, and labels.", - ), - "meta.color": ( - BLACK, - _validate_color, - "Color of axis spines, tick marks, tick labels, and labels. " - "Alias for :rcraw:`meta.edgecolor`.", - ), - "meta.linewidth": ( - LINEWIDTH, - _validate_pt, - "Thickness of axis spines and major tick lines.", - ), - "meta.width": ( - LINEWIDTH, - _validate_pt, - "Thickness of axis spines and major tick lines. " - "Alias for :rcraw:`meta.linewidth`.", - ), - # For negative positive patches - "negcolor": ( - "blue7", - _validate_color, - "Color for negative bars and shaded areas when using ``negpos=True``. " - "See also :rcraw:`poscolor`.", - ), - "poscolor": ( - "red7", - _validate_color, - "Color for positive bars and shaded areas when using ``negpos=True``. " - "See also :rcraw:`negcolor`.", - ), - # Ocean patches - "ocean": (False, _validate_bool, "Toggles ocean patches on and off."), - "ocean.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for ocean patches", - ), - "ocean.color": (WHITE, _validate_color, "Face color for ocean patches."), - "ocean.zorder": (ZPATCHES, _validate_float, "Z-order for ocean patches."), - "ocean.rasterized": ( - False, - _validate_bool, - "Turns rasterization on or off for the oceans feature for GeoAxes.", - ), - # Geographic resolution - "reso": ( - "lo", - _validate_belongs("lo", "med", "hi", "x-hi", "xx-hi"), - "Resolution for `~ultraplot.axes.GeoAxes` geographic features. " - "Must be one of ``'lo'``, ``'med'``, ``'hi'``, ``'x-hi'``, or ``'xx-hi'``.", - ), - # Right subplot labels - "rightlabel.color": ( - BLACK, - _validate_color, - "Font color for row labels on the right-hand side.", - ), - "rightlabel.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and row labels on the right-hand side." - + _addendum_pt, - ), - "rightlabel.rotation": ( - "vertical", - _validate_rotation, - "Rotation for row labels on the right-hand side." + _addendum_rotation, - ), - "rightlabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for row labels on the right-hand side." + _addendum_font, - ), - "rightlabel.weight": ( - "bold", - _validate_fontweight, - "Font weight for row labels on the right-hand side.", - ), - # River lines - "rivers": (False, _validate_bool, "Toggles river lines on and off."), - "rivers.alpha": ( - None, - _validate_or_none(_validate_float), - "Opacity for river lines.", - ), - "rivers.color": (BLACK, _validate_color, "Line color for river lines."), - "rivers.linewidth": (LINEWIDTH, _validate_pt, "Line width for river lines."), - "rivers.zorder": (ZLINES, _validate_float, "Z-order for river lines."), - "rivers.rasterized": ( - False, - _validate_bool, - "Toggles rasterization on or off for rivers feature for GeoAxes.", - ), - # Circlize settings - "chord.start": ( - 0.0, - _validate_float, - "Start angle for chord diagrams.", - ), - "chord.end": ( - 360.0, - _validate_float, - "End angle for chord diagrams.", - ), - "chord.space": ( - 0.0, - _validate_float_or_iterable, - "Inter-sector spacing for chord diagrams.", - ), - "chord.endspace": ( - True, - _validate_bool, - "Whether to add an ending space gap for chord diagrams.", - ), - "chord.r_lim": ( - (97.0, 100.0), - _validate_tuple_float_2, - "Radial limits for chord diagrams.", - ), - "chord.ticks_interval": ( - None, - _validate_or_none(_validate_int), - "Tick interval for chord diagrams.", - ), - "chord.order": ( - None, - _validate_or_none(_validate_string_or_iterable), - "Ordering of sectors for chord diagrams.", - ), - "radar.r_lim": ( - (0.0, 100.0), - _validate_tuple_float_2, - "Radial limits for radar charts.", - ), - "radar.vmin": ( - 0.0, - _validate_float, - "Minimum value for radar charts.", - ), - "radar.vmax": ( - 100.0, - _validate_float, - "Maximum value for radar charts.", - ), - "radar.fill": ( - True, - _validate_bool, - "Whether to fill radar chart polygons.", - ), - "radar.marker_size": ( - 0, - _validate_int, - "Marker size for radar charts.", - ), - "radar.bg_color": ( - "#eeeeee80", - _validate_or_none(_validate_color), - "Background color for radar charts.", - ), - "radar.circular": ( - False, - _validate_bool, - "Whether to use circular radar charts.", - ), - "radar.show_grid_label": ( - True, - _validate_bool, - "Whether to show grid labels on radar charts.", - ), - "radar.grid_interval_ratio": ( - 0.2, - _validate_or_none(_validate_float), - "Grid interval ratio for radar charts.", - ), - "phylogeny.start": ( - 0.0, - _validate_float, - "Start angle for phylogeny plots.", - ), - "phylogeny.end": ( - 360.0, - _validate_float, - "End angle for phylogeny plots.", - ), - "phylogeny.r_lim": ( - (50.0, 100.0), - _validate_tuple_float_2, - "Radial limits for phylogeny plots.", - ), - "phylogeny.format": ( - "newick", - _validate_string, - "Input format for phylogeny plots.", - ), - "phylogeny.outer": ( - True, - _validate_bool, - "Whether to place phylogeny leaves on the outer edge.", - ), - "phylogeny.align_leaf_label": ( - True, - _validate_bool, - "Whether to align phylogeny leaf labels.", - ), - "phylogeny.ignore_branch_length": ( - False, - _validate_bool, - "Whether to ignore branch lengths in phylogeny plots.", - ), - "phylogeny.leaf_label_size": ( - None, - _validate_or_none(_validate_float), - "Leaf label font size for phylogeny plots.", - ), - "phylogeny.leaf_label_rmargin": ( - 2.0, - _validate_float, - "Radial margin for phylogeny leaf labels.", - ), - "phylogeny.reverse": ( - False, - _validate_bool, - "Whether to reverse phylogeny orientation.", - ), - "phylogeny.ladderize": ( - False, - _validate_bool, - "Whether to ladderize phylogeny branches.", - ), - # Sankey diagrams - "sankey.align": ( - "center", - _validate_belongs("center", "left", "right", "justify"), - "Horizontal alignment of nodes.", - ), - "sankey.connect": ( - (0, 0), - _validate_tuple_int_2, - "Connection path for Sankey diagram.", - ), - "sankey.flow_labels": ( - False, - _validate_bool, - "Whether to draw flow labels.", - ), - "sankey.flow_label_pos": ( - 0.5, - _validate_float, - "Position of flow labels along the flow.", - ), - "sankey.flow_sort": ( - True, - _validate_bool, - "Whether to sort flows.", - ), - "sankey.node_labels": ( - True, - _validate_bool, - "Whether to draw node labels.", - ), - "sankey.node_label_offset": ( - 0.01, - _validate_float, - "Offset for node labels.", - ), - "sankey.node_label_outside": ( - "auto", - _validate_bool_or_string, - "Position of node labels relative to the node.", - ), - "sankey.other_label": ( - "Other", - _validate_string, - "Label for 'other' category in Sankey diagram.", - ), - "sankey.pathlabel": ( - "", - _validate_string, - "Label for the patch.", - ), - "sankey.pathlengths": ( - 0.25, - _validate_float, - "Path lengths for Sankey diagram.", - ), - "sankey.rotation": ( - 0.0, - _validate_float, - "Rotation of the Sankey diagram.", - ), - "sankey.trunklength": ( - 1.0, - _validate_float, - "Trunk length for Sankey diagram.", - ), - # Subplots settings - "subplots.align": ( - False, - _validate_bool, - "Whether to align axis labels during draw. See `aligning labels " - "`__.", # noqa: E501 - ), - "subplots.equalspace": ( - False, - _validate_bool, - "Whether to make the tight layout algorithm assign the same space for " - "every row and the same space for every column.", - ), - "subplots.groupspace": ( - True, - _validate_bool, - "Whether to make the tight layout algorithm consider space between only " - 'adjacent subplot "groups" rather than every subplot in the row or column.', - ), - "subplots.innerpad": ( - 1, - _validate_em, - "Padding between adjacent subplots." + _addendum_em, - ), - "subplots.outerpad": ( - 0.5, - _validate_em, - "Padding around figure edge." + _addendum_em, - ), - "subplots.panelpad": ( - 0.5, - _validate_em, - "Padding between subplots and panels, and between stacked panels." - + _addendum_em, - ), - "subplots.panelwidth": (0.5, _validate_in, "Width of side panels." + _addendum_in), - "subplots.refwidth": ( - 2.5, - _validate_in, - "Default width of the reference subplot." + _addendum_in, - ), - "subplots.share": ( - "auto", - _validate_belongs( - 0, 1, 2, 3, 4, False, "labels", "limits", True, "all", "auto" - ), - "The axis sharing level, one of ``0``, ``1``, ``2``, or ``3``, or the " - "more intuitive aliases ``False``, ``'labels'``, ``'limits'``, ``True``, " - "or ``'auto'``. See `~ultraplot.figure.Figure` for details.", - ), - "subplots.span": ( - True, - _validate_bool, - "Toggles spanning axis labels. See `~ultraplot.ui.subplots` for details.", - ), - "subplots.tight": ( - True, - _validate_bool, - "Whether to auto-adjust the subplot spaces and figure margins.", - ), - "subplots.pixelsnap": ( - False, - _validate_bool, - "Whether to snap subplot bounds to the renderer pixel grid during draw.", - ), - # Super title settings - "suptitle.color": (BLACK, _validate_color, "Figure title color."), - "suptitle.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and the figure super title." + _addendum_pt, - ), - "suptitle.size": ( - LARGESIZE, - _validate_fontsize, - "Figure title font size." + _addendum_font, - ), - "suptitle.weight": ("bold", _validate_fontweight, "Figure title font weight."), - # Tick settings - "tick.color": (BLACK, _validate_color, "Major and minor tick color."), - "tick.dir": ( - TICKDIR, - _validate_belongs("in", "out", "inout"), - "Major and minor tick direction. Must be one of " - "``'out'``, ``'in'``, or ``'inout'``.", - ), - "tick.labelcolor": (BLACK, _validate_color, "Axis tick label color."), - "tick.labelpad": ( - TICKPAD, - _validate_pt, - "Padding between ticks and tick labels." + _addendum_pt, - ), - "tick.labelsize": ( - SMALLSIZE, - _validate_fontsize, - "Axis tick label font size." + _addendum_font, - ), - "tick.labelweight": ( - "normal", - _validate_fontweight, - "Axis tick label font weight.", - ), - "tick.len": (TICKLEN, _validate_pt, "Length of major ticks in points."), - "tick.lenratio": ( - TICKLENRATIO, - _validate_float, - "Ratio of minor tickline length to major tickline length.", - ), - "tick.linewidth": (LINEWIDTH, _validate_pt, "Major tickline width."), - "tick.minor": ( - TICKMINOR, - _validate_bool, - "Toggles minor ticks on and off.", - ), - "tick.pad": (TICKPAD, _validate_pt, "Alias for :rcraw:`tick.labelpad`."), - "tick.width": ( - LINEWIDTH, - _validate_pt, - "Major tickline width. Alias for :rcraw:`tick.linewidth`.", - ), - "tick.widthratio": ( - TICKWIDTHRATIO, - _validate_float, - "Ratio of minor tickline width to major tickline width.", - ), - # Title settings - "title.above": ( - True, - _validate_belongs(False, True, "panels"), - "Whether to move outer titles and a-b-c labels above panels, colorbars, or " - "legends that are above the axes. If the string 'panels' then text is only " - "redirected above axes panels. Otherwise should be boolean.", - ), - "title.border": ( - True, - _validate_bool, - "Whether to draw a white border around titles " - "when :rcraw:`title.loc` is inside the axes.", - ), - "title.borderwidth": (1.5, _validate_pt, "Width of the border around titles."), - "title.bbox": ( - False, - _validate_bool, - "Whether to draw semi-transparent bounding boxes around titles " - "when :rcraw:`title.loc` is inside the axes.", - ), - "title.bboxcolor": (WHITE, _validate_color, "Axes title bounding box color."), - "title.bboxstyle": ("square", _validate_boxstyle, "Axes title bounding box style."), - "title.bboxalpha": (0.5, _validate_float, "Axes title bounding box opacity."), - "title.bboxpad": ( - None, - _validate_or_none(_validate_pt), - "Padding for the title bounding box. By default this is scaled " - "to make the box flush against the axes edge." + _addendum_pt, - ), - "title.color": ( - BLACK, - _validate_color, - "Axes title color. Alias for :rcraw:`axes.titlecolor`.", - ), - "title.loc": ( - "center", - _validate_belongs(*TEXT_LOCS), - "Title position. For options see the :ref:`location table `.", - ), - "title.pad": ( - TITLEPAD, - _validate_pt, - "Padding between the axes edge and the inner and outer titles and " - "a-b-c labels. Alias for :rcraw:`axes.titlepad`." + _addendum_pt, - ), - "title.size": ( - LARGESIZE, - _validate_fontsize, - "Axes title font size. Alias for :rcraw:`axes.titlesize`." + _addendum_font, - ), - "title.weight": ( - "normal", - _validate_fontweight, - "Axes title font weight. Alias for :rcraw:`axes.titleweight`.", - ), - # Top subplot label settings - "toplabel.color": ( - BLACK, - _validate_color, - "Font color for column labels on the top of the figure.", - ), - "toplabel.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and column labels on the top of the figure." - + _addendum_pt, - ), - "toplabel.rotation": ( - "horizontal", - _validate_rotation, - "Rotation for column labels at the top of the figure." + _addendum_rotation, - ), - "toplabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for column labels on the top of the figure." + _addendum_font, - ), - "toplabel.weight": ( - "bold", - _validate_fontweight, - "Font weight for column labels on the top of the figure.", - ), - # Unit formatting - "unitformat": ( - "L", - _validate_string, - "The format string used to format `pint.Quantity` default unit labels " - "using ``format(units, unitformat)``. See also :rcraw:`autoformat`.", - ), - "ultraplot.check_for_latest_version": ( - False, - _validate_bool, - "Whether to check for the latest version of UltraPlot on PyPI when importing", - ), - "ultraplot.eager_import": ( - False, - _validate_bool, - "Whether to import the full public API during setup instead of lazily.", - ), + "external.shrink": ( + 0.9, + _validate_float, + "Default shrink factor for external axes containers.", + ), + # Stylesheet + "style": ( + None, + _validate_or_none(_validate_string), + "The default matplotlib `stylesheet " + "`__ " # noqa: E501 + "name. If ``None``, a custom ultraplot style is used. " + "If ``'default'``, the default matplotlib style is used.", + ), + # A-b-c labels + "abc": ( + False, + _validate_abc, + "If ``False`` then a-b-c labels are disabled. If ``True`` the default label " + "style `a` is used. If string this indicates the style and must contain the " + "character `a` or ``A``, for example ``'a.'`` or ``'(A)'``.", + ), + "abc.border": ( + True, + _validate_bool, + "Whether to draw a white border around a-b-c labels " + "when :rcraw:`abc.loc` is inside the axes.", + ), + "abc.borderwidth": ( + 1.5, + _validate_pt, + "Width of the white border around a-b-c labels.", + ), + "text.borderstyle": ( + "bevel", + _validate_joinstyle, + "Join style for text border strokes. Must be one of " + "``'miter'``, ``'round'``, or ``'bevel'``.", + ), + "text.curved.upright": ( + True, + _validate_bool, + "Whether curved text is flipped to remain upright by default.", + ), + "text.curved.ellipsis": ( + False, + _validate_bool, + "Whether to show ellipses when curved text exceeds path length.", + ), + "text.curved.avoid_overlap": ( + True, + _validate_bool, + "Whether curved text hides overlapping glyphs by default.", + ), + "text.curved.overlap_tol": ( + 0.1, + _validate_float, + "Overlap threshold used when hiding curved-text glyphs.", + ), + "text.curved.curvature_pad": ( + 2.0, + _validate_float, + "Extra curved-text glyph spacing per radian of local curvature.", + ), + "text.curved.min_advance": ( + 1.0, + _validate_float, + "Minimum extra curved-text glyph spacing in pixels.", + ), + "abc.bbox": ( + False, + _validate_bool, + "Whether to draw semi-transparent bounding boxes around a-b-c labels " + "when :rcraw:`abc.loc` is inside the axes.", + ), + "abc.bboxcolor": (WHITE, _validate_color, "a-b-c label bounding box color."), + "abc.bboxstyle": ( + "square", + _validate_boxstyle, + "a-b-c label bounding box style.", + ), + "abc.bboxalpha": (0.5, _validate_float, "a-b-c label bounding box opacity."), + "abc.bboxpad": ( + None, + _validate_or_none(_validate_pt), + "Padding for the a-b-c label bounding box. By default this is scaled " + "to make the box flush against the subplot edge." + _addendum_pt, + ), + "abc.color": (BLACK, _validate_color, "a-b-c label color."), + "abc.loc": ( + "left", # left side above the axes + _validate_belongs(*TEXT_LOCS), + "a-b-c label position. " + "For options see the :ref:`location table `.", + ), + "abc.size": ( + LARGESIZE, + _validate_fontsize, + "a-b-c label font size." + _addendum_font, + ), + "abc.titlepad": ( + LABELPAD, + _validate_pt, + "Padding separating the title and a-b-c label when in the same location." + + _addendum_pt, + ), + "abc.weight": ("bold", _validate_fontweight, "a-b-c label font weight."), + # Autoformatting + "autoformat": ( + True, + _validate_bool, + "Whether to automatically apply labels from `pandas.Series`, " + "`pandas.DataFrame`, and `xarray.DataArray` objects passed to " + "plotting functions. See also :rcraw:`unitformat`.", + ), + # Axes additions + "axes.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity of the background axes patch.", + ), + "axes.inbounds": ( + True, + _validate_bool, + "Whether to exclude out-of-bounds data when determining the default *y* (*x*) " + "axis limits and the *x* (*y*) axis limits have been locked.", + ), + "axes.margin": ( + MARGIN, + _validate_float, + "The fractional *x* and *y* axis margins when limits are unset.", + ), + "bar.bar_labels": ( + False, + _validate_bool, + "Add value of the bars to the bar labels", + ), + # Country borders + "borders": (False, _validate_bool, "Toggles country border lines on and off."), + "borders.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for country border lines.", + ), + "borders.color": ( + BLACK, + _validate_color, + "Line color for country border lines.", + ), + "borders.linewidth": ( + LINEWIDTH, + _validate_pt, + "Line width for country border lines.", + ), + "borders.zorder": ( + ZLINES, + _validate_float, + "Z-order for country border lines.", + ), + "borders.rasterized": ( + False, + _validate_bool, + "Toggles rasterization on or off for border feature in GeoAxes.", + ), + # Bottom subplot labels + "bottomlabel.color": ( + BLACK, + _validate_color, + "Font color for column labels on the bottom of the figure.", + ), + "bottomlabel.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and column labels on the bottom of the figure." + + _addendum_pt, + ), + "bottomlabel.rotation": ( + "horizontal", + _validate_rotation, + "Rotation for column labels at the bottom of the figure." + + _addendum_rotation, + ), + "bottomlabel.size": ( + LARGESIZE, + _validate_fontsize, + "Font size for column labels on the bottom of the figure." + _addendum_font, + ), + "bottomlabel.weight": ( + "bold", + _validate_fontweight, + "Font weight for column labels on the bottom of the figure.", + ), + "cftime.time_unit": ( + "days since 2000-01-01", + _validate_string, + "Time unit for non-Gregorian calendars.", + ), + "cftime.resolution": ( + "DAILY", + _validate_cftime_resolution, + "Default time resolution for non-Gregorian calendars.", + ), + "cftime.time_resolution_format": ( + { + "SECONDLY": "%S", + "MINUTELY": "%M", + "HOURLY": "%H", + "DAILY": "%d", + "MONTHLY": "%m", + "YEARLY": "%Y", + }, + _validate_cftime_resolution_format, + "Dict used for formatting non-Gregorian calendars.", + ), + "cftime.max_display_ticks": ( + 7, + _validate_int, + "Number of ticks to display for cftime units.", + ), + # Coastlines + "coast": (False, _validate_bool, "Toggles coastline lines on and off."), + "coast.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for coast lines", + ), + "coast.color": (BLACK, _validate_color, "Line color for coast lines."), + "coast.linewidth": (LINEWIDTH, _validate_pt, "Line width for coast lines."), + "coast.zorder": (ZLINES, _validate_float, "Z-order for coast lines."), + "coast.rasterized": ( + False, + _validate_bool, + "Toggles the rasterization of the coastlines feature for GeoAxes.", + ), + # Colorbars + "colorbar.center_levels": ( + False, + _validate_bool, + "Center the ticks in the center of each segment.", + ), + "colorbar.edgecolor": ( + BLACK, + _validate_color, + "Color for the inset colorbar frame edge.", + ), + "colorbar.extend": ( + 1.3, + _validate_em, + 'Length of rectangular or triangular "extensions" for panel colorbars.' + + _addendum_em, + ), + "colorbar.outline": ( + True, + _validate_bool, + "Whether to draw a frame around the colorbar.", + ), + "colorbar.labelrotation": ( + "auto", + _validate_float_or_auto, + "Rotation of colorbar labels.", + ), + "colorbar.fancybox": ( + False, + _validate_bool, + 'Whether to use a "fancy" round bounding box for inset colorbar frames.', + ), + "colorbar.framealpha": ( + FRAMEALPHA, + _validate_float, + "Opacity for inset colorbar frames.", + ), + "colorbar.facecolor": ( + WHITE, + _validate_color, + "Color for the inset colorbar frame.", + ), + "colorbar.frameon": ( + True, + _validate_bool, + "Whether to draw a frame behind inset colorbars.", + ), + "colorbar.grid": ( + False, + _validate_bool, + "Whether to draw borders between each level of the colorbar.", + ), + "colorbar.insetextend": ( + 0.9, + _validate_em, + 'Length of rectangular or triangular "extensions" for inset colorbars.' + + _addendum_em, + ), + "colorbar.insetlength": ( + 8, + _validate_em, + "Length of inset colorbars." + _addendum_em, + ), + "colorbar.insetpad": ( + 0.7, + _validate_em, + "Padding between axes edge and inset colorbars." + _addendum_em, + ), + "colorbar.insetwidth": ( + 1.2, + _validate_em, + "Width of inset colorbars." + _addendum_em, + ), + "colorbar.length": (1, _validate_em, "Length of outer colorbars."), + "colorbar.loc": ( + "right", + _validate_belongs(*COLORBAR_LOCS), + "Inset colorbar location. " + "For options see the :ref:`location table `.", + ), + "colorbar.width": ( + 0.2, + _validate_in, + "Width of outer colorbars." + _addendum_in, + ), + "colorbar.rasterized": ( + False, + _validate_bool, + "Whether to use rasterization for colorbar solids.", + ), + "colorbar.shadow": ( + False, + _validate_bool, + "Whether to add a shadow underneath inset colorbar frames.", + ), + # Color cycle additions + "cycle": ( + CYCLE, + _validate_cmap("discrete", cycle=True), + "Name of the color cycle assigned to :rcraw:`axes.prop_cycle`.", + ), + # Colormap additions + "cmap": ( + CMAPSEQ, + _validate_cmap("continuous"), + "Alias for :rcraw:`cmap.sequential` and :rcraw:`image.cmap`.", + ), + "cmap.autodiverging": ( + True, + _validate_bool, + "Whether to automatically apply a diverging colormap and " + "normalizer based on the data.", + ), + "cmap.qualitative": ( + CMAPCAT, + _validate_cmap("discrete"), + "Default colormap for qualitative datasets.", + ), + "cmap.cyclic": ( + CMAPCYC, + _validate_cmap("continuous"), + "Default colormap for cyclic datasets.", + ), + "cmap.discrete": ( + None, + _validate_or_none(_validate_bool), + "If ``True``, `~ultraplot.colors.DiscreteNorm` is used for every colormap plot. " + "If ``False``, it is never used. If ``None``, it is used for all plot types " + "except `imshow`, `matshow`, `spy`, `hexbin`, and `hist2d`.", + ), + "cmap.diverging": ( + CMAPDIV, + _validate_cmap("continuous"), + "Default colormap for diverging datasets.", + ), + "cmap.inbounds": ( + True, + _validate_bool, + "If ``True`` and the *x* and *y* axis limits are fixed, only in-bounds data " + "is considered when determining the default colormap `vmin` and `vmax`.", + ), + "cmap.levels": ( + 11, + _validate_int, + "Default number of `~ultraplot.colors.DiscreteNorm` levels for plotting " + "commands that use colormaps.", + ), + "cmap.listedthresh": ( + 64, + _validate_int, + "Native `~matplotlib.colors.ListedColormap`\\ s with more colors than " + "this are converted to :class:`~ultraplot.colors.ContinuousColormap` rather than " + ":class:`~ultraplot.colors.DiscreteColormap`. This helps translate continuous " + "colormaps from external projects.", + ), + "cmap.lut": ( + 256, + _validate_int, + "Number of colors in the colormap lookup table. " + "Alias for :rcraw:`image.lut`.", + ), + "cmap.robust": ( + False, + _validate_bool, + "If ``True``, the default colormap `vmin` and `vmax` are chosen using the " + "2nd to 98th percentiles rather than the minimum and maximum.", + ), + "cmap.sequential": ( + CMAPSEQ, + _validate_cmap("continuous"), + "Default colormap for sequential datasets. Alias for :rcraw:`image.cmap`.", + ), + # Special setting + "edgefix": ( + True, + _validate_bool, + 'Whether to fix issues with "white lines" appearing between patches ' + "in saved vector graphics and with vector graphic backends. Applies " + "to colorbar levels and bar, area, pcolor, and contour plots.", + ), + # Font settings + "font.name": (FONTNAME, _validate_fontname, "Alias for :rcraw:`font.family`."), + "font.small": ( + SMALLSIZE, + _validate_fontsize, + "Alias for :rcraw:`font.smallsize`.", + ), + "font.smallsize": ( + SMALLSIZE, + _validate_fontsize, + "Meta setting that changes the label-like sizes ``axes.labelsize``, " + "``legend.fontsize``, ``tick.labelsize``, and ``grid.labelsize``. Default is " + "``'medium'`` (equivalent to :rcraw:`font.size`)." + _addendum_font, + ), + "font.large": ( + LARGESIZE, + _validate_fontsize, + "Alias for :rcraw:`font.largesize`.", + ), + "font.largesize": ( + LARGESIZE, + _validate_fontsize, + "Meta setting that changes the title-like sizes ``abc.size``, ``title.size``, " + "``suptitle.size``, ``leftlabel.size``, ``rightlabel.size``, etc. Default is " + "``'med-large'`` (i.e. 1.1 times :rcraw:`font.size`)." + _addendum_font, + ), + # Formatter settings + "formatter.timerotation": ( + "vertical", + _validate_rotation, + "Rotation for *x* axis datetime tick labels." + _addendum_rotation, + ), + "formatter.zerotrim": ( + True, + _validate_bool, + "Whether to trim trailing decimal zeros on tick labels.", + ), + "formatter.log": ( + False, + _validate_bool, + "Whether to use log formatting (e.g., $10^{4}$) for " + "logarithmically scaled axis tick labels.", + ), + "formatter.limits": ( + [-5, 6], # must be list or else validated + _validate["axes.formatter.limits"], + "Alias for :rcraw:`axes.formatter.limits`.", + ), + "formatter.min_exponent": ( + 0, + _validate["axes.formatter.min_exponent"], + "Alias for :rcraw:`axes.formatter.min_exponent`.", + ), + "formatter.offset_threshold": ( + 4, + _validate["axes.formatter.offset_threshold"], + "Alias for :rcraw:`axes.formatter.offset_threshold`.", + ), + "formatter.use_locale": ( + False, + _validate_bool, + "Alias for :rcraw:`axes.formatter.use_locale`.", + ), + "formatter.use_mathtext": ( + MATHTEXT, + _validate_bool, + "Alias for :rcraw:`axes.formatter.use_mathtext`.", + ), + "formatter.use_offset": ( + True, + _validate_bool, + "Alias for :rcraw:`axes.formatter.useOffset`.", + ), + # Geographic axes settings + "geo.backend": ( + "cartopy", + _validate_belongs("cartopy", "basemap"), + "The backend used for `~ultraplot.axes.GeoAxes`. Must be " + "either 'cartopy' or 'basemap'.", + ), + "geo.extent": ( + "globe", + _validate_belongs("globe", "auto"), + "If ``'globe'``, the extent of cartopy `~ultraplot.axes.GeoAxes` is always " + "global. If ``'auto'``, the extent is automatically adjusted based on " + "plotted content. Default is ``'globe'``.", + ), + "geo.round": ( + True, + _validate_bool, + "If ``True`` (the default), polar `~ultraplot.axes.GeoAxes` like ``'npstere'`` " + "and ``'spstere'`` are bounded with circles rather than squares.", + ), + # Graphs + "graph.draw_nodes": ( + True, + _validate_bool_or_iterable, + "If ``True`` draws the nodes for all the nodes, otherwise only the nodes that are in the iterable.", + ), + "graph.draw_edges": ( + True, + _validate_bool_or_iterable, + "If ``True`` draws the edges for all the edges, otherwise only the edges that are in the iterable.", + ), + "graph.draw_labels": ( + False, + _validate_bool_or_iterable, + "If ``True`` draws the labels for all the nodes, otherwise only the nodes that are in the iterable.", + ), + "graph.draw_grid": ( + False, + _validate_bool, + "If ``True`` draws the grid for all the edges, otherwise only the edges that are in the iterable.", + ), + "graph.aspect": ( + "equal", + _validate_belongs("equal", "auto"), + "The aspect ratio of the graph.", + ), + "graph.facecolor": ("none", _validate_color, "The facecolor of the graph."), + "graph.draw_spines": ( + False, + _validate_bool_or_iterable, + "If ``True`` draws the spines for all the edges, otherwise only the edges that are in the iterable.", + ), + "graph.rescale": ( + True, + _validate_bool, + "If ``True`` rescales the graph to fit the data.", + ), + # Gridlines + # NOTE: Here 'grid' and 'gridminor' or *not* aliases for native 'axes.grid' and + # invented 'axes.gridminor' because native 'axes.grid' controls both major *and* + # minor gridlines. Must handle it independently from these settings. + "grid": (True, _validate_bool, "Toggle major gridlines on and off."), + "grid.below": ( + GRIDBELOW, # like axes.axisbelow + _validate_belongs(False, "line", True), + "Alias for :rcraw:`axes.axisbelow`. If ``True``, draw gridlines below " + "everything. If ``True``, draw them above everything. If ``'line'``, " + "draw them above patches but below lines and markers.", + ), + "grid.checkoverlap": ( + True, + _validate_bool, + "Whether to have cartopy automatically check for and remove overlapping " + "`~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.dmslabels": ( + True, + _validate_bool, + "Whether to use degrees-minutes-seconds rather than decimals for " + "cartopy `~ultraplot.axes.GeoAxes` gridlines.", + ), + "grid.geolabels": ( + True, + _validate_bool, + "Whether to include the ``'geo'`` spine in cartopy >= 0.20 when otherwise " + "toggling left, right, bottom, or top `~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.inlinelabels": ( + False, + _validate_bool, + "Whether to add inline labels for cartopy `~ultraplot.axes.GeoAxes` gridlines.", + ), + "grid.labels": ( + False, + _validate_bool, + "Whether to add outer labels for `~ultraplot.axes.GeoAxes` gridlines.", + ), + "grid.labelcolor": ( + BLACK, + _validate_color, + "Font color for `~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.labelpad": ( + GRIDPAD, + _validate_pt, + "Padding between the map boundary and cartopy `~ultraplot.axes.GeoAxes` " + "gridline labels." + _addendum_pt, + ), + "grid.labelsize": ( + SMALLSIZE, + _validate_fontsize, + "Font size for `~ultraplot.axes.GeoAxes` gridline labels." + _addendum_font, + ), + "grid.labelweight": ( + "normal", + _validate_fontweight, + "Font weight for `~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.nsteps": ( + 250, + _validate_int, + "Number of points used to draw cartopy `~ultraplot.axes.GeoAxes` gridlines.", + ), + "grid.pad": (GRIDPAD, _validate_pt, "Alias for :rcraw:`grid.labelpad`."), + "grid.rotatelabels": ( + False, # False limits projections where labels are available + _validate_bool, + "Whether to rotate cartopy `~ultraplot.axes.GeoAxes` gridline labels.", + ), + "grid.style": ( + "-", + _validate_linestyle, + "Major gridline style. Alias for :rcraw:`grid.linestyle`.", + ), + "grid.width": ( + LINEWIDTH, + _validate_pt, + "Major gridline width. Alias for :rcraw:`grid.linewidth`.", + ), + "grid.widthratio": ( + GRIDRATIO, + _validate_float, + "Ratio of minor gridline width to major gridline width.", + ), + # Minor gridlines + "gridminor": (False, _validate_bool, "Toggle minor gridlines on and off."), + "gridminor.alpha": (GRIDALPHA, _validate_float, "Minor gridline opacity."), + "gridminor.color": (BLACK, _validate_color, "Minor gridline color."), + "gridminor.linestyle": ( + GRIDSTYLE, + _validate_linestyle, + "Minor gridline style.", + ), + "gridminor.linewidth": ( + GRIDRATIO * LINEWIDTH, + _validate_pt, + "Minor gridline width.", + ), + "gridminor.style": ( + GRIDSTYLE, + _validate_linestyle, + "Minor gridline style. Alias for :rcraw:`gridminor.linestyle`.", + ), + "gridminor.width": ( + GRIDRATIO * LINEWIDTH, + _validate_pt, + "Minor gridline width. Alias for :rcraw:`gridminor.linewidth`.", + ), + # Backend stuff + "inlineformat": ( + "retina", + _validate_belongs("svg", "pdf", "retina", "png", "jpeg"), + "The inline backend figure format. Valid formats include " + "``'svg'``, ``'pdf'``, ``'retina'``, ``'png'``, and ``jpeg``.", + ), + # Inner borders + "innerborders": ( + False, + _validate_bool, + "Toggles internal political border lines (e.g. states and provinces) " + "on and off.", + ), + "innerborders.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for internal political border lines", + ), + "innerborders.color": ( + BLACK, + _validate_color, + "Line color for internal political border lines.", + ), + "innerborders.linewidth": ( + LINEWIDTH, + _validate_pt, + "Line width for internal political border lines.", + ), + "innerborders.zorder": ( + ZLINES, + _validate_float, + "Z-order for internal political border lines.", + ), + # Axis label settings + "label.color": (BLACK, _validate_color, "Alias for :rcraw:`axes.labelcolor`."), + "label.pad": ( + LABELPAD, + _validate_pt, + "Alias for :rcraw:`axes.labelpad`." + _addendum_pt, + ), + "label.size": ( + SMALLSIZE, + _validate_fontsize, + "Alias for :rcraw:`axes.labelsize`." + _addendum_font, + ), + "label.weight": ( + "normal", + _validate_fontweight, + "Alias for :rcraw:`axes.labelweight`.", + ), + # Lake patches + "lakes": (False, _validate_bool, "Toggles lake patches on and off."), + "lakes.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for lake patches", + ), + "lakes.color": (WHITE, _validate_color, "Face color for lake patches."), + "lakes.zorder": (ZPATCHES, _validate_float, "Z-order for lake patches."), + "lakes.rasterized": ( + False, + _validate_bool, + "Toggles rasterization on or off for lake feature", + ), + # Land patches + "land": (False, _validate_bool, "Toggles land patches on and off."), + "land.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for land patches", + ), + "land.color": (BLACK, _validate_color, "Face color for land patches."), + "land.zorder": (ZPATCHES, _validate_float, "Z-order for land patches."), + "land.rasterized": ( + False, + _validate_bool, + "Toggles the rasterization of the land feature.", + ), + # Left subplot labels + "leftlabel.color": ( + BLACK, + _validate_color, + "Font color for row labels on the left-hand side.", + ), + "leftlabel.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and row labels on the left-hand side." + + _addendum_pt, + ), + "leftlabel.rotation": ( + "vertical", + _validate_rotation, + "Rotation for row labels on the left-hand side." + _addendum_rotation, + ), + "leftlabel.size": ( + LARGESIZE, + _validate_fontsize, + "Font size for row labels on the left-hand side." + _addendum_font, + ), + "lollipop.markersize": ( + 36, + _validate_float, + "Size of lollipops in the lollipop plot.", + ), + "lollipop.stemcolor": ( + BLACK, + _validate_color, + "Color of lollipop lines.", + ), + "lollipop.stemwidth": ( + LINEWIDTH, + _validate_pt, + "Width of the stem", + ), + "lollipop.stemlinestyle": ( + "-", + _validate_linestyle, + "Line style of lollipop lines.", + ), + "leftlabel.weight": ( + "bold", + _validate_fontweight, + "Font weight for row labels on the left-hand side.", + ), + # Meta settings + "margin": ( + MARGIN, + _validate_float, + "The fractional *x* and *y* axis data margins when limits are unset. " + "Alias for :rcraw:`axes.margin`.", + ), + "meta.edgecolor": ( + BLACK, + _validate_color, + "Color of axis spines, tick marks, tick labels, and labels.", + ), + "meta.color": ( + BLACK, + _validate_color, + "Color of axis spines, tick marks, tick labels, and labels. " + "Alias for :rcraw:`meta.edgecolor`.", + ), + "meta.linewidth": ( + LINEWIDTH, + _validate_pt, + "Thickness of axis spines and major tick lines.", + ), + "meta.width": ( + LINEWIDTH, + _validate_pt, + "Thickness of axis spines and major tick lines. " + "Alias for :rcraw:`meta.linewidth`.", + ), + # For negative positive patches + "negcolor": ( + "blue7", + _validate_color, + "Color for negative bars and shaded areas when using ``negpos=True``. " + "See also :rcraw:`poscolor`.", + ), + "poscolor": ( + "red7", + _validate_color, + "Color for positive bars and shaded areas when using ``negpos=True``. " + "See also :rcraw:`negcolor`.", + ), + # Ocean patches + "ocean": (False, _validate_bool, "Toggles ocean patches on and off."), + "ocean.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for ocean patches", + ), + "ocean.color": (WHITE, _validate_color, "Face color for ocean patches."), + "ocean.zorder": (ZPATCHES, _validate_float, "Z-order for ocean patches."), + "ocean.rasterized": ( + False, + _validate_bool, + "Turns rasterization on or off for the oceans feature for GeoAxes.", + ), + # Geographic resolution + "reso": ( + "lo", + _validate_belongs("lo", "med", "hi", "x-hi", "xx-hi"), + "Resolution for `~ultraplot.axes.GeoAxes` geographic features. " + "Must be one of ``'lo'``, ``'med'``, ``'hi'``, ``'x-hi'``, or ``'xx-hi'``.", + ), + # Right subplot labels + "rightlabel.color": ( + BLACK, + _validate_color, + "Font color for row labels on the right-hand side.", + ), + "rightlabel.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and row labels on the right-hand side." + + _addendum_pt, + ), + "rightlabel.rotation": ( + "vertical", + _validate_rotation, + "Rotation for row labels on the right-hand side." + _addendum_rotation, + ), + "rightlabel.size": ( + LARGESIZE, + _validate_fontsize, + "Font size for row labels on the right-hand side." + _addendum_font, + ), + "rightlabel.weight": ( + "bold", + _validate_fontweight, + "Font weight for row labels on the right-hand side.", + ), + # River lines + "rivers": (False, _validate_bool, "Toggles river lines on and off."), + "rivers.alpha": ( + None, + _validate_or_none(_validate_float), + "Opacity for river lines.", + ), + "rivers.color": (BLACK, _validate_color, "Line color for river lines."), + "rivers.linewidth": (LINEWIDTH, _validate_pt, "Line width for river lines."), + "rivers.zorder": (ZLINES, _validate_float, "Z-order for river lines."), + "rivers.rasterized": ( + False, + _validate_bool, + "Toggles rasterization on or off for rivers feature for GeoAxes.", + ), + # Circlize settings + "chord.start": ( + 0.0, + _validate_float, + "Start angle for chord diagrams.", + ), + "chord.end": ( + 360.0, + _validate_float, + "End angle for chord diagrams.", + ), + "chord.space": ( + 0.0, + _validate_float_or_iterable, + "Inter-sector spacing for chord diagrams.", + ), + "chord.endspace": ( + True, + _validate_bool, + "Whether to add an ending space gap for chord diagrams.", + ), + "chord.r_lim": ( + (97.0, 100.0), + _validate_tuple_float_2, + "Radial limits for chord diagrams.", + ), + "chord.ticks_interval": ( + None, + _validate_or_none(_validate_int), + "Tick interval for chord diagrams.", + ), + "chord.order": ( + None, + _validate_or_none(_validate_string_or_iterable), + "Ordering of sectors for chord diagrams.", + ), + "radar.r_lim": ( + (0.0, 100.0), + _validate_tuple_float_2, + "Radial limits for radar charts.", + ), + "radar.vmin": ( + 0.0, + _validate_float, + "Minimum value for radar charts.", + ), + "radar.vmax": ( + 100.0, + _validate_float, + "Maximum value for radar charts.", + ), + "radar.fill": ( + True, + _validate_bool, + "Whether to fill radar chart polygons.", + ), + "radar.marker_size": ( + 0, + _validate_int, + "Marker size for radar charts.", + ), + "radar.bg_color": ( + "#eeeeee80", + _validate_or_none(_validate_color), + "Background color for radar charts.", + ), + "radar.circular": ( + False, + _validate_bool, + "Whether to use circular radar charts.", + ), + "radar.show_grid_label": ( + True, + _validate_bool, + "Whether to show grid labels on radar charts.", + ), + "radar.grid_interval_ratio": ( + 0.2, + _validate_or_none(_validate_float), + "Grid interval ratio for radar charts.", + ), + "phylogeny.start": ( + 0.0, + _validate_float, + "Start angle for phylogeny plots.", + ), + "phylogeny.end": ( + 360.0, + _validate_float, + "End angle for phylogeny plots.", + ), + "phylogeny.r_lim": ( + (50.0, 100.0), + _validate_tuple_float_2, + "Radial limits for phylogeny plots.", + ), + "phylogeny.format": ( + "newick", + _validate_string, + "Input format for phylogeny plots.", + ), + "phylogeny.outer": ( + True, + _validate_bool, + "Whether to place phylogeny leaves on the outer edge.", + ), + "phylogeny.align_leaf_label": ( + True, + _validate_bool, + "Whether to align phylogeny leaf labels.", + ), + "phylogeny.ignore_branch_length": ( + False, + _validate_bool, + "Whether to ignore branch lengths in phylogeny plots.", + ), + "phylogeny.leaf_label_size": ( + None, + _validate_or_none(_validate_float), + "Leaf label font size for phylogeny plots.", + ), + "phylogeny.leaf_label_rmargin": ( + 2.0, + _validate_float, + "Radial margin for phylogeny leaf labels.", + ), + "phylogeny.reverse": ( + False, + _validate_bool, + "Whether to reverse phylogeny orientation.", + ), + "phylogeny.ladderize": ( + False, + _validate_bool, + "Whether to ladderize phylogeny branches.", + ), + # Sankey diagrams + "sankey.align": ( + "center", + _validate_belongs("center", "left", "right", "justify"), + "Horizontal alignment of nodes.", + ), + "sankey.connect": ( + (0, 0), + _validate_tuple_int_2, + "Connection path for Sankey diagram.", + ), + "sankey.flow_labels": ( + False, + _validate_bool, + "Whether to draw flow labels.", + ), + "sankey.flow_label_pos": ( + 0.5, + _validate_float, + "Position of flow labels along the flow.", + ), + "sankey.flow_sort": ( + True, + _validate_bool, + "Whether to sort flows.", + ), + "sankey.node_labels": ( + True, + _validate_bool, + "Whether to draw node labels.", + ), + "sankey.node_label_offset": ( + 0.01, + _validate_float, + "Offset for node labels.", + ), + "sankey.node_label_outside": ( + "auto", + _validate_bool_or_string, + "Position of node labels relative to the node.", + ), + "sankey.other_label": ( + "Other", + _validate_string, + "Label for 'other' category in Sankey diagram.", + ), + "sankey.pathlabel": ( + "", + _validate_string, + "Label for the patch.", + ), + "sankey.pathlengths": ( + 0.25, + _validate_float, + "Path lengths for Sankey diagram.", + ), + "sankey.rotation": ( + 0.0, + _validate_float, + "Rotation of the Sankey diagram.", + ), + "sankey.trunklength": ( + 1.0, + _validate_float, + "Trunk length for Sankey diagram.", + ), + # Subplots settings + "subplots.align": ( + False, + _validate_bool, + "Whether to align axis labels during draw. See `aligning labels " + "`__.", # noqa: E501 + ), + "subplots.equalspace": ( + False, + _validate_bool, + "Whether to make the tight layout algorithm assign the same space for " + "every row and the same space for every column.", + ), + "subplots.groupspace": ( + True, + _validate_bool, + "Whether to make the tight layout algorithm consider space between only " + 'adjacent subplot "groups" rather than every subplot in the row or column.', + ), + "subplots.innerpad": ( + 1, + _validate_em, + "Padding between adjacent subplots." + _addendum_em, + ), + "subplots.outerpad": ( + 0.5, + _validate_em, + "Padding around figure edge." + _addendum_em, + ), + "subplots.panelpad": ( + 0.5, + _validate_em, + "Padding between subplots and panels, and between stacked panels." + + _addendum_em, + ), + "subplots.panelwidth": ( + 0.5, + _validate_in, + "Width of side panels." + _addendum_in, + ), + "subplots.refwidth": ( + 2.5, + _validate_in, + "Default width of the reference subplot." + _addendum_in, + ), + "subplots.share": ( + "auto", + _validate_belongs( + 0, 1, 2, 3, 4, False, "labels", "limits", True, "all", "auto" + ), + "The axis sharing level, one of ``0``, ``1``, ``2``, or ``3``, or the " + "more intuitive aliases ``False``, ``'labels'``, ``'limits'``, ``True``, " + "or ``'auto'``. See `~ultraplot.figure.Figure` for details.", + ), + "subplots.span": ( + True, + _validate_bool, + "Toggles spanning axis labels. See `~ultraplot.ui.subplots` for details.", + ), + "subplots.tight": ( + True, + _validate_bool, + "Whether to auto-adjust the subplot spaces and figure margins.", + ), + "subplots.pixelsnap": ( + False, + _validate_bool, + "Whether to snap subplot bounds to the renderer pixel grid during draw.", + ), + # Super title settings + "suptitle.color": (BLACK, _validate_color, "Figure title color."), + "suptitle.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and the figure super title." + _addendum_pt, + ), + "suptitle.size": ( + LARGESIZE, + _validate_fontsize, + "Figure title font size." + _addendum_font, + ), + "suptitle.weight": ("bold", _validate_fontweight, "Figure title font weight."), + # Tick settings + "tick.color": (BLACK, _validate_color, "Major and minor tick color."), + "tick.dir": ( + TICKDIR, + _validate_belongs("in", "out", "inout"), + "Major and minor tick direction. Must be one of " + "``'out'``, ``'in'``, or ``'inout'``.", + ), + "tick.labelcolor": (BLACK, _validate_color, "Axis tick label color."), + "tick.labelpad": ( + TICKPAD, + _validate_pt, + "Padding between ticks and tick labels." + _addendum_pt, + ), + "tick.labelsize": ( + SMALLSIZE, + _validate_fontsize, + "Axis tick label font size." + _addendum_font, + ), + "tick.labelweight": ( + "normal", + _validate_fontweight, + "Axis tick label font weight.", + ), + "tick.len": (TICKLEN, _validate_pt, "Length of major ticks in points."), + "tick.lenratio": ( + TICKLENRATIO, + _validate_float, + "Ratio of minor tickline length to major tickline length.", + ), + "tick.linewidth": (LINEWIDTH, _validate_pt, "Major tickline width."), + "tick.minor": ( + TICKMINOR, + _validate_bool, + "Toggles minor ticks on and off.", + ), + "tick.pad": (TICKPAD, _validate_pt, "Alias for :rcraw:`tick.labelpad`."), + "tick.width": ( + LINEWIDTH, + _validate_pt, + "Major tickline width. Alias for :rcraw:`tick.linewidth`.", + ), + "tick.widthratio": ( + TICKWIDTHRATIO, + _validate_float, + "Ratio of minor tickline width to major tickline width.", + ), + # Title settings + "title.above": ( + True, + _validate_belongs(False, True, "panels"), + "Whether to move outer titles and a-b-c labels above panels, colorbars, or " + "legends that are above the axes. If the string 'panels' then text is only " + "redirected above axes panels. Otherwise should be boolean.", + ), + "title.border": ( + True, + _validate_bool, + "Whether to draw a white border around titles " + "when :rcraw:`title.loc` is inside the axes.", + ), + "title.borderwidth": (1.5, _validate_pt, "Width of the border around titles."), + "title.bbox": ( + False, + _validate_bool, + "Whether to draw semi-transparent bounding boxes around titles " + "when :rcraw:`title.loc` is inside the axes.", + ), + "title.bboxcolor": (WHITE, _validate_color, "Axes title bounding box color."), + "title.bboxstyle": ( + "square", + _validate_boxstyle, + "Axes title bounding box style.", + ), + "title.bboxalpha": (0.5, _validate_float, "Axes title bounding box opacity."), + "title.bboxpad": ( + None, + _validate_or_none(_validate_pt), + "Padding for the title bounding box. By default this is scaled " + "to make the box flush against the axes edge." + _addendum_pt, + ), + "title.color": ( + BLACK, + _validate_color, + "Axes title color. Alias for :rcraw:`axes.titlecolor`.", + ), + "title.loc": ( + "center", + _validate_belongs(*TEXT_LOCS), + "Title position. For options see the :ref:`location table `.", + ), + "title.pad": ( + TITLEPAD, + _validate_pt, + "Padding between the axes edge and the inner and outer titles and " + "a-b-c labels. Alias for :rcraw:`axes.titlepad`." + _addendum_pt, + ), + "title.size": ( + LARGESIZE, + _validate_fontsize, + "Axes title font size. Alias for :rcraw:`axes.titlesize`." + _addendum_font, + ), + "title.weight": ( + "normal", + _validate_fontweight, + "Axes title font weight. Alias for :rcraw:`axes.titleweight`.", + ), + # Top subplot label settings + "toplabel.color": ( + BLACK, + _validate_color, + "Font color for column labels on the top of the figure.", + ), + "toplabel.pad": ( + TITLEPAD, + _validate_pt, + "Padding between axes content and column labels on the top of the figure." + + _addendum_pt, + ), + "toplabel.rotation": ( + "horizontal", + _validate_rotation, + "Rotation for column labels at the top of the figure." + _addendum_rotation, + ), + "toplabel.size": ( + LARGESIZE, + _validate_fontsize, + "Font size for column labels on the top of the figure." + _addendum_font, + ), + "toplabel.weight": ( + "bold", + _validate_fontweight, + "Font weight for column labels on the top of the figure.", + ), + # Unit formatting + "unitformat": ( + "L", + _validate_string, + "The format string used to format `pint.Quantity` default unit labels " + "using ``format(units, unitformat)``. See also :rcraw:`autoformat`.", + ), + "ultraplot.check_for_latest_version": ( + False, + _validate_bool, + "Whether to check for the latest version of UltraPlot on PyPI when importing", + ), + "ultraplot.eager_import": ( + False, + _validate_bool, + "Whether to import the full public API during setup instead of lazily.", + ), } From 10890f316d470e5a3f810c22bc15fb419654a979 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 12 Feb 2026 08:55:24 +1000 Subject: [PATCH 5/9] Simplify rc architecture to single settings table with validator aliases --- ultraplot/internals/rc/__init__.py | 20 +--- ultraplot/internals/rc/axes.py | 61 ------------ ultraplot/internals/rc/plot_types.py | 92 ------------------- ultraplot/internals/rc/ribbon.py | 35 ------- ultraplot/internals/rc/sankey.py | 28 ------ .../internals/rc/{core.py => settings.py} | 78 +++++++++++++++- ultraplot/internals/rc/subplots.py | 22 ----- ultraplot/internals/rc/text.py | 34 ------- ultraplot/internals/rc/validators.py | 23 +++++ ultraplot/internals/rcsetup.py | 15 +-- ultraplot/tests/test_config.py | 48 ++++------ 11 files changed, 123 insertions(+), 333 deletions(-) delete mode 100644 ultraplot/internals/rc/axes.py delete mode 100644 ultraplot/internals/rc/plot_types.py delete mode 100644 ultraplot/internals/rc/ribbon.py delete mode 100644 ultraplot/internals/rc/sankey.py rename ultraplot/internals/rc/{core.py => settings.py} (95%) delete mode 100644 ultraplot/internals/rc/subplots.py delete mode 100644 ultraplot/internals/rc/text.py create mode 100644 ultraplot/internals/rc/validators.py diff --git a/ultraplot/internals/rc/__init__.py b/ultraplot/internals/rc/__init__.py index c0f553019..0f260268b 100644 --- a/ultraplot/internals/rc/__init__.py +++ b/ultraplot/internals/rc/__init__.py @@ -1,26 +1,16 @@ #!/usr/bin/env python3 """ -Helpers for composing rc parameter tables. +rc table assembly helpers. """ -from .axes import build_axes_rc_table -from .core import build_core_rc_table from .deprecations import get_rc_removed, get_rc_renamed -from .plot_types import build_plot_type_rc_table -from .ribbon import build_ribbon_rc_table from .registry import merge_rc_tables -from .sankey import build_sankey_rc_table -from .subplots import build_subplots_rc_table -from .text import build_text_rc_table +from .settings import build_settings_rc_table +from .validators import build_validator_aliases __all__ = [ - "build_axes_rc_table", - "build_core_rc_table", - "build_plot_type_rc_table", - "build_ribbon_rc_table", - "build_sankey_rc_table", - "build_subplots_rc_table", - "build_text_rc_table", + "build_settings_rc_table", + "build_validator_aliases", "get_rc_removed", "get_rc_renamed", "merge_rc_tables", diff --git a/ultraplot/internals/rc/axes.py b/ultraplot/internals/rc/axes.py deleted file mode 100644 index f9d147b22..000000000 --- a/ultraplot/internals/rc/axes.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -""" -Axes-domain rc defaults. -""" - -from __future__ import annotations - -from typing import Any, Callable, Mapping - -from .core import build_core_rc_table -from .plot_types import build_plot_type_rc_table - -RcValidator = Callable[[Any], Any] -RcEntry = tuple[Any, RcValidator, str] -RcTable = dict[str, RcEntry] - -_TEXT_PREFIXES = ( - "abc.", - "text.", - "title.", - "suptitle.", - "leftlabel.", - "rightlabel.", - "toplabel.", - "bottomlabel.", - "font.", -) - - -def _plot_type_validators(ns: Mapping[str, Any]) -> dict[str, RcValidator]: - return { - "validate_bool": ns["_validate_bool"], - "validate_color": ns["_validate_color"], - "validate_float": ns["_validate_float"], - "validate_int": ns["_validate_int"], - "validate_string": ns["_validate_string"], - } - - -def build_axes_rc_table(ns: Mapping[str, Any]) -> RcTable: - """ - Build axes-domain rc entries. - - This includes all core settings except text/subplots domains and adds - curved-quiver plot-type defaults. - """ - core = build_core_rc_table(ns) - table = { - key: value - for key, value in core.items() - if not key.startswith("subplots.") and not key.startswith(_TEXT_PREFIXES) - } - plot_types = build_plot_type_rc_table(**_plot_type_validators(ns)) - table.update( - { - key: value - for key, value in plot_types.items() - if key.startswith("curved_quiver.") - } - ) - return table diff --git a/ultraplot/internals/rc/plot_types.py b/ultraplot/internals/rc/plot_types.py deleted file mode 100644 index 4472b5c40..000000000 --- a/ultraplot/internals/rc/plot_types.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python3 -""" -rc defaults for plot-type specific settings. -""" - -from __future__ import annotations - -from typing import Any, Callable - -RcValidator = Callable[[Any], Any] -RcEntry = tuple[Any, RcValidator, str] -RcTable = dict[str, RcEntry] - - -def build_plot_type_rc_table( - *, - validate_bool: RcValidator, - validate_color: RcValidator, - validate_float: RcValidator, - validate_int: RcValidator, - validate_string: RcValidator, -) -> RcTable: - """ - Return rc table entries scoped to plot types. - - Validators are passed from rcsetup to avoid import cycles and keep - validation behavior centralized. - """ - return { - # Curved quiver settings - "curved_quiver.arrowsize": ( - 1.0, - validate_float, - "Default size scaling for arrows in curved quiver plots.", - ), - "curved_quiver.arrowstyle": ( - "-|>", - validate_string, - "Default arrow style for curved quiver plots.", - ), - "curved_quiver.scale": ( - 1.0, - validate_float, - "Default scale factor for curved quiver plots.", - ), - "curved_quiver.grains": ( - 15, - validate_int, - "Default number of grains (segments) for curved quiver arrows.", - ), - "curved_quiver.density": ( - 10, - validate_int, - "Default density of arrows for curved quiver plots.", - ), - "curved_quiver.arrows_at_end": ( - True, - validate_bool, - "Whether to draw arrows at the end of curved quiver lines by default.", - ), - # Sankey settings - "sankey.nodepad": ( - 0.02, - validate_float, - "Vertical padding between nodes in layered sankey diagrams.", - ), - "sankey.nodewidth": ( - 0.03, - validate_float, - "Node width for layered sankey diagrams (axes-relative units).", - ), - "sankey.margin": ( - 0.05, - validate_float, - "Margin around layered sankey diagrams (axes-relative units).", - ), - "sankey.flow.alpha": ( - 0.75, - validate_float, - "Flow transparency for layered sankey diagrams.", - ), - "sankey.flow.curvature": ( - 0.5, - validate_float, - "Flow curvature for layered sankey diagrams.", - ), - "sankey.node.facecolor": ( - "0.75", - validate_color, - "Default node facecolor for layered sankey diagrams.", - ), - } diff --git a/ultraplot/internals/rc/ribbon.py b/ultraplot/internals/rc/ribbon.py deleted file mode 100644 index 59d9a8d2a..000000000 --- a/ultraplot/internals/rc/ribbon.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -""" -Ribbon-domain rc defaults. -""" - -from __future__ import annotations - -from typing import Any, Callable, Mapping - -from .core import build_core_rc_table -from .plot_types import build_plot_type_rc_table -from .registry import merge_rc_tables - -RcValidator = Callable[[Any], Any] -RcEntry = tuple[Any, RcValidator, str] -RcTable = dict[str, RcEntry] - - -def build_ribbon_rc_table(ns: Mapping[str, Any]) -> RcTable: - """ - Build ribbon-domain rc entries. - - Ribbon keys may be absent on branches where ribbon rc defaults have not yet - landed on main; this function safely returns an empty table in that case. - """ - core = build_core_rc_table(ns) - plot_types = build_plot_type_rc_table( - validate_bool=ns["_validate_bool"], - validate_color=ns["_validate_color"], - validate_float=ns["_validate_float"], - validate_int=ns["_validate_int"], - validate_string=ns["_validate_string"], - ) - combined = merge_rc_tables(core, plot_types) - return {key: value for key, value in combined.items() if key.startswith("ribbon.")} diff --git a/ultraplot/internals/rc/sankey.py b/ultraplot/internals/rc/sankey.py deleted file mode 100644 index cfb29e12e..000000000 --- a/ultraplot/internals/rc/sankey.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 -""" -Sankey-domain rc defaults. -""" - -from __future__ import annotations - -from typing import Any, Callable, Mapping - -from .plot_types import build_plot_type_rc_table - -RcValidator = Callable[[Any], Any] -RcEntry = tuple[Any, RcValidator, str] -RcTable = dict[str, RcEntry] - - -def build_sankey_rc_table(ns: Mapping[str, Any]) -> RcTable: - """ - Build sankey-domain rc entries. - """ - table = build_plot_type_rc_table( - validate_bool=ns["_validate_bool"], - validate_color=ns["_validate_color"], - validate_float=ns["_validate_float"], - validate_int=ns["_validate_int"], - validate_string=ns["_validate_string"], - ) - return {key: value for key, value in table.items() if key.startswith("sankey.")} diff --git a/ultraplot/internals/rc/core.py b/ultraplot/internals/rc/settings.py similarity index 95% rename from ultraplot/internals/rc/core.py rename to ultraplot/internals/rc/settings.py index f9e0bbc76..b36217e48 100644 --- a/ultraplot/internals/rc/core.py +++ b/ultraplot/internals/rc/settings.py @@ -1,18 +1,19 @@ #!/usr/bin/env python3 """ -Core rc defaults extracted from rcsetup. +Single-source rc setting table with section headers. """ from __future__ import annotations from typing import Any, Callable, Mapping +from .validators import build_validator_aliases + RcValidator = Callable[[Any], Any] RcEntry = tuple[Any, RcValidator, str] RcTable = dict[str, RcEntry] - -def build_core_rc_table(ns: Mapping[str, Any]) -> RcTable: +def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: """Build the core ultraplot rc table from rcsetup namespace symbols.""" BLACK = ns["BLACK"] CMAPCAT = ns["CMAPCAT"] @@ -79,7 +80,78 @@ def build_core_rc_table(ns: Mapping[str, Any]) -> RcTable: _validate_string_or_iterable = ns["_validate_string_or_iterable"] _validate_tuple_float_2 = ns["_validate_tuple_float_2"] _validate_tuple_int_2 = ns["_validate_tuple_int_2"] + + validators = build_validator_aliases(ns) + validate_bool = validators["bool"] + validate_color = validators["color"] + validate_float = validators["float"] + validate_int = validators["int"] + validate_string = validators["string"] + return { + # Plot-type settings + # Curved quiver settings + "curved_quiver.arrowsize": ( + 1.0, + validate_float, + "Default size scaling for arrows in curved quiver plots.", + ), + "curved_quiver.arrowstyle": ( + "-|>", + validate_string, + "Default arrow style for curved quiver plots.", + ), + "curved_quiver.scale": ( + 1.0, + validate_float, + "Default scale factor for curved quiver plots.", + ), + "curved_quiver.grains": ( + 15, + validate_int, + "Default number of grains (segments) for curved quiver arrows.", + ), + "curved_quiver.density": ( + 10, + validate_int, + "Default density of arrows for curved quiver plots.", + ), + "curved_quiver.arrows_at_end": ( + True, + validate_bool, + "Whether to draw arrows at the end of curved quiver lines by default.", + ), + # Sankey settings + "sankey.nodepad": ( + 0.02, + validate_float, + "Vertical padding between nodes in layered sankey diagrams.", + ), + "sankey.nodewidth": ( + 0.03, + validate_float, + "Node width for layered sankey diagrams (axes-relative units).", + ), + "sankey.margin": ( + 0.05, + validate_float, + "Margin around layered sankey diagrams (axes-relative units).", + ), + "sankey.flow.alpha": ( + 0.75, + validate_float, + "Flow transparency for layered sankey diagrams.", + ), + "sankey.flow.curvature": ( + 0.5, + validate_float, + "Flow curvature for layered sankey diagrams.", + ), + "sankey.node.facecolor": ( + "0.75", + validate_color, + "Default node facecolor for layered sankey diagrams.", + ), "external.shrink": ( 0.9, _validate_float, diff --git a/ultraplot/internals/rc/subplots.py b/ultraplot/internals/rc/subplots.py deleted file mode 100644 index 975db4e60..000000000 --- a/ultraplot/internals/rc/subplots.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python3 -""" -Subplots-domain rc defaults. -""" - -from __future__ import annotations - -from typing import Any, Callable, Mapping - -from .core import build_core_rc_table - -RcValidator = Callable[[Any], Any] -RcEntry = tuple[Any, RcValidator, str] -RcTable = dict[str, RcEntry] - - -def build_subplots_rc_table(ns: Mapping[str, Any]) -> RcTable: - """ - Build subplots-domain rc entries. - """ - core = build_core_rc_table(ns) - return {key: value for key, value in core.items() if key.startswith("subplots.")} diff --git a/ultraplot/internals/rc/text.py b/ultraplot/internals/rc/text.py deleted file mode 100644 index 08f9ee3bc..000000000 --- a/ultraplot/internals/rc/text.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 -""" -Text-domain rc defaults. -""" - -from __future__ import annotations - -from typing import Any, Callable, Mapping - -from .core import build_core_rc_table - -RcValidator = Callable[[Any], Any] -RcEntry = tuple[Any, RcValidator, str] -RcTable = dict[str, RcEntry] - -_TEXT_PREFIXES = ( - "abc.", - "text.", - "title.", - "suptitle.", - "leftlabel.", - "rightlabel.", - "toplabel.", - "bottomlabel.", - "font.", -) - - -def build_text_rc_table(ns: Mapping[str, Any]) -> RcTable: - """ - Build text-domain rc entries. - """ - core = build_core_rc_table(ns) - return {key: value for key, value in core.items() if key.startswith(_TEXT_PREFIXES)} diff --git a/ultraplot/internals/rc/validators.py b/ultraplot/internals/rc/validators.py new file mode 100644 index 000000000..f1f7daf61 --- /dev/null +++ b/ultraplot/internals/rc/validators.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +""" +Validator aliases for declarative rc settings. +""" + +from __future__ import annotations + +from typing import Any, Callable, Mapping + +RcValidator = Callable[[Any], Any] + + +def build_validator_aliases(ns: Mapping[str, Any]) -> dict[str, RcValidator]: + """ + Build a compact validator alias map from rcsetup namespace. + """ + return { + "bool": ns["_validate_bool"], + "color": ns["_validate_color"], + "float": ns["_validate_float"], + "int": ns["_validate_int"], + "string": ns["_validate_string"], + } diff --git a/ultraplot/internals/rcsetup.py b/ultraplot/internals/rcsetup.py index ee5467ca0..4b379e0bf 100644 --- a/ultraplot/internals/rcsetup.py +++ b/ultraplot/internals/rcsetup.py @@ -27,14 +27,9 @@ warnings, ) from .rc import ( - build_axes_rc_table, - build_ribbon_rc_table, - build_sankey_rc_table, - build_subplots_rc_table, - build_text_rc_table, + build_settings_rc_table, get_rc_removed, get_rc_renamed, - merge_rc_tables, ) from .versions import _version_mpl @@ -981,13 +976,7 @@ def _validator_accepts(validator, value): " Must be a :ref:`relative font size ` or unit string " "interpreted by `~ultraplot.utils.units`. Numeric units are points." ) -_rc_ultraplot_table = merge_rc_tables( - build_axes_rc_table(globals()), - build_text_rc_table(globals()), - build_subplots_rc_table(globals()), - build_sankey_rc_table(globals()), - build_ribbon_rc_table(globals()), -) +_rc_ultraplot_table = build_settings_rc_table(globals()) # Child settings. Changing the parent changes all the children, but # changing any of the children does not change the parent. diff --git a/ultraplot/tests/test_config.py b/ultraplot/tests/test_config.py index bbe277760..96dc9e2d3 100644 --- a/ultraplot/tests/test_config.py +++ b/ultraplot/tests/test_config.py @@ -255,41 +255,29 @@ def test_rc_registry_merge_duplicate_keys_raises(): merge_rc_tables(table1, table2) -def test_rc_domain_tables_match_legacy_tables(): +def test_rc_settings_table_matches_rcsetup_table(): """ - Domain split tables should match legacy composed rc table keys. + Single settings table should match rcsetup composed table keys. """ from ultraplot.internals import rcsetup - from ultraplot.internals.rc import ( - build_axes_rc_table, - build_core_rc_table, - build_plot_type_rc_table, - build_ribbon_rc_table, - build_sankey_rc_table, - build_subplots_rc_table, - build_text_rc_table, - merge_rc_tables, - ) + from ultraplot.internals.rc import build_settings_rc_table ns = vars(rcsetup) - legacy = merge_rc_tables( - build_core_rc_table(ns), - build_plot_type_rc_table( - validate_bool=ns["_validate_bool"], - validate_color=ns["_validate_color"], - validate_float=ns["_validate_float"], - validate_int=ns["_validate_int"], - validate_string=ns["_validate_string"], - ), - ) - domain_split = merge_rc_tables( - build_axes_rc_table(ns), - build_text_rc_table(ns), - build_subplots_rc_table(ns), - build_sankey_rc_table(ns), - build_ribbon_rc_table(ns), - ) - assert set(domain_split) == set(legacy) + settings_table = build_settings_rc_table(ns) + assert set(settings_table) == set(rcsetup._rc_ultraplot_table) + + +def test_rc_validator_aliases_include_common_validators(): + """ + Validator aliases should include common primitive validators. + """ + from ultraplot.internals import rcsetup + from ultraplot.internals.rc import build_validator_aliases + + aliases = build_validator_aliases(vars(rcsetup)) + assert aliases["float"] is rcsetup._validate_float + assert aliases["bool"] is rcsetup._validate_bool + assert aliases["color"] is rcsetup._validate_color def test_rc_deprecations_module_matches_rcsetup(): From 4133b8d81fd28fddd73ec1e47e82383792faa637 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Thu, 12 Feb 2026 09:04:27 +1000 Subject: [PATCH 6/9] Refactor rc settings table with Spec helper and section-based composition --- ultraplot/internals/rc/settings.py | 928 ++++++++++++++--------------- 1 file changed, 464 insertions(+), 464 deletions(-) diff --git a/ultraplot/internals/rc/settings.py b/ultraplot/internals/rc/settings.py index b36217e48..bf57d2e81 100644 --- a/ultraplot/internals/rc/settings.py +++ b/ultraplot/internals/rc/settings.py @@ -5,162 +5,139 @@ from __future__ import annotations +from dataclasses import dataclass from typing import Any, Callable, Mapping +from .registry import merge_rc_tables from .validators import build_validator_aliases RcValidator = Callable[[Any], Any] RcEntry = tuple[Any, RcValidator, str] RcTable = dict[str, RcEntry] -def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: - """Build the core ultraplot rc table from rcsetup namespace symbols.""" - BLACK = ns["BLACK"] - CMAPCAT = ns["CMAPCAT"] - CMAPCYC = ns["CMAPCYC"] - CMAPDIV = ns["CMAPDIV"] - CMAPSEQ = ns["CMAPSEQ"] - COLORBAR_LOCS = ns["COLORBAR_LOCS"] - CYCLE = ns["CYCLE"] - FONTNAME = ns["FONTNAME"] - FRAMEALPHA = ns["FRAMEALPHA"] - GRIDALPHA = ns["GRIDALPHA"] - GRIDBELOW = ns["GRIDBELOW"] - GRIDPAD = ns["GRIDPAD"] - GRIDRATIO = ns["GRIDRATIO"] - GRIDSTYLE = ns["GRIDSTYLE"] - LABELPAD = ns["LABELPAD"] - LARGESIZE = ns["LARGESIZE"] - LINEWIDTH = ns["LINEWIDTH"] - MARGIN = ns["MARGIN"] - MATHTEXT = ns["MATHTEXT"] - SMALLSIZE = ns["SMALLSIZE"] - TEXT_LOCS = ns["TEXT_LOCS"] - TICKDIR = ns["TICKDIR"] - TICKLEN = ns["TICKLEN"] - TICKLENRATIO = ns["TICKLENRATIO"] - TICKMINOR = ns["TICKMINOR"] - TICKPAD = ns["TICKPAD"] - TICKWIDTHRATIO = ns["TICKWIDTHRATIO"] - TITLEPAD = ns["TITLEPAD"] - WHITE = ns["WHITE"] - ZLINES = ns["ZLINES"] - ZPATCHES = ns["ZPATCHES"] - _addendum_em = ns["_addendum_em"] - _addendum_font = ns["_addendum_font"] - _addendum_in = ns["_addendum_in"] - _addendum_pt = ns["_addendum_pt"] - _addendum_rotation = ns["_addendum_rotation"] - _validate = ns["_validate"] - _validate_abc = ns["_validate_abc"] - _validate_belongs = ns["_validate_belongs"] - _validate_bool = ns["_validate_bool"] - _validate_bool_or_iterable = ns["_validate_bool_or_iterable"] - _validate_bool_or_string = ns["_validate_bool_or_string"] - _validate_boxstyle = ns["_validate_boxstyle"] - _validate_cftime_resolution = ns["_validate_cftime_resolution"] - _validate_cftime_resolution_format = ns["_validate_cftime_resolution_format"] - _validate_cmap = ns["_validate_cmap"] - _validate_color = ns["_validate_color"] - _validate_em = ns["_validate_em"] - _validate_float = ns["_validate_float"] - _validate_float_or_auto = ns["_validate_float_or_auto"] - _validate_float_or_iterable = ns["_validate_float_or_iterable"] - _validate_fontname = ns["_validate_fontname"] - _validate_fontsize = ns["_validate_fontsize"] - _validate_fontweight = ns["_validate_fontweight"] - _validate_in = ns["_validate_in"] - _validate_int = ns["_validate_int"] - _validate_joinstyle = ns["_validate_joinstyle"] - _validate_linestyle = ns["_validate_linestyle"] - _validate_or_none = ns["_validate_or_none"] - _validate_pt = ns["_validate_pt"] - _validate_rotation = ns["_validate_rotation"] - _validate_string = ns["_validate_string"] - _validate_string_or_iterable = ns["_validate_string_or_iterable"] - _validate_tuple_float_2 = ns["_validate_tuple_float_2"] - _validate_tuple_int_2 = ns["_validate_tuple_int_2"] +_PLOT_PREFIXES = ("curved_quiver.", "sankey.", "ribbon.") +_TEXT_PREFIXES = ( + "abc.", + "text.", + "title.", + "suptitle.", + "leftlabel.", + "rightlabel.", + "toplabel.", + "bottomlabel.", + "font.", +) +_SUBPLOT_PREFIX = "subplots." + + +@dataclass(frozen=True) +class Spec: + """Declarative rc setting specification.""" + + default: Any + validator: RcValidator + description: str + + def as_entry(self) -> RcEntry: + return (self.default, self.validator, self.description) + + +def _coerce_spec(value: Spec | RcEntry) -> Spec: + if isinstance(value, Spec): + return value + default, validator, description = value + return Spec(default=default, validator=validator, description=description) - validators = build_validator_aliases(ns) - validate_bool = validators["bool"] - validate_color = validators["color"] - validate_float = validators["float"] - validate_int = validators["int"] - validate_string = validators["string"] +def _section_entries( + raw_settings: Mapping[str, Spec | RcEntry], + predicate, +) -> dict[str, RcEntry]: return { + key: _coerce_spec(value).as_entry() + for key, value in raw_settings.items() + if predicate(key) + } + + +def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: + """Build the rc setting table from a single declarative settings map.""" + g = ns.__getitem__ + v = build_validator_aliases(ns) + + raw_settings: dict[str, Spec | RcEntry] = { # Plot-type settings # Curved quiver settings "curved_quiver.arrowsize": ( 1.0, - validate_float, + v["float"], "Default size scaling for arrows in curved quiver plots.", ), "curved_quiver.arrowstyle": ( "-|>", - validate_string, + v["string"], "Default arrow style for curved quiver plots.", ), "curved_quiver.scale": ( 1.0, - validate_float, + v["float"], "Default scale factor for curved quiver plots.", ), "curved_quiver.grains": ( 15, - validate_int, + v["int"], "Default number of grains (segments) for curved quiver arrows.", ), "curved_quiver.density": ( 10, - validate_int, + v["int"], "Default density of arrows for curved quiver plots.", ), "curved_quiver.arrows_at_end": ( True, - validate_bool, + v["bool"], "Whether to draw arrows at the end of curved quiver lines by default.", ), # Sankey settings "sankey.nodepad": ( 0.02, - validate_float, + v["float"], "Vertical padding between nodes in layered sankey diagrams.", ), "sankey.nodewidth": ( 0.03, - validate_float, + v["float"], "Node width for layered sankey diagrams (axes-relative units).", ), "sankey.margin": ( 0.05, - validate_float, + v["float"], "Margin around layered sankey diagrams (axes-relative units).", ), "sankey.flow.alpha": ( 0.75, - validate_float, + v["float"], "Flow transparency for layered sankey diagrams.", ), "sankey.flow.curvature": ( 0.5, - validate_float, + v["float"], "Flow curvature for layered sankey diagrams.", ), "sankey.node.facecolor": ( "0.75", - validate_color, + v["color"], "Default node facecolor for layered sankey diagrams.", ), "external.shrink": ( 0.9, - _validate_float, + g("_validate_float"), "Default shrink factor for external axes containers.", ), # Stylesheet "style": ( None, - _validate_or_none(_validate_string), + g("_validate_or_none")(g("_validate_string")), "The default matplotlib `stylesheet " "`__ " # noqa: E501 "name. If ``None``, a custom ultraplot style is used. " @@ -169,100 +146,100 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: # A-b-c labels "abc": ( False, - _validate_abc, + g("_validate_abc"), "If ``False`` then a-b-c labels are disabled. If ``True`` the default label " "style `a` is used. If string this indicates the style and must contain the " "character `a` or ``A``, for example ``'a.'`` or ``'(A)'``.", ), "abc.border": ( True, - _validate_bool, + g("_validate_bool"), "Whether to draw a white border around a-b-c labels " "when :rcraw:`abc.loc` is inside the axes.", ), "abc.borderwidth": ( 1.5, - _validate_pt, + g("_validate_pt"), "Width of the white border around a-b-c labels.", ), "text.borderstyle": ( "bevel", - _validate_joinstyle, + g("_validate_joinstyle"), "Join style for text border strokes. Must be one of " "``'miter'``, ``'round'``, or ``'bevel'``.", ), "text.curved.upright": ( True, - _validate_bool, + g("_validate_bool"), "Whether curved text is flipped to remain upright by default.", ), "text.curved.ellipsis": ( False, - _validate_bool, + g("_validate_bool"), "Whether to show ellipses when curved text exceeds path length.", ), "text.curved.avoid_overlap": ( True, - _validate_bool, + g("_validate_bool"), "Whether curved text hides overlapping glyphs by default.", ), "text.curved.overlap_tol": ( 0.1, - _validate_float, + g("_validate_float"), "Overlap threshold used when hiding curved-text glyphs.", ), "text.curved.curvature_pad": ( 2.0, - _validate_float, + g("_validate_float"), "Extra curved-text glyph spacing per radian of local curvature.", ), "text.curved.min_advance": ( 1.0, - _validate_float, + g("_validate_float"), "Minimum extra curved-text glyph spacing in pixels.", ), "abc.bbox": ( False, - _validate_bool, + g("_validate_bool"), "Whether to draw semi-transparent bounding boxes around a-b-c labels " "when :rcraw:`abc.loc` is inside the axes.", ), - "abc.bboxcolor": (WHITE, _validate_color, "a-b-c label bounding box color."), + "abc.bboxcolor": (g("WHITE"), g("_validate_color"), "a-b-c label bounding box color."), "abc.bboxstyle": ( "square", - _validate_boxstyle, + g("_validate_boxstyle"), "a-b-c label bounding box style.", ), - "abc.bboxalpha": (0.5, _validate_float, "a-b-c label bounding box opacity."), + "abc.bboxalpha": (0.5, g("_validate_float"), "a-b-c label bounding box opacity."), "abc.bboxpad": ( None, - _validate_or_none(_validate_pt), + g("_validate_or_none")(g("_validate_pt")), "Padding for the a-b-c label bounding box. By default this is scaled " - "to make the box flush against the subplot edge." + _addendum_pt, + "to make the box flush against the subplot edge." + g("_addendum_pt"), ), - "abc.color": (BLACK, _validate_color, "a-b-c label color."), + "abc.color": (g("BLACK"), g("_validate_color"), "a-b-c label color."), "abc.loc": ( "left", # left side above the axes - _validate_belongs(*TEXT_LOCS), + g("_validate_belongs")(*g("TEXT_LOCS")), "a-b-c label position. " "For options see the :ref:`location table `.", ), "abc.size": ( - LARGESIZE, - _validate_fontsize, - "a-b-c label font size." + _addendum_font, + g("LARGESIZE"), + g("_validate_fontsize"), + "a-b-c label font size." + g("_addendum_font"), ), "abc.titlepad": ( - LABELPAD, - _validate_pt, + g("LABELPAD"), + g("_validate_pt"), "Padding separating the title and a-b-c label when in the same location." - + _addendum_pt, + + g("_addendum_pt"), ), - "abc.weight": ("bold", _validate_fontweight, "a-b-c label font weight."), + "abc.weight": ("bold", g("_validate_fontweight"), "a-b-c label font weight."), # Autoformatting "autoformat": ( True, - _validate_bool, + g("_validate_bool"), "Whether to automatically apply labels from `pandas.Series`, " "`pandas.DataFrame`, and `xarray.DataArray` objects passed to " "plotting functions. See also :rcraw:`unitformat`.", @@ -270,88 +247,88 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: # Axes additions "axes.alpha": ( None, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Opacity of the background axes patch.", ), "axes.inbounds": ( True, - _validate_bool, + g("_validate_bool"), "Whether to exclude out-of-bounds data when determining the default *y* (*x*) " "axis limits and the *x* (*y*) axis limits have been locked.", ), "axes.margin": ( - MARGIN, - _validate_float, + g("MARGIN"), + g("_validate_float"), "The fractional *x* and *y* axis margins when limits are unset.", ), "bar.bar_labels": ( False, - _validate_bool, + g("_validate_bool"), "Add value of the bars to the bar labels", ), # Country borders - "borders": (False, _validate_bool, "Toggles country border lines on and off."), + "borders": (False, g("_validate_bool"), "Toggles country border lines on and off."), "borders.alpha": ( None, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Opacity for country border lines.", ), "borders.color": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Line color for country border lines.", ), "borders.linewidth": ( - LINEWIDTH, - _validate_pt, + g("LINEWIDTH"), + g("_validate_pt"), "Line width for country border lines.", ), "borders.zorder": ( - ZLINES, - _validate_float, + g("ZLINES"), + g("_validate_float"), "Z-order for country border lines.", ), "borders.rasterized": ( False, - _validate_bool, + g("_validate_bool"), "Toggles rasterization on or off for border feature in GeoAxes.", ), # Bottom subplot labels "bottomlabel.color": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Font color for column labels on the bottom of the figure.", ), "bottomlabel.pad": ( - TITLEPAD, - _validate_pt, + g("TITLEPAD"), + g("_validate_pt"), "Padding between axes content and column labels on the bottom of the figure." - + _addendum_pt, + + g("_addendum_pt"), ), "bottomlabel.rotation": ( "horizontal", - _validate_rotation, + g("_validate_rotation"), "Rotation for column labels at the bottom of the figure." - + _addendum_rotation, + + g("_addendum_rotation"), ), "bottomlabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for column labels on the bottom of the figure." + _addendum_font, + g("LARGESIZE"), + g("_validate_fontsize"), + "Font size for column labels on the bottom of the figure." + g("_addendum_font"), ), "bottomlabel.weight": ( "bold", - _validate_fontweight, + g("_validate_fontweight"), "Font weight for column labels on the bottom of the figure.", ), "cftime.time_unit": ( "days since 2000-01-01", - _validate_string, + g("_validate_string"), "Time unit for non-Gregorian calendars.", ), "cftime.resolution": ( "DAILY", - _validate_cftime_resolution, + g("_validate_cftime_resolution"), "Default time resolution for non-Gregorian calendars.", ), "cftime.time_resolution_format": ( @@ -363,179 +340,179 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "MONTHLY": "%m", "YEARLY": "%Y", }, - _validate_cftime_resolution_format, + g("_validate_cftime_resolution_format"), "Dict used for formatting non-Gregorian calendars.", ), "cftime.max_display_ticks": ( 7, - _validate_int, + g("_validate_int"), "Number of ticks to display for cftime units.", ), # Coastlines - "coast": (False, _validate_bool, "Toggles coastline lines on and off."), + "coast": (False, g("_validate_bool"), "Toggles coastline lines on and off."), "coast.alpha": ( None, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Opacity for coast lines", ), - "coast.color": (BLACK, _validate_color, "Line color for coast lines."), - "coast.linewidth": (LINEWIDTH, _validate_pt, "Line width for coast lines."), - "coast.zorder": (ZLINES, _validate_float, "Z-order for coast lines."), + "coast.color": (g("BLACK"), g("_validate_color"), "Line color for coast lines."), + "coast.linewidth": (g("LINEWIDTH"), g("_validate_pt"), "Line width for coast lines."), + "coast.zorder": (g("ZLINES"), g("_validate_float"), "Z-order for coast lines."), "coast.rasterized": ( False, - _validate_bool, + g("_validate_bool"), "Toggles the rasterization of the coastlines feature for GeoAxes.", ), # Colorbars "colorbar.center_levels": ( False, - _validate_bool, + g("_validate_bool"), "Center the ticks in the center of each segment.", ), "colorbar.edgecolor": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Color for the inset colorbar frame edge.", ), "colorbar.extend": ( 1.3, - _validate_em, + g("_validate_em"), 'Length of rectangular or triangular "extensions" for panel colorbars.' - + _addendum_em, + + g("_addendum_em"), ), "colorbar.outline": ( True, - _validate_bool, + g("_validate_bool"), "Whether to draw a frame around the colorbar.", ), "colorbar.labelrotation": ( "auto", - _validate_float_or_auto, + g("_validate_float_or_auto"), "Rotation of colorbar labels.", ), "colorbar.fancybox": ( False, - _validate_bool, + g("_validate_bool"), 'Whether to use a "fancy" round bounding box for inset colorbar frames.', ), "colorbar.framealpha": ( - FRAMEALPHA, - _validate_float, + g("FRAMEALPHA"), + g("_validate_float"), "Opacity for inset colorbar frames.", ), "colorbar.facecolor": ( - WHITE, - _validate_color, + g("WHITE"), + g("_validate_color"), "Color for the inset colorbar frame.", ), "colorbar.frameon": ( True, - _validate_bool, + g("_validate_bool"), "Whether to draw a frame behind inset colorbars.", ), "colorbar.grid": ( False, - _validate_bool, + g("_validate_bool"), "Whether to draw borders between each level of the colorbar.", ), "colorbar.insetextend": ( 0.9, - _validate_em, + g("_validate_em"), 'Length of rectangular or triangular "extensions" for inset colorbars.' - + _addendum_em, + + g("_addendum_em"), ), "colorbar.insetlength": ( 8, - _validate_em, - "Length of inset colorbars." + _addendum_em, + g("_validate_em"), + "Length of inset colorbars." + g("_addendum_em"), ), "colorbar.insetpad": ( 0.7, - _validate_em, - "Padding between axes edge and inset colorbars." + _addendum_em, + g("_validate_em"), + "Padding between axes edge and inset colorbars." + g("_addendum_em"), ), "colorbar.insetwidth": ( 1.2, - _validate_em, - "Width of inset colorbars." + _addendum_em, + g("_validate_em"), + "Width of inset colorbars." + g("_addendum_em"), ), - "colorbar.length": (1, _validate_em, "Length of outer colorbars."), + "colorbar.length": (1, g("_validate_em"), "Length of outer colorbars."), "colorbar.loc": ( "right", - _validate_belongs(*COLORBAR_LOCS), + g("_validate_belongs")(*g("COLORBAR_LOCS")), "Inset colorbar location. " "For options see the :ref:`location table `.", ), "colorbar.width": ( 0.2, - _validate_in, - "Width of outer colorbars." + _addendum_in, + g("_validate_in"), + "Width of outer colorbars." + g("_addendum_in"), ), "colorbar.rasterized": ( False, - _validate_bool, + g("_validate_bool"), "Whether to use rasterization for colorbar solids.", ), "colorbar.shadow": ( False, - _validate_bool, + g("_validate_bool"), "Whether to add a shadow underneath inset colorbar frames.", ), # Color cycle additions "cycle": ( - CYCLE, - _validate_cmap("discrete", cycle=True), + g("CYCLE"), + g("_validate_cmap")("discrete", cycle=True), "Name of the color cycle assigned to :rcraw:`axes.prop_cycle`.", ), # Colormap additions "cmap": ( - CMAPSEQ, - _validate_cmap("continuous"), + g("CMAPSEQ"), + g("_validate_cmap")("continuous"), "Alias for :rcraw:`cmap.sequential` and :rcraw:`image.cmap`.", ), "cmap.autodiverging": ( True, - _validate_bool, + g("_validate_bool"), "Whether to automatically apply a diverging colormap and " "normalizer based on the data.", ), "cmap.qualitative": ( - CMAPCAT, - _validate_cmap("discrete"), + g("CMAPCAT"), + g("_validate_cmap")("discrete"), "Default colormap for qualitative datasets.", ), "cmap.cyclic": ( - CMAPCYC, - _validate_cmap("continuous"), + g("CMAPCYC"), + g("_validate_cmap")("continuous"), "Default colormap for cyclic datasets.", ), "cmap.discrete": ( None, - _validate_or_none(_validate_bool), + g("_validate_or_none")(g("_validate_bool")), "If ``True``, `~ultraplot.colors.DiscreteNorm` is used for every colormap plot. " "If ``False``, it is never used. If ``None``, it is used for all plot types " "except `imshow`, `matshow`, `spy`, `hexbin`, and `hist2d`.", ), "cmap.diverging": ( - CMAPDIV, - _validate_cmap("continuous"), + g("CMAPDIV"), + g("_validate_cmap")("continuous"), "Default colormap for diverging datasets.", ), "cmap.inbounds": ( True, - _validate_bool, + g("_validate_bool"), "If ``True`` and the *x* and *y* axis limits are fixed, only in-bounds data " "is considered when determining the default colormap `vmin` and `vmax`.", ), "cmap.levels": ( 11, - _validate_int, + g("_validate_int"), "Default number of `~ultraplot.colors.DiscreteNorm` levels for plotting " "commands that use colormaps.", ), "cmap.listedthresh": ( 64, - _validate_int, + g("_validate_int"), "Native `~matplotlib.colors.ListedColormap`\\ s with more colors than " "this are converted to :class:`~ultraplot.colors.ContinuousColormap` rather than " ":class:`~ultraplot.colors.DiscreteColormap`. This helps translate continuous " @@ -543,751 +520,751 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: ), "cmap.lut": ( 256, - _validate_int, + g("_validate_int"), "Number of colors in the colormap lookup table. " "Alias for :rcraw:`image.lut`.", ), "cmap.robust": ( False, - _validate_bool, + g("_validate_bool"), "If ``True``, the default colormap `vmin` and `vmax` are chosen using the " "2nd to 98th percentiles rather than the minimum and maximum.", ), "cmap.sequential": ( - CMAPSEQ, - _validate_cmap("continuous"), + g("CMAPSEQ"), + g("_validate_cmap")("continuous"), "Default colormap for sequential datasets. Alias for :rcraw:`image.cmap`.", ), # Special setting "edgefix": ( True, - _validate_bool, + g("_validate_bool"), 'Whether to fix issues with "white lines" appearing between patches ' "in saved vector graphics and with vector graphic backends. Applies " "to colorbar levels and bar, area, pcolor, and contour plots.", ), # Font settings - "font.name": (FONTNAME, _validate_fontname, "Alias for :rcraw:`font.family`."), + "font.name": (g("FONTNAME"), g("_validate_fontname"), "Alias for :rcraw:`font.family`."), "font.small": ( - SMALLSIZE, - _validate_fontsize, + g("SMALLSIZE"), + g("_validate_fontsize"), "Alias for :rcraw:`font.smallsize`.", ), "font.smallsize": ( - SMALLSIZE, - _validate_fontsize, + g("SMALLSIZE"), + g("_validate_fontsize"), "Meta setting that changes the label-like sizes ``axes.labelsize``, " "``legend.fontsize``, ``tick.labelsize``, and ``grid.labelsize``. Default is " - "``'medium'`` (equivalent to :rcraw:`font.size`)." + _addendum_font, + "``'medium'`` (equivalent to :rcraw:`font.size`)." + g("_addendum_font"), ), "font.large": ( - LARGESIZE, - _validate_fontsize, + g("LARGESIZE"), + g("_validate_fontsize"), "Alias for :rcraw:`font.largesize`.", ), "font.largesize": ( - LARGESIZE, - _validate_fontsize, + g("LARGESIZE"), + g("_validate_fontsize"), "Meta setting that changes the title-like sizes ``abc.size``, ``title.size``, " "``suptitle.size``, ``leftlabel.size``, ``rightlabel.size``, etc. Default is " - "``'med-large'`` (i.e. 1.1 times :rcraw:`font.size`)." + _addendum_font, + "``'med-large'`` (i.e. 1.1 times :rcraw:`font.size`)." + g("_addendum_font"), ), # Formatter settings "formatter.timerotation": ( "vertical", - _validate_rotation, - "Rotation for *x* axis datetime tick labels." + _addendum_rotation, + g("_validate_rotation"), + "Rotation for *x* axis datetime tick labels." + g("_addendum_rotation"), ), "formatter.zerotrim": ( True, - _validate_bool, + g("_validate_bool"), "Whether to trim trailing decimal zeros on tick labels.", ), "formatter.log": ( False, - _validate_bool, + g("_validate_bool"), "Whether to use log formatting (e.g., $10^{4}$) for " "logarithmically scaled axis tick labels.", ), "formatter.limits": ( [-5, 6], # must be list or else validated - _validate["axes.formatter.limits"], + g("_validate")["axes.formatter.limits"], "Alias for :rcraw:`axes.formatter.limits`.", ), "formatter.min_exponent": ( 0, - _validate["axes.formatter.min_exponent"], + g("_validate")["axes.formatter.min_exponent"], "Alias for :rcraw:`axes.formatter.min_exponent`.", ), "formatter.offset_threshold": ( 4, - _validate["axes.formatter.offset_threshold"], + g("_validate")["axes.formatter.offset_threshold"], "Alias for :rcraw:`axes.formatter.offset_threshold`.", ), "formatter.use_locale": ( False, - _validate_bool, + g("_validate_bool"), "Alias for :rcraw:`axes.formatter.use_locale`.", ), "formatter.use_mathtext": ( - MATHTEXT, - _validate_bool, + g("MATHTEXT"), + g("_validate_bool"), "Alias for :rcraw:`axes.formatter.use_mathtext`.", ), "formatter.use_offset": ( True, - _validate_bool, + g("_validate_bool"), "Alias for :rcraw:`axes.formatter.useOffset`.", ), # Geographic axes settings "geo.backend": ( "cartopy", - _validate_belongs("cartopy", "basemap"), + g("_validate_belongs")("cartopy", "basemap"), "The backend used for `~ultraplot.axes.GeoAxes`. Must be " "either 'cartopy' or 'basemap'.", ), "geo.extent": ( "globe", - _validate_belongs("globe", "auto"), + g("_validate_belongs")("globe", "auto"), "If ``'globe'``, the extent of cartopy `~ultraplot.axes.GeoAxes` is always " "global. If ``'auto'``, the extent is automatically adjusted based on " "plotted content. Default is ``'globe'``.", ), "geo.round": ( True, - _validate_bool, + g("_validate_bool"), "If ``True`` (the default), polar `~ultraplot.axes.GeoAxes` like ``'npstere'`` " "and ``'spstere'`` are bounded with circles rather than squares.", ), # Graphs "graph.draw_nodes": ( True, - _validate_bool_or_iterable, + g("_validate_bool_or_iterable"), "If ``True`` draws the nodes for all the nodes, otherwise only the nodes that are in the iterable.", ), "graph.draw_edges": ( True, - _validate_bool_or_iterable, + g("_validate_bool_or_iterable"), "If ``True`` draws the edges for all the edges, otherwise only the edges that are in the iterable.", ), "graph.draw_labels": ( False, - _validate_bool_or_iterable, + g("_validate_bool_or_iterable"), "If ``True`` draws the labels for all the nodes, otherwise only the nodes that are in the iterable.", ), "graph.draw_grid": ( False, - _validate_bool, + g("_validate_bool"), "If ``True`` draws the grid for all the edges, otherwise only the edges that are in the iterable.", ), "graph.aspect": ( "equal", - _validate_belongs("equal", "auto"), + g("_validate_belongs")("equal", "auto"), "The aspect ratio of the graph.", ), - "graph.facecolor": ("none", _validate_color, "The facecolor of the graph."), + "graph.facecolor": ("none", g("_validate_color"), "The facecolor of the graph."), "graph.draw_spines": ( False, - _validate_bool_or_iterable, + g("_validate_bool_or_iterable"), "If ``True`` draws the spines for all the edges, otherwise only the edges that are in the iterable.", ), "graph.rescale": ( True, - _validate_bool, + g("_validate_bool"), "If ``True`` rescales the graph to fit the data.", ), # Gridlines # NOTE: Here 'grid' and 'gridminor' or *not* aliases for native 'axes.grid' and # invented 'axes.gridminor' because native 'axes.grid' controls both major *and* # minor gridlines. Must handle it independently from these settings. - "grid": (True, _validate_bool, "Toggle major gridlines on and off."), + "grid": (True, g("_validate_bool"), "Toggle major gridlines on and off."), "grid.below": ( - GRIDBELOW, # like axes.axisbelow - _validate_belongs(False, "line", True), + g("GRIDBELOW"), # like axes.axisbelow + g("_validate_belongs")(False, "line", True), "Alias for :rcraw:`axes.axisbelow`. If ``True``, draw gridlines below " "everything. If ``True``, draw them above everything. If ``'line'``, " "draw them above patches but below lines and markers.", ), "grid.checkoverlap": ( True, - _validate_bool, + g("_validate_bool"), "Whether to have cartopy automatically check for and remove overlapping " "`~ultraplot.axes.GeoAxes` gridline labels.", ), "grid.dmslabels": ( True, - _validate_bool, + g("_validate_bool"), "Whether to use degrees-minutes-seconds rather than decimals for " "cartopy `~ultraplot.axes.GeoAxes` gridlines.", ), "grid.geolabels": ( True, - _validate_bool, + g("_validate_bool"), "Whether to include the ``'geo'`` spine in cartopy >= 0.20 when otherwise " "toggling left, right, bottom, or top `~ultraplot.axes.GeoAxes` gridline labels.", ), "grid.inlinelabels": ( False, - _validate_bool, + g("_validate_bool"), "Whether to add inline labels for cartopy `~ultraplot.axes.GeoAxes` gridlines.", ), "grid.labels": ( False, - _validate_bool, + g("_validate_bool"), "Whether to add outer labels for `~ultraplot.axes.GeoAxes` gridlines.", ), "grid.labelcolor": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Font color for `~ultraplot.axes.GeoAxes` gridline labels.", ), "grid.labelpad": ( - GRIDPAD, - _validate_pt, + g("GRIDPAD"), + g("_validate_pt"), "Padding between the map boundary and cartopy `~ultraplot.axes.GeoAxes` " - "gridline labels." + _addendum_pt, + "gridline labels." + g("_addendum_pt"), ), "grid.labelsize": ( - SMALLSIZE, - _validate_fontsize, - "Font size for `~ultraplot.axes.GeoAxes` gridline labels." + _addendum_font, + g("SMALLSIZE"), + g("_validate_fontsize"), + "Font size for `~ultraplot.axes.GeoAxes` gridline labels." + g("_addendum_font"), ), "grid.labelweight": ( "normal", - _validate_fontweight, + g("_validate_fontweight"), "Font weight for `~ultraplot.axes.GeoAxes` gridline labels.", ), "grid.nsteps": ( 250, - _validate_int, + g("_validate_int"), "Number of points used to draw cartopy `~ultraplot.axes.GeoAxes` gridlines.", ), - "grid.pad": (GRIDPAD, _validate_pt, "Alias for :rcraw:`grid.labelpad`."), + "grid.pad": (g("GRIDPAD"), g("_validate_pt"), "Alias for :rcraw:`grid.labelpad`."), "grid.rotatelabels": ( False, # False limits projections where labels are available - _validate_bool, + g("_validate_bool"), "Whether to rotate cartopy `~ultraplot.axes.GeoAxes` gridline labels.", ), "grid.style": ( "-", - _validate_linestyle, + g("_validate_linestyle"), "Major gridline style. Alias for :rcraw:`grid.linestyle`.", ), "grid.width": ( - LINEWIDTH, - _validate_pt, + g("LINEWIDTH"), + g("_validate_pt"), "Major gridline width. Alias for :rcraw:`grid.linewidth`.", ), "grid.widthratio": ( - GRIDRATIO, - _validate_float, + g("GRIDRATIO"), + g("_validate_float"), "Ratio of minor gridline width to major gridline width.", ), # Minor gridlines - "gridminor": (False, _validate_bool, "Toggle minor gridlines on and off."), - "gridminor.alpha": (GRIDALPHA, _validate_float, "Minor gridline opacity."), - "gridminor.color": (BLACK, _validate_color, "Minor gridline color."), + "gridminor": (False, g("_validate_bool"), "Toggle minor gridlines on and off."), + "gridminor.alpha": (g("GRIDALPHA"), g("_validate_float"), "Minor gridline opacity."), + "gridminor.color": (g("BLACK"), g("_validate_color"), "Minor gridline color."), "gridminor.linestyle": ( - GRIDSTYLE, - _validate_linestyle, + g("GRIDSTYLE"), + g("_validate_linestyle"), "Minor gridline style.", ), "gridminor.linewidth": ( - GRIDRATIO * LINEWIDTH, - _validate_pt, + g("GRIDRATIO") * g("LINEWIDTH"), + g("_validate_pt"), "Minor gridline width.", ), "gridminor.style": ( - GRIDSTYLE, - _validate_linestyle, + g("GRIDSTYLE"), + g("_validate_linestyle"), "Minor gridline style. Alias for :rcraw:`gridminor.linestyle`.", ), "gridminor.width": ( - GRIDRATIO * LINEWIDTH, - _validate_pt, + g("GRIDRATIO") * g("LINEWIDTH"), + g("_validate_pt"), "Minor gridline width. Alias for :rcraw:`gridminor.linewidth`.", ), # Backend stuff "inlineformat": ( "retina", - _validate_belongs("svg", "pdf", "retina", "png", "jpeg"), + g("_validate_belongs")("svg", "pdf", "retina", "png", "jpeg"), "The inline backend figure format. Valid formats include " "``'svg'``, ``'pdf'``, ``'retina'``, ``'png'``, and ``jpeg``.", ), # Inner borders "innerborders": ( False, - _validate_bool, + g("_validate_bool"), "Toggles internal political border lines (e.g. states and provinces) " "on and off.", ), "innerborders.alpha": ( None, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Opacity for internal political border lines", ), "innerborders.color": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Line color for internal political border lines.", ), "innerborders.linewidth": ( - LINEWIDTH, - _validate_pt, + g("LINEWIDTH"), + g("_validate_pt"), "Line width for internal political border lines.", ), "innerborders.zorder": ( - ZLINES, - _validate_float, + g("ZLINES"), + g("_validate_float"), "Z-order for internal political border lines.", ), # Axis label settings - "label.color": (BLACK, _validate_color, "Alias for :rcraw:`axes.labelcolor`."), + "label.color": (g("BLACK"), g("_validate_color"), "Alias for :rcraw:`axes.labelcolor`."), "label.pad": ( - LABELPAD, - _validate_pt, - "Alias for :rcraw:`axes.labelpad`." + _addendum_pt, + g("LABELPAD"), + g("_validate_pt"), + "Alias for :rcraw:`axes.labelpad`." + g("_addendum_pt"), ), "label.size": ( - SMALLSIZE, - _validate_fontsize, - "Alias for :rcraw:`axes.labelsize`." + _addendum_font, + g("SMALLSIZE"), + g("_validate_fontsize"), + "Alias for :rcraw:`axes.labelsize`." + g("_addendum_font"), ), "label.weight": ( "normal", - _validate_fontweight, + g("_validate_fontweight"), "Alias for :rcraw:`axes.labelweight`.", ), # Lake patches - "lakes": (False, _validate_bool, "Toggles lake patches on and off."), + "lakes": (False, g("_validate_bool"), "Toggles lake patches on and off."), "lakes.alpha": ( None, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Opacity for lake patches", ), - "lakes.color": (WHITE, _validate_color, "Face color for lake patches."), - "lakes.zorder": (ZPATCHES, _validate_float, "Z-order for lake patches."), + "lakes.color": (g("WHITE"), g("_validate_color"), "Face color for lake patches."), + "lakes.zorder": (g("ZPATCHES"), g("_validate_float"), "Z-order for lake patches."), "lakes.rasterized": ( False, - _validate_bool, + g("_validate_bool"), "Toggles rasterization on or off for lake feature", ), # Land patches - "land": (False, _validate_bool, "Toggles land patches on and off."), + "land": (False, g("_validate_bool"), "Toggles land patches on and off."), "land.alpha": ( None, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Opacity for land patches", ), - "land.color": (BLACK, _validate_color, "Face color for land patches."), - "land.zorder": (ZPATCHES, _validate_float, "Z-order for land patches."), + "land.color": (g("BLACK"), g("_validate_color"), "Face color for land patches."), + "land.zorder": (g("ZPATCHES"), g("_validate_float"), "Z-order for land patches."), "land.rasterized": ( False, - _validate_bool, + g("_validate_bool"), "Toggles the rasterization of the land feature.", ), # Left subplot labels "leftlabel.color": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Font color for row labels on the left-hand side.", ), "leftlabel.pad": ( - TITLEPAD, - _validate_pt, + g("TITLEPAD"), + g("_validate_pt"), "Padding between axes content and row labels on the left-hand side." - + _addendum_pt, + + g("_addendum_pt"), ), "leftlabel.rotation": ( "vertical", - _validate_rotation, - "Rotation for row labels on the left-hand side." + _addendum_rotation, + g("_validate_rotation"), + "Rotation for row labels on the left-hand side." + g("_addendum_rotation"), ), "leftlabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for row labels on the left-hand side." + _addendum_font, + g("LARGESIZE"), + g("_validate_fontsize"), + "Font size for row labels on the left-hand side." + g("_addendum_font"), ), "lollipop.markersize": ( 36, - _validate_float, + g("_validate_float"), "Size of lollipops in the lollipop plot.", ), "lollipop.stemcolor": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Color of lollipop lines.", ), "lollipop.stemwidth": ( - LINEWIDTH, - _validate_pt, + g("LINEWIDTH"), + g("_validate_pt"), "Width of the stem", ), "lollipop.stemlinestyle": ( "-", - _validate_linestyle, + g("_validate_linestyle"), "Line style of lollipop lines.", ), "leftlabel.weight": ( "bold", - _validate_fontweight, + g("_validate_fontweight"), "Font weight for row labels on the left-hand side.", ), # Meta settings "margin": ( - MARGIN, - _validate_float, + g("MARGIN"), + g("_validate_float"), "The fractional *x* and *y* axis data margins when limits are unset. " "Alias for :rcraw:`axes.margin`.", ), "meta.edgecolor": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Color of axis spines, tick marks, tick labels, and labels.", ), "meta.color": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Color of axis spines, tick marks, tick labels, and labels. " "Alias for :rcraw:`meta.edgecolor`.", ), "meta.linewidth": ( - LINEWIDTH, - _validate_pt, + g("LINEWIDTH"), + g("_validate_pt"), "Thickness of axis spines and major tick lines.", ), "meta.width": ( - LINEWIDTH, - _validate_pt, + g("LINEWIDTH"), + g("_validate_pt"), "Thickness of axis spines and major tick lines. " "Alias for :rcraw:`meta.linewidth`.", ), # For negative positive patches "negcolor": ( "blue7", - _validate_color, + g("_validate_color"), "Color for negative bars and shaded areas when using ``negpos=True``. " "See also :rcraw:`poscolor`.", ), "poscolor": ( "red7", - _validate_color, + g("_validate_color"), "Color for positive bars and shaded areas when using ``negpos=True``. " "See also :rcraw:`negcolor`.", ), # Ocean patches - "ocean": (False, _validate_bool, "Toggles ocean patches on and off."), + "ocean": (False, g("_validate_bool"), "Toggles ocean patches on and off."), "ocean.alpha": ( None, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Opacity for ocean patches", ), - "ocean.color": (WHITE, _validate_color, "Face color for ocean patches."), - "ocean.zorder": (ZPATCHES, _validate_float, "Z-order for ocean patches."), + "ocean.color": (g("WHITE"), g("_validate_color"), "Face color for ocean patches."), + "ocean.zorder": (g("ZPATCHES"), g("_validate_float"), "Z-order for ocean patches."), "ocean.rasterized": ( False, - _validate_bool, + g("_validate_bool"), "Turns rasterization on or off for the oceans feature for GeoAxes.", ), # Geographic resolution "reso": ( "lo", - _validate_belongs("lo", "med", "hi", "x-hi", "xx-hi"), + g("_validate_belongs")("lo", "med", "hi", "x-hi", "xx-hi"), "Resolution for `~ultraplot.axes.GeoAxes` geographic features. " "Must be one of ``'lo'``, ``'med'``, ``'hi'``, ``'x-hi'``, or ``'xx-hi'``.", ), # Right subplot labels "rightlabel.color": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Font color for row labels on the right-hand side.", ), "rightlabel.pad": ( - TITLEPAD, - _validate_pt, + g("TITLEPAD"), + g("_validate_pt"), "Padding between axes content and row labels on the right-hand side." - + _addendum_pt, + + g("_addendum_pt"), ), "rightlabel.rotation": ( "vertical", - _validate_rotation, - "Rotation for row labels on the right-hand side." + _addendum_rotation, + g("_validate_rotation"), + "Rotation for row labels on the right-hand side." + g("_addendum_rotation"), ), "rightlabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for row labels on the right-hand side." + _addendum_font, + g("LARGESIZE"), + g("_validate_fontsize"), + "Font size for row labels on the right-hand side." + g("_addendum_font"), ), "rightlabel.weight": ( "bold", - _validate_fontweight, + g("_validate_fontweight"), "Font weight for row labels on the right-hand side.", ), # River lines - "rivers": (False, _validate_bool, "Toggles river lines on and off."), + "rivers": (False, g("_validate_bool"), "Toggles river lines on and off."), "rivers.alpha": ( None, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Opacity for river lines.", ), - "rivers.color": (BLACK, _validate_color, "Line color for river lines."), - "rivers.linewidth": (LINEWIDTH, _validate_pt, "Line width for river lines."), - "rivers.zorder": (ZLINES, _validate_float, "Z-order for river lines."), + "rivers.color": (g("BLACK"), g("_validate_color"), "Line color for river lines."), + "rivers.linewidth": (g("LINEWIDTH"), g("_validate_pt"), "Line width for river lines."), + "rivers.zorder": (g("ZLINES"), g("_validate_float"), "Z-order for river lines."), "rivers.rasterized": ( False, - _validate_bool, + g("_validate_bool"), "Toggles rasterization on or off for rivers feature for GeoAxes.", ), # Circlize settings "chord.start": ( 0.0, - _validate_float, + g("_validate_float"), "Start angle for chord diagrams.", ), "chord.end": ( 360.0, - _validate_float, + g("_validate_float"), "End angle for chord diagrams.", ), "chord.space": ( 0.0, - _validate_float_or_iterable, + g("_validate_float_or_iterable"), "Inter-sector spacing for chord diagrams.", ), "chord.endspace": ( True, - _validate_bool, + g("_validate_bool"), "Whether to add an ending space gap for chord diagrams.", ), "chord.r_lim": ( (97.0, 100.0), - _validate_tuple_float_2, + g("_validate_tuple_float_2"), "Radial limits for chord diagrams.", ), "chord.ticks_interval": ( None, - _validate_or_none(_validate_int), + g("_validate_or_none")(g("_validate_int")), "Tick interval for chord diagrams.", ), "chord.order": ( None, - _validate_or_none(_validate_string_or_iterable), + g("_validate_or_none")(g("_validate_string_or_iterable")), "Ordering of sectors for chord diagrams.", ), "radar.r_lim": ( (0.0, 100.0), - _validate_tuple_float_2, + g("_validate_tuple_float_2"), "Radial limits for radar charts.", ), "radar.vmin": ( 0.0, - _validate_float, + g("_validate_float"), "Minimum value for radar charts.", ), "radar.vmax": ( 100.0, - _validate_float, + g("_validate_float"), "Maximum value for radar charts.", ), "radar.fill": ( True, - _validate_bool, + g("_validate_bool"), "Whether to fill radar chart polygons.", ), "radar.marker_size": ( 0, - _validate_int, + g("_validate_int"), "Marker size for radar charts.", ), "radar.bg_color": ( "#eeeeee80", - _validate_or_none(_validate_color), + g("_validate_or_none")(g("_validate_color")), "Background color for radar charts.", ), "radar.circular": ( False, - _validate_bool, + g("_validate_bool"), "Whether to use circular radar charts.", ), "radar.show_grid_label": ( True, - _validate_bool, + g("_validate_bool"), "Whether to show grid labels on radar charts.", ), "radar.grid_interval_ratio": ( 0.2, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Grid interval ratio for radar charts.", ), "phylogeny.start": ( 0.0, - _validate_float, + g("_validate_float"), "Start angle for phylogeny plots.", ), "phylogeny.end": ( 360.0, - _validate_float, + g("_validate_float"), "End angle for phylogeny plots.", ), "phylogeny.r_lim": ( (50.0, 100.0), - _validate_tuple_float_2, + g("_validate_tuple_float_2"), "Radial limits for phylogeny plots.", ), "phylogeny.format": ( "newick", - _validate_string, + g("_validate_string"), "Input format for phylogeny plots.", ), "phylogeny.outer": ( True, - _validate_bool, + g("_validate_bool"), "Whether to place phylogeny leaves on the outer edge.", ), "phylogeny.align_leaf_label": ( True, - _validate_bool, + g("_validate_bool"), "Whether to align phylogeny leaf labels.", ), "phylogeny.ignore_branch_length": ( False, - _validate_bool, + g("_validate_bool"), "Whether to ignore branch lengths in phylogeny plots.", ), "phylogeny.leaf_label_size": ( None, - _validate_or_none(_validate_float), + g("_validate_or_none")(g("_validate_float")), "Leaf label font size for phylogeny plots.", ), "phylogeny.leaf_label_rmargin": ( 2.0, - _validate_float, + g("_validate_float"), "Radial margin for phylogeny leaf labels.", ), "phylogeny.reverse": ( False, - _validate_bool, + g("_validate_bool"), "Whether to reverse phylogeny orientation.", ), "phylogeny.ladderize": ( False, - _validate_bool, + g("_validate_bool"), "Whether to ladderize phylogeny branches.", ), # Sankey diagrams "sankey.align": ( "center", - _validate_belongs("center", "left", "right", "justify"), + g("_validate_belongs")("center", "left", "right", "justify"), "Horizontal alignment of nodes.", ), "sankey.connect": ( (0, 0), - _validate_tuple_int_2, + g("_validate_tuple_int_2"), "Connection path for Sankey diagram.", ), "sankey.flow_labels": ( False, - _validate_bool, + g("_validate_bool"), "Whether to draw flow labels.", ), "sankey.flow_label_pos": ( 0.5, - _validate_float, + g("_validate_float"), "Position of flow labels along the flow.", ), "sankey.flow_sort": ( True, - _validate_bool, + g("_validate_bool"), "Whether to sort flows.", ), "sankey.node_labels": ( True, - _validate_bool, + g("_validate_bool"), "Whether to draw node labels.", ), "sankey.node_label_offset": ( 0.01, - _validate_float, + g("_validate_float"), "Offset for node labels.", ), "sankey.node_label_outside": ( "auto", - _validate_bool_or_string, + g("_validate_bool_or_string"), "Position of node labels relative to the node.", ), "sankey.other_label": ( "Other", - _validate_string, + g("_validate_string"), "Label for 'other' category in Sankey diagram.", ), "sankey.pathlabel": ( "", - _validate_string, + g("_validate_string"), "Label for the patch.", ), "sankey.pathlengths": ( 0.25, - _validate_float, + g("_validate_float"), "Path lengths for Sankey diagram.", ), "sankey.rotation": ( 0.0, - _validate_float, + g("_validate_float"), "Rotation of the Sankey diagram.", ), "sankey.trunklength": ( 1.0, - _validate_float, + g("_validate_float"), "Trunk length for Sankey diagram.", ), # Subplots settings "subplots.align": ( False, - _validate_bool, + g("_validate_bool"), "Whether to align axis labels during draw. See `aligning labels " "`__.", # noqa: E501 ), "subplots.equalspace": ( False, - _validate_bool, + g("_validate_bool"), "Whether to make the tight layout algorithm assign the same space for " "every row and the same space for every column.", ), "subplots.groupspace": ( True, - _validate_bool, + g("_validate_bool"), "Whether to make the tight layout algorithm consider space between only " 'adjacent subplot "groups" rather than every subplot in the row or column.', ), "subplots.innerpad": ( 1, - _validate_em, - "Padding between adjacent subplots." + _addendum_em, + g("_validate_em"), + "Padding between adjacent subplots." + g("_addendum_em"), ), "subplots.outerpad": ( 0.5, - _validate_em, - "Padding around figure edge." + _addendum_em, + g("_validate_em"), + "Padding around figure edge." + g("_addendum_em"), ), "subplots.panelpad": ( 0.5, - _validate_em, + g("_validate_em"), "Padding between subplots and panels, and between stacked panels." - + _addendum_em, + + g("_addendum_em"), ), "subplots.panelwidth": ( 0.5, - _validate_in, - "Width of side panels." + _addendum_in, + g("_validate_in"), + "Width of side panels." + g("_addendum_in"), ), "subplots.refwidth": ( 2.5, - _validate_in, - "Default width of the reference subplot." + _addendum_in, + g("_validate_in"), + "Default width of the reference subplot." + g("_addendum_in"), ), "subplots.share": ( "auto", - _validate_belongs( + g("_validate_belongs")( 0, 1, 2, 3, 4, False, "labels", "limits", True, "all", "auto" ), "The axis sharing level, one of ``0``, ``1``, ``2``, or ``3``, or the " @@ -1296,181 +1273,204 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: ), "subplots.span": ( True, - _validate_bool, + g("_validate_bool"), "Toggles spanning axis labels. See `~ultraplot.ui.subplots` for details.", ), "subplots.tight": ( True, - _validate_bool, + g("_validate_bool"), "Whether to auto-adjust the subplot spaces and figure margins.", ), "subplots.pixelsnap": ( False, - _validate_bool, + g("_validate_bool"), "Whether to snap subplot bounds to the renderer pixel grid during draw.", ), # Super title settings - "suptitle.color": (BLACK, _validate_color, "Figure title color."), + "suptitle.color": (g("BLACK"), g("_validate_color"), "Figure title color."), "suptitle.pad": ( - TITLEPAD, - _validate_pt, - "Padding between axes content and the figure super title." + _addendum_pt, + g("TITLEPAD"), + g("_validate_pt"), + "Padding between axes content and the figure super title." + g("_addendum_pt"), ), "suptitle.size": ( - LARGESIZE, - _validate_fontsize, - "Figure title font size." + _addendum_font, + g("LARGESIZE"), + g("_validate_fontsize"), + "Figure title font size." + g("_addendum_font"), ), - "suptitle.weight": ("bold", _validate_fontweight, "Figure title font weight."), + "suptitle.weight": ("bold", g("_validate_fontweight"), "Figure title font weight."), # Tick settings - "tick.color": (BLACK, _validate_color, "Major and minor tick color."), + "tick.color": (g("BLACK"), g("_validate_color"), "Major and minor tick color."), "tick.dir": ( - TICKDIR, - _validate_belongs("in", "out", "inout"), + g("TICKDIR"), + g("_validate_belongs")("in", "out", "inout"), "Major and minor tick direction. Must be one of " "``'out'``, ``'in'``, or ``'inout'``.", ), - "tick.labelcolor": (BLACK, _validate_color, "Axis tick label color."), + "tick.labelcolor": (g("BLACK"), g("_validate_color"), "Axis tick label color."), "tick.labelpad": ( - TICKPAD, - _validate_pt, - "Padding between ticks and tick labels." + _addendum_pt, + g("TICKPAD"), + g("_validate_pt"), + "Padding between ticks and tick labels." + g("_addendum_pt"), ), "tick.labelsize": ( - SMALLSIZE, - _validate_fontsize, - "Axis tick label font size." + _addendum_font, + g("SMALLSIZE"), + g("_validate_fontsize"), + "Axis tick label font size." + g("_addendum_font"), ), "tick.labelweight": ( "normal", - _validate_fontweight, + g("_validate_fontweight"), "Axis tick label font weight.", ), - "tick.len": (TICKLEN, _validate_pt, "Length of major ticks in points."), + "tick.len": (g("TICKLEN"), g("_validate_pt"), "Length of major ticks in points."), "tick.lenratio": ( - TICKLENRATIO, - _validate_float, + g("TICKLENRATIO"), + g("_validate_float"), "Ratio of minor tickline length to major tickline length.", ), - "tick.linewidth": (LINEWIDTH, _validate_pt, "Major tickline width."), + "tick.linewidth": (g("LINEWIDTH"), g("_validate_pt"), "Major tickline width."), "tick.minor": ( - TICKMINOR, - _validate_bool, + g("TICKMINOR"), + g("_validate_bool"), "Toggles minor ticks on and off.", ), - "tick.pad": (TICKPAD, _validate_pt, "Alias for :rcraw:`tick.labelpad`."), + "tick.pad": (g("TICKPAD"), g("_validate_pt"), "Alias for :rcraw:`tick.labelpad`."), "tick.width": ( - LINEWIDTH, - _validate_pt, + g("LINEWIDTH"), + g("_validate_pt"), "Major tickline width. Alias for :rcraw:`tick.linewidth`.", ), "tick.widthratio": ( - TICKWIDTHRATIO, - _validate_float, + g("TICKWIDTHRATIO"), + g("_validate_float"), "Ratio of minor tickline width to major tickline width.", ), # Title settings "title.above": ( True, - _validate_belongs(False, True, "panels"), + g("_validate_belongs")(False, True, "panels"), "Whether to move outer titles and a-b-c labels above panels, colorbars, or " "legends that are above the axes. If the string 'panels' then text is only " "redirected above axes panels. Otherwise should be boolean.", ), "title.border": ( True, - _validate_bool, + g("_validate_bool"), "Whether to draw a white border around titles " "when :rcraw:`title.loc` is inside the axes.", ), - "title.borderwidth": (1.5, _validate_pt, "Width of the border around titles."), + "title.borderwidth": (1.5, g("_validate_pt"), "Width of the border around titles."), "title.bbox": ( False, - _validate_bool, + g("_validate_bool"), "Whether to draw semi-transparent bounding boxes around titles " "when :rcraw:`title.loc` is inside the axes.", ), - "title.bboxcolor": (WHITE, _validate_color, "Axes title bounding box color."), + "title.bboxcolor": (g("WHITE"), g("_validate_color"), "Axes title bounding box color."), "title.bboxstyle": ( "square", - _validate_boxstyle, + g("_validate_boxstyle"), "Axes title bounding box style.", ), - "title.bboxalpha": (0.5, _validate_float, "Axes title bounding box opacity."), + "title.bboxalpha": (0.5, g("_validate_float"), "Axes title bounding box opacity."), "title.bboxpad": ( None, - _validate_or_none(_validate_pt), + g("_validate_or_none")(g("_validate_pt")), "Padding for the title bounding box. By default this is scaled " - "to make the box flush against the axes edge." + _addendum_pt, + "to make the box flush against the axes edge." + g("_addendum_pt"), ), "title.color": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Axes title color. Alias for :rcraw:`axes.titlecolor`.", ), "title.loc": ( "center", - _validate_belongs(*TEXT_LOCS), + g("_validate_belongs")(*g("TEXT_LOCS")), "Title position. For options see the :ref:`location table `.", ), "title.pad": ( - TITLEPAD, - _validate_pt, + g("TITLEPAD"), + g("_validate_pt"), "Padding between the axes edge and the inner and outer titles and " - "a-b-c labels. Alias for :rcraw:`axes.titlepad`." + _addendum_pt, + "a-b-c labels. Alias for :rcraw:`axes.titlepad`." + g("_addendum_pt"), ), "title.size": ( - LARGESIZE, - _validate_fontsize, - "Axes title font size. Alias for :rcraw:`axes.titlesize`." + _addendum_font, + g("LARGESIZE"), + g("_validate_fontsize"), + "Axes title font size. Alias for :rcraw:`axes.titlesize`." + g("_addendum_font"), ), "title.weight": ( "normal", - _validate_fontweight, + g("_validate_fontweight"), "Axes title font weight. Alias for :rcraw:`axes.titleweight`.", ), # Top subplot label settings "toplabel.color": ( - BLACK, - _validate_color, + g("BLACK"), + g("_validate_color"), "Font color for column labels on the top of the figure.", ), "toplabel.pad": ( - TITLEPAD, - _validate_pt, + g("TITLEPAD"), + g("_validate_pt"), "Padding between axes content and column labels on the top of the figure." - + _addendum_pt, + + g("_addendum_pt"), ), "toplabel.rotation": ( "horizontal", - _validate_rotation, - "Rotation for column labels at the top of the figure." + _addendum_rotation, + g("_validate_rotation"), + "Rotation for column labels at the top of the figure." + g("_addendum_rotation"), ), "toplabel.size": ( - LARGESIZE, - _validate_fontsize, - "Font size for column labels on the top of the figure." + _addendum_font, + g("LARGESIZE"), + g("_validate_fontsize"), + "Font size for column labels on the top of the figure." + g("_addendum_font"), ), "toplabel.weight": ( "bold", - _validate_fontweight, + g("_validate_fontweight"), "Font weight for column labels on the top of the figure.", ), # Unit formatting "unitformat": ( "L", - _validate_string, + g("_validate_string"), "The format string used to format `pint.Quantity` default unit labels " "using ``format(units, unitformat)``. See also :rcraw:`autoformat`.", ), "ultraplot.check_for_latest_version": ( False, - _validate_bool, + g("_validate_bool"), "Whether to check for the latest version of UltraPlot on PyPI when importing", ), "ultraplot.eager_import": ( False, - _validate_bool, + g("_validate_bool"), "Whether to import the full public API during setup instead of lazily.", ), } + + plot_entries = _section_entries( + raw_settings, + lambda key: key.startswith(_PLOT_PREFIXES), + ) + text_entries = _section_entries( + raw_settings, + lambda key: key.startswith(_TEXT_PREFIXES), + ) + subplot_entries = _section_entries( + raw_settings, + lambda key: key.startswith(_SUBPLOT_PREFIX), + ) + misc_entries = _section_entries( + raw_settings, + lambda key: ( + not key.startswith(_PLOT_PREFIXES) + and not key.startswith(_TEXT_PREFIXES) + and not key.startswith(_SUBPLOT_PREFIX) + ), + ) + + return merge_rc_tables(plot_entries, text_entries, subplot_entries, misc_entries) From 0dab6460c430ef840f85ea2df4bcd605799ae578 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Fri, 13 Feb 2026 14:58:33 +1000 Subject: [PATCH 7/9] Restore missing ribbon rc defaults after rc settings refactor --- ultraplot/internals/rc/settings.py | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/ultraplot/internals/rc/settings.py b/ultraplot/internals/rc/settings.py index bf57d2e81..60c6da8f6 100644 --- a/ultraplot/internals/rc/settings.py +++ b/ultraplot/internals/rc/settings.py @@ -129,6 +129,57 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: v["color"], "Default node facecolor for layered sankey diagrams.", ), + # Ribbon settings + "ribbon.xmargin": ( + 0.12, + v["float"], + "Horizontal margin around ribbon diagrams (axes-relative units).", + ), + "ribbon.ymargin": ( + 0.08, + v["float"], + "Vertical margin around ribbon diagrams (axes-relative units).", + ), + "ribbon.rowheightratio": ( + 2.2, + v["float"], + "Height scale factor controlling ribbon row occupancy.", + ), + "ribbon.nodewidth": ( + 0.018, + v["float"], + "Node width for ribbon diagrams (axes-relative units).", + ), + "ribbon.flow.curvature": ( + 0.45, + v["float"], + "Flow curvature for ribbon diagrams.", + ), + "ribbon.flow.alpha": ( + 0.58, + v["float"], + "Flow transparency for ribbon diagrams.", + ), + "ribbon.topic_labels": ( + True, + v["bool"], + "Whether to draw topic labels on the right side of ribbon diagrams.", + ), + "ribbon.topic_label_offset": ( + 0.028, + v["float"], + "Offset for right-side ribbon topic labels.", + ), + "ribbon.topic_label_size": ( + 7.4, + v["float"], + "Font size for ribbon topic labels.", + ), + "ribbon.topic_label_box": ( + True, + v["bool"], + "Whether to draw backing boxes behind ribbon topic labels.", + ), "external.shrink": ( 0.9, g("_validate_float"), From 2bff1c49baab54ad23dffde249b2d972fbee6903 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Fri, 13 Feb 2026 15:06:54 +1000 Subject: [PATCH 8/9] Black --- ultraplot/internals/rc/settings.py | 171 +++++++++++++++++++++++------ ultraplot/tests/test_config.py | 2 + 2 files changed, 141 insertions(+), 32 deletions(-) diff --git a/ultraplot/internals/rc/settings.py b/ultraplot/internals/rc/settings.py index 60c6da8f6..bfca7ae51 100644 --- a/ultraplot/internals/rc/settings.py +++ b/ultraplot/internals/rc/settings.py @@ -255,13 +255,21 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "Whether to draw semi-transparent bounding boxes around a-b-c labels " "when :rcraw:`abc.loc` is inside the axes.", ), - "abc.bboxcolor": (g("WHITE"), g("_validate_color"), "a-b-c label bounding box color."), + "abc.bboxcolor": ( + g("WHITE"), + g("_validate_color"), + "a-b-c label bounding box color.", + ), "abc.bboxstyle": ( "square", g("_validate_boxstyle"), "a-b-c label bounding box style.", ), - "abc.bboxalpha": (0.5, g("_validate_float"), "a-b-c label bounding box opacity."), + "abc.bboxalpha": ( + 0.5, + g("_validate_float"), + "a-b-c label bounding box opacity.", + ), "abc.bboxpad": ( None, g("_validate_or_none")(g("_validate_pt")), @@ -318,7 +326,11 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "Add value of the bars to the bar labels", ), # Country borders - "borders": (False, g("_validate_bool"), "Toggles country border lines on and off."), + "borders": ( + False, + g("_validate_bool"), + "Toggles country border lines on and off.", + ), "borders.alpha": ( None, g("_validate_or_none")(g("_validate_float")), @@ -365,7 +377,8 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "bottomlabel.size": ( g("LARGESIZE"), g("_validate_fontsize"), - "Font size for column labels on the bottom of the figure." + g("_addendum_font"), + "Font size for column labels on the bottom of the figure." + + g("_addendum_font"), ), "bottomlabel.weight": ( "bold", @@ -406,8 +419,16 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_or_none")(g("_validate_float")), "Opacity for coast lines", ), - "coast.color": (g("BLACK"), g("_validate_color"), "Line color for coast lines."), - "coast.linewidth": (g("LINEWIDTH"), g("_validate_pt"), "Line width for coast lines."), + "coast.color": ( + g("BLACK"), + g("_validate_color"), + "Line color for coast lines.", + ), + "coast.linewidth": ( + g("LINEWIDTH"), + g("_validate_pt"), + "Line width for coast lines.", + ), "coast.zorder": (g("ZLINES"), g("_validate_float"), "Z-order for coast lines."), "coast.rasterized": ( False, @@ -595,7 +616,11 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "to colorbar levels and bar, area, pcolor, and contour plots.", ), # Font settings - "font.name": (g("FONTNAME"), g("_validate_fontname"), "Alias for :rcraw:`font.family`."), + "font.name": ( + g("FONTNAME"), + g("_validate_fontname"), + "Alias for :rcraw:`font.family`.", + ), "font.small": ( g("SMALLSIZE"), g("_validate_fontsize"), @@ -618,7 +643,8 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_fontsize"), "Meta setting that changes the title-like sizes ``abc.size``, ``title.size``, " "``suptitle.size``, ``leftlabel.size``, ``rightlabel.size``, etc. Default is " - "``'med-large'`` (i.e. 1.1 times :rcraw:`font.size`)." + g("_addendum_font"), + "``'med-large'`` (i.e. 1.1 times :rcraw:`font.size`)." + + g("_addendum_font"), ), # Formatter settings "formatter.timerotation": ( @@ -713,7 +739,11 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_belongs")("equal", "auto"), "The aspect ratio of the graph.", ), - "graph.facecolor": ("none", g("_validate_color"), "The facecolor of the graph."), + "graph.facecolor": ( + "none", + g("_validate_color"), + "The facecolor of the graph.", + ), "graph.draw_spines": ( False, g("_validate_bool_or_iterable"), @@ -778,7 +808,8 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "grid.labelsize": ( g("SMALLSIZE"), g("_validate_fontsize"), - "Font size for `~ultraplot.axes.GeoAxes` gridline labels." + g("_addendum_font"), + "Font size for `~ultraplot.axes.GeoAxes` gridline labels." + + g("_addendum_font"), ), "grid.labelweight": ( "normal", @@ -790,7 +821,11 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_int"), "Number of points used to draw cartopy `~ultraplot.axes.GeoAxes` gridlines.", ), - "grid.pad": (g("GRIDPAD"), g("_validate_pt"), "Alias for :rcraw:`grid.labelpad`."), + "grid.pad": ( + g("GRIDPAD"), + g("_validate_pt"), + "Alias for :rcraw:`grid.labelpad`.", + ), "grid.rotatelabels": ( False, # False limits projections where labels are available g("_validate_bool"), @@ -813,7 +848,11 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: ), # Minor gridlines "gridminor": (False, g("_validate_bool"), "Toggle minor gridlines on and off."), - "gridminor.alpha": (g("GRIDALPHA"), g("_validate_float"), "Minor gridline opacity."), + "gridminor.alpha": ( + g("GRIDALPHA"), + g("_validate_float"), + "Minor gridline opacity.", + ), "gridminor.color": (g("BLACK"), g("_validate_color"), "Minor gridline color."), "gridminor.linestyle": ( g("GRIDSTYLE"), @@ -870,7 +909,11 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "Z-order for internal political border lines.", ), # Axis label settings - "label.color": (g("BLACK"), g("_validate_color"), "Alias for :rcraw:`axes.labelcolor`."), + "label.color": ( + g("BLACK"), + g("_validate_color"), + "Alias for :rcraw:`axes.labelcolor`.", + ), "label.pad": ( g("LABELPAD"), g("_validate_pt"), @@ -893,8 +936,16 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_or_none")(g("_validate_float")), "Opacity for lake patches", ), - "lakes.color": (g("WHITE"), g("_validate_color"), "Face color for lake patches."), - "lakes.zorder": (g("ZPATCHES"), g("_validate_float"), "Z-order for lake patches."), + "lakes.color": ( + g("WHITE"), + g("_validate_color"), + "Face color for lake patches.", + ), + "lakes.zorder": ( + g("ZPATCHES"), + g("_validate_float"), + "Z-order for lake patches.", + ), "lakes.rasterized": ( False, g("_validate_bool"), @@ -907,8 +958,16 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_or_none")(g("_validate_float")), "Opacity for land patches", ), - "land.color": (g("BLACK"), g("_validate_color"), "Face color for land patches."), - "land.zorder": (g("ZPATCHES"), g("_validate_float"), "Z-order for land patches."), + "land.color": ( + g("BLACK"), + g("_validate_color"), + "Face color for land patches.", + ), + "land.zorder": ( + g("ZPATCHES"), + g("_validate_float"), + "Z-order for land patches.", + ), "land.rasterized": ( False, g("_validate_bool"), @@ -1010,8 +1069,16 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_or_none")(g("_validate_float")), "Opacity for ocean patches", ), - "ocean.color": (g("WHITE"), g("_validate_color"), "Face color for ocean patches."), - "ocean.zorder": (g("ZPATCHES"), g("_validate_float"), "Z-order for ocean patches."), + "ocean.color": ( + g("WHITE"), + g("_validate_color"), + "Face color for ocean patches.", + ), + "ocean.zorder": ( + g("ZPATCHES"), + g("_validate_float"), + "Z-order for ocean patches.", + ), "ocean.rasterized": ( False, g("_validate_bool"), @@ -1058,9 +1125,21 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_or_none")(g("_validate_float")), "Opacity for river lines.", ), - "rivers.color": (g("BLACK"), g("_validate_color"), "Line color for river lines."), - "rivers.linewidth": (g("LINEWIDTH"), g("_validate_pt"), "Line width for river lines."), - "rivers.zorder": (g("ZLINES"), g("_validate_float"), "Z-order for river lines."), + "rivers.color": ( + g("BLACK"), + g("_validate_color"), + "Line color for river lines.", + ), + "rivers.linewidth": ( + g("LINEWIDTH"), + g("_validate_pt"), + "Line width for river lines.", + ), + "rivers.zorder": ( + g("ZLINES"), + g("_validate_float"), + "Z-order for river lines.", + ), "rivers.rasterized": ( False, g("_validate_bool"), @@ -1342,14 +1421,19 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "suptitle.pad": ( g("TITLEPAD"), g("_validate_pt"), - "Padding between axes content and the figure super title." + g("_addendum_pt"), + "Padding between axes content and the figure super title." + + g("_addendum_pt"), ), "suptitle.size": ( g("LARGESIZE"), g("_validate_fontsize"), "Figure title font size." + g("_addendum_font"), ), - "suptitle.weight": ("bold", g("_validate_fontweight"), "Figure title font weight."), + "suptitle.weight": ( + "bold", + g("_validate_fontweight"), + "Figure title font weight.", + ), # Tick settings "tick.color": (g("BLACK"), g("_validate_color"), "Major and minor tick color."), "tick.dir": ( @@ -1374,7 +1458,11 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_fontweight"), "Axis tick label font weight.", ), - "tick.len": (g("TICKLEN"), g("_validate_pt"), "Length of major ticks in points."), + "tick.len": ( + g("TICKLEN"), + g("_validate_pt"), + "Length of major ticks in points.", + ), "tick.lenratio": ( g("TICKLENRATIO"), g("_validate_float"), @@ -1386,7 +1474,11 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: g("_validate_bool"), "Toggles minor ticks on and off.", ), - "tick.pad": (g("TICKPAD"), g("_validate_pt"), "Alias for :rcraw:`tick.labelpad`."), + "tick.pad": ( + g("TICKPAD"), + g("_validate_pt"), + "Alias for :rcraw:`tick.labelpad`.", + ), "tick.width": ( g("LINEWIDTH"), g("_validate_pt"), @@ -1411,20 +1503,32 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "Whether to draw a white border around titles " "when :rcraw:`title.loc` is inside the axes.", ), - "title.borderwidth": (1.5, g("_validate_pt"), "Width of the border around titles."), + "title.borderwidth": ( + 1.5, + g("_validate_pt"), + "Width of the border around titles.", + ), "title.bbox": ( False, g("_validate_bool"), "Whether to draw semi-transparent bounding boxes around titles " "when :rcraw:`title.loc` is inside the axes.", ), - "title.bboxcolor": (g("WHITE"), g("_validate_color"), "Axes title bounding box color."), + "title.bboxcolor": ( + g("WHITE"), + g("_validate_color"), + "Axes title bounding box color.", + ), "title.bboxstyle": ( "square", g("_validate_boxstyle"), "Axes title bounding box style.", ), - "title.bboxalpha": (0.5, g("_validate_float"), "Axes title bounding box opacity."), + "title.bboxalpha": ( + 0.5, + g("_validate_float"), + "Axes title bounding box opacity.", + ), "title.bboxpad": ( None, g("_validate_or_none")(g("_validate_pt")), @@ -1450,7 +1554,8 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "title.size": ( g("LARGESIZE"), g("_validate_fontsize"), - "Axes title font size. Alias for :rcraw:`axes.titlesize`." + g("_addendum_font"), + "Axes title font size. Alias for :rcraw:`axes.titlesize`." + + g("_addendum_font"), ), "title.weight": ( "normal", @@ -1472,12 +1577,14 @@ def build_settings_rc_table(ns: Mapping[str, Any]) -> RcTable: "toplabel.rotation": ( "horizontal", g("_validate_rotation"), - "Rotation for column labels at the top of the figure." + g("_addendum_rotation"), + "Rotation for column labels at the top of the figure." + + g("_addendum_rotation"), ), "toplabel.size": ( g("LARGESIZE"), g("_validate_fontsize"), - "Font size for column labels on the top of the figure." + g("_addendum_font"), + "Font size for column labels on the top of the figure." + + g("_addendum_font"), ), "toplabel.weight": ( "bold", diff --git a/ultraplot/tests/test_config.py b/ultraplot/tests/test_config.py index 7aaabb80f..cd7d4358e 100644 --- a/ultraplot/tests/test_config.py +++ b/ultraplot/tests/test_config.py @@ -65,6 +65,8 @@ def test_curved_quiver_rc_defaults(): assert uplt.rc["curved_quiver.grains"] == 15 assert uplt.rc["curved_quiver.density"] == 10 assert uplt.rc["curved_quiver.arrows_at_end"] is True + + def test_ribbon_rc_defaults(): """ Sanity check ribbon defaults in rc. From 44216f96f1cc8b6a8089eddaae6638776b704b1b Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Fri, 13 Feb 2026 16:02:33 +1000 Subject: [PATCH 9/9] CI: invalidate cached baselines to stop stale centered-legend image diffs (cherry picked from commit 3c34186bed70b66597fc4f22ddb08a85c398e194) --- .github/workflows/build-ultraplot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-ultraplot.yml b/.github/workflows/build-ultraplot.yml index 3a2e9a9eb..4fe9693e9 100644 --- a/.github/workflows/build-ultraplot.yml +++ b/.github/workflows/build-ultraplot.yml @@ -136,9 +136,9 @@ jobs: with: path: ./ultraplot/tests/baseline # The directory to cache # Key is based on OS, Python/Matplotlib versions, and the base commit SHA - key: ${{ runner.os }}-baseline-base-v4-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }} + key: ${{ runner.os }}-baseline-base-v5-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }} restore-keys: | - ${{ runner.os }}-baseline-base-v4-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}- + ${{ runner.os }}-baseline-base-v5-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}- # Conditional Baseline Generation (Only runs on cache miss) - name: Generate baseline from main