From 255f7361cac4c88f395b83504a7cbbf5b91d80f2 Mon Sep 17 00:00:00 2001 From: Dan Redding <125183946+dangotbanned@users.noreply.github.com> Date: Mon, 11 Nov 2024 17:13:14 +0000 Subject: [PATCH] refactor: `alt.themes` -> `alt.theme` (#3618) --- altair/__init__.py | 38 +- altair/theme.py | 321 +++++++++++++ altair/typing/__init__.py | 4 - altair/typing/theme.py | 1 - altair/utils/deprecation.py | 87 +++- altair/utils/plugin_registry.py | 16 +- altair/utils/theme.py | 52 --- altair/vegalite/v5/__init__.py | 644 +++++++++++++++++++++++++- altair/vegalite/v5/api.py | 8 +- altair/vegalite/v5/schema/__init__.py | 569 ++++++++++++++++++++++- altair/vegalite/v5/schema/_config.py | 134 +++--- altair/vegalite/v5/theme.py | 145 +++--- doc/user_guide/api.rst | 102 +++- doc/user_guide/customization.rst | 54 +-- tests/utils/test_deprecation.py | 24 + tests/vegalite/test_common.py | 13 +- tests/vegalite/v5/test_api.py | 8 +- tests/vegalite/v5/test_theme.py | 81 +++- tools/generate_api_docs.py | 43 +- tools/generate_schema_wrapper.py | 116 ++++- tools/update_init_file.py | 119 ++++- 21 files changed, 2268 insertions(+), 311 deletions(-) create mode 100644 altair/theme.py delete mode 100644 altair/typing/theme.py delete mode 100644 altair/utils/theme.py diff --git a/altair/__init__.py b/altair/__init__.py index d4e20f02f..cbc2c3b52 100644 --- a/altair/__init__.py +++ b/altair/__init__.py @@ -617,7 +617,6 @@ "mixins", "param", "parse_shorthand", - "register_theme", "renderers", "repeat", "sample", @@ -627,7 +626,6 @@ "sequence", "sphere", "theme", - "themes", "to_csv", "to_json", "to_values", @@ -653,10 +651,44 @@ def __dir__(): from altair.jupyter import JupyterChart from altair.expr import expr from altair.utils import AltairDeprecationWarning, parse_shorthand, Undefined -from altair import typing +from altair import typing, theme def load_ipython_extension(ipython): from altair._magics import vegalite ipython.register_magic_function(vegalite, "cell") + + +def __getattr__(name: str): + from altair.utils.deprecation import deprecated_warn + + if name == "themes": + deprecated_warn( + "Most cases require only the following change:\n\n" + " # Deprecated\n" + " alt.themes.enable('quartz')\n\n" + " # Updated\n" + " alt.theme.enable('quartz')\n\n" + "If your code registers a theme, make the following change:\n\n" + " # Deprecated\n" + " def custom_theme():\n" + " return {'height': 400, 'width': 700}\n" + " alt.themes.register('theme_name', custom_theme)\n" + " alt.themes.enable('theme_name')\n\n" + " # Updated\n" + " @alt.theme.register('theme_name', enable=True)\n" + " def custom_theme() -> alt.theme.ThemeConfig:\n" + " return {'height': 400, 'width': 700}\n\n" + "See the updated User Guide for further details:\n" + " https://altair-viz.github.io/user_guide/api.html#theme\n" + " https://altair-viz.github.io/user_guide/customization.html#chart-themes", + version="5.5.0", + alternative="altair.theme", + stacklevel=3, + action="once", + ) + return theme._themes + else: + msg = f"module {__name__!r} has no attribute {name!r}" + raise AttributeError(msg) diff --git a/altair/theme.py b/altair/theme.py new file mode 100644 index 000000000..e77a95eb8 --- /dev/null +++ b/altair/theme.py @@ -0,0 +1,321 @@ +"""Customizing chart configuration defaults.""" + +from __future__ import annotations + +from functools import wraps as _wraps +from typing import TYPE_CHECKING, Any +from typing import overload as _overload + +from altair.vegalite.v5.schema._config import ( + AreaConfigKwds, + AutoSizeParamsKwds, + AxisConfigKwds, + AxisResolveMapKwds, + BarConfigKwds, + BindCheckboxKwds, + BindDirectKwds, + BindInputKwds, + BindRadioSelectKwds, + BindRangeKwds, + BoxPlotConfigKwds, + BrushConfigKwds, + CompositionConfigKwds, + ConfigKwds, + DateTimeKwds, + DerivedStreamKwds, + ErrorBandConfigKwds, + ErrorBarConfigKwds, + FeatureGeometryGeoJsonPropertiesKwds, + FormatConfigKwds, + GeoJsonFeatureCollectionKwds, + GeoJsonFeatureKwds, + GeometryCollectionKwds, + GradientStopKwds, + HeaderConfigKwds, + IntervalSelectionConfigKwds, + IntervalSelectionConfigWithoutTypeKwds, + LegendConfigKwds, + LegendResolveMapKwds, + LegendStreamBindingKwds, + LinearGradientKwds, + LineConfigKwds, + LineStringKwds, + LocaleKwds, + MarkConfigKwds, + MergedStreamKwds, + MultiLineStringKwds, + MultiPointKwds, + MultiPolygonKwds, + NumberLocaleKwds, + OverlayMarkDefKwds, + PaddingKwds, + PointKwds, + PointSelectionConfigKwds, + PointSelectionConfigWithoutTypeKwds, + PolygonKwds, + ProjectionConfigKwds, + ProjectionKwds, + RadialGradientKwds, + RangeConfigKwds, + RectConfigKwds, + ResolveKwds, + RowColKwds, + ScaleConfigKwds, + ScaleInvalidDataConfigKwds, + ScaleResolveMapKwds, + SelectionConfigKwds, + StepKwds, + StyleConfigIndexKwds, + ThemeConfig, + TickConfigKwds, + TimeIntervalStepKwds, + TimeLocaleKwds, + TitleConfigKwds, + TitleParamsKwds, + TooltipContentKwds, + TopLevelSelectionParameterKwds, + VariableParameterKwds, + ViewBackgroundKwds, + ViewConfigKwds, +) +from altair.vegalite.v5.theme import themes as _themes + +if TYPE_CHECKING: + import sys + from typing import Any, Callable, Literal + + if sys.version_info >= (3, 11): + from typing import LiteralString + else: + from typing_extensions import LiteralString + if sys.version_info >= (3, 10): + from typing import ParamSpec + else: + from typing_extensions import ParamSpec + + from altair.utils.plugin_registry import Plugin + + P = ParamSpec("P") + +__all__ = [ + "AreaConfigKwds", + "AutoSizeParamsKwds", + "AxisConfigKwds", + "AxisResolveMapKwds", + "BarConfigKwds", + "BindCheckboxKwds", + "BindDirectKwds", + "BindInputKwds", + "BindRadioSelectKwds", + "BindRangeKwds", + "BoxPlotConfigKwds", + "BrushConfigKwds", + "CompositionConfigKwds", + "ConfigKwds", + "DateTimeKwds", + "DerivedStreamKwds", + "ErrorBandConfigKwds", + "ErrorBarConfigKwds", + "FeatureGeometryGeoJsonPropertiesKwds", + "FormatConfigKwds", + "GeoJsonFeatureCollectionKwds", + "GeoJsonFeatureKwds", + "GeometryCollectionKwds", + "GradientStopKwds", + "HeaderConfigKwds", + "IntervalSelectionConfigKwds", + "IntervalSelectionConfigWithoutTypeKwds", + "LegendConfigKwds", + "LegendResolveMapKwds", + "LegendStreamBindingKwds", + "LineConfigKwds", + "LineStringKwds", + "LinearGradientKwds", + "LocaleKwds", + "MarkConfigKwds", + "MergedStreamKwds", + "MultiLineStringKwds", + "MultiPointKwds", + "MultiPolygonKwds", + "NumberLocaleKwds", + "OverlayMarkDefKwds", + "PaddingKwds", + "PointKwds", + "PointSelectionConfigKwds", + "PointSelectionConfigWithoutTypeKwds", + "PolygonKwds", + "ProjectionConfigKwds", + "ProjectionKwds", + "RadialGradientKwds", + "RangeConfigKwds", + "RectConfigKwds", + "ResolveKwds", + "RowColKwds", + "ScaleConfigKwds", + "ScaleInvalidDataConfigKwds", + "ScaleResolveMapKwds", + "SelectionConfigKwds", + "StepKwds", + "StyleConfigIndexKwds", + "ThemeConfig", + "TickConfigKwds", + "TimeIntervalStepKwds", + "TimeLocaleKwds", + "TitleConfigKwds", + "TitleParamsKwds", + "TooltipContentKwds", + "TopLevelSelectionParameterKwds", + "VariableParameterKwds", + "ViewBackgroundKwds", + "ViewConfigKwds", + "active", + "enable", + "get", + "names", + "options", + "register", + "unregister", +] + + +def register( + name: LiteralString, *, enable: bool +) -> Callable[[Plugin[ThemeConfig]], Plugin[ThemeConfig]]: + """ + Decorator for registering a theme function. + + Parameters + ---------- + name + Unique name assigned in registry. + enable + Auto-enable the wrapped theme. + + Examples + -------- + Register and enable a theme:: + + import altair as alt + from altair import theme + + + @theme.register("param_font_size", enable=True) + def custom_theme() -> theme.ThemeConfig: + sizes = 12, 14, 16, 18, 20 + return { + "autosize": {"contains": "content", "resize": True}, + "background": "#F3F2F1", + "config": { + "axisX": {"labelFontSize": sizes[1], "titleFontSize": sizes[1]}, + "axisY": {"labelFontSize": sizes[1], "titleFontSize": sizes[1]}, + "font": "'Lato', 'Segoe UI', Tahoma, Verdana, sans-serif", + "headerColumn": {"labelFontSize": sizes[1]}, + "headerFacet": {"labelFontSize": sizes[1]}, + "headerRow": {"labelFontSize": sizes[1]}, + "legend": {"labelFontSize": sizes[0], "titleFontSize": sizes[1]}, + "text": {"fontSize": sizes[0]}, + "title": {"fontSize": sizes[-1]}, + }, + "height": {"step": 28}, + "width": 350, + } + + We can then see the ``name`` parameter displayed when checking:: + + theme.active + "param_font_size" + + Until another theme has been enabled, all charts will use defaults set in ``custom_theme()``:: + + from vega_datasets import data + + source = data.stocks() + lines = ( + alt.Chart(source, title=alt.Title("Stocks")) + .mark_line() + .encode(x="date:T", y="price:Q", color="symbol:N") + ) + lines.interactive(bind_y=False) + + """ + + # HACK: See for `LiteralString` requirement in `name` + # https://github.com/vega/altair/pull/3526#discussion_r1743350127 + def decorate(func: Plugin[ThemeConfig], /) -> Plugin[ThemeConfig]: + _register(name, func) + if enable: + _themes.enable(name) + + @_wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> ThemeConfig: + return func(*args, **kwargs) + + return wrapper + + return decorate + + +def unregister(name: LiteralString) -> Plugin[ThemeConfig]: + """ + Remove and return a previously registered theme. + + Parameters + ---------- + name + Unique name assigned during ``alt.theme.register``. + + Raises + ------ + TypeError + When ``name`` has not been registered. + """ + plugin = _register(name, None) + if plugin is None: + msg = ( + f"Found no theme named {name!r} in registry.\n" + f"Registered themes:\n" + f"{names()!r}" + ) + raise TypeError(msg) + else: + return plugin + + +enable = _themes.enable +get = _themes.get +names = _themes.names +active: str +"""Return the name of the currently active theme.""" +options: dict[str, Any] +"""Return the current themes options dictionary.""" + + +def __dir__() -> list[str]: + return __all__ + + +@_overload +def __getattr__(name: Literal["active"]) -> str: ... # type: ignore[misc] +@_overload +def __getattr__(name: Literal["options"]) -> dict[str, Any]: ... # type: ignore[misc] +def __getattr__(name: str) -> Any: + if name == "active": + return _themes.active + elif name == "options": + return _themes.options + else: + msg = f"module {__name__!r} has no attribute {name!r}" + raise AttributeError(msg) + + +def _register( + name: LiteralString, fn: Plugin[ThemeConfig] | None, / +) -> Plugin[ThemeConfig] | None: + if fn is None: + return _themes._plugins.pop(name, None) + elif _themes.plugin_type(fn): + _themes._plugins[name] = fn + return fn + else: + msg = f"{type(fn).__name__!r} is not a callable theme\n\n{fn!r}" + raise TypeError(msg) diff --git a/altair/typing/__init__.py b/altair/typing/__init__.py index d80469f35..cd8cb1489 100644 --- a/altair/typing/__init__.py +++ b/altair/typing/__init__.py @@ -46,13 +46,9 @@ "ChartType", "EncodeKwds", "Optional", - "ThemeConfig", "is_chart_type", - "theme", ] -from altair.typing import theme -from altair.typing.theme import ThemeConfig from altair.utils.schemapi import Optional from altair.vegalite.v5.api import ChartType, is_chart_type from altair.vegalite.v5.schema.channels import ( diff --git a/altair/typing/theme.py b/altair/typing/theme.py deleted file mode 100644 index 17f9a7fd2..000000000 --- a/altair/typing/theme.py +++ /dev/null @@ -1 +0,0 @@ -from altair.vegalite.v5.schema._config import * # noqa: F403 diff --git a/altair/utils/deprecation.py b/altair/utils/deprecation.py index f11dd1a41..2b07a229e 100644 --- a/altair/utils/deprecation.py +++ b/altair/utils/deprecation.py @@ -1,8 +1,9 @@ from __future__ import annotations import sys +import threading import warnings -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Literal if sys.version_info >= (3, 13): from warnings import deprecated as _deprecated @@ -16,6 +17,13 @@ else: from typing_extensions import LiteralString +__all__ = [ + "AltairDeprecationWarning", + "deprecated", + "deprecated_static_only", + "deprecated_warn", +] + class AltairDeprecationWarning(DeprecationWarning): ... @@ -26,7 +34,7 @@ def _format_message( message: LiteralString | None, /, ) -> LiteralString: - output = f"Deprecated in `altair={version}`." + output = f"\nDeprecated since `altair={version}`." if alternative: output = f"{output} Use {alternative} instead." return f"{output}\n{message}" if message else output @@ -81,6 +89,7 @@ def deprecated_warn( alternative: LiteralString | None = None, category: type[AltairDeprecationWarning] = AltairDeprecationWarning, stacklevel: int = 2, + action: Literal["once"] | None = None, ) -> None: """ Indicate that the current code path is deprecated. @@ -112,4 +121,76 @@ def deprecated_warn( [warnings.warn](https://docs.python.org/3/library/warnings.html#warnings.warn) """ msg = _format_message(version, alternative, message) - warnings.warn(msg, category=category, stacklevel=stacklevel) + if action is None: + warnings.warn(msg, category=category, stacklevel=stacklevel) + elif action == "once": + _warn_once(msg, category=category, stacklevel=stacklevel) + else: + raise NotImplementedError(action) + + +deprecated_static_only = _deprecated +""" +Using this decorator **exactly as described**, ensures ``message`` is displayed to a static type checker. + +**BE CAREFUL USING THIS**. + +See screenshots in `comment`_ for motivation. + +Every use should look like:: + + @deprecated_static_only( + "Deprecated since `altair=5.5.0`. Use altair.other instead.", + category=None, + ) + def old_function(*args): ... + +If a runtime warning is desired, use `@alt.utils.deprecated` instead. + +Parameters +---------- +message : LiteralString + - **Not** a variable + - **Not** use placeholders + - **Not** use concatenation + - **Do not use anything that could be considered dynamic** + +category : None + You **need** to explicitly pass ``None`` + +.. _comment: + https://github.com/vega/altair/pull/3618#issuecomment-2423991968 +--- +""" + + +class _WarningsMonitor: + def __init__(self) -> None: + self._warned: dict[LiteralString, Literal[True]] = {} + self._lock = threading.Lock() + + def __contains__(self, key: LiteralString, /) -> bool: + with self._lock: + return key in self._warned + + def hit(self, key: LiteralString, /) -> None: + with self._lock: + self._warned[key] = True + + def clear(self) -> None: + with self._lock: + self._warned.clear() + + +_warnings_monitor = _WarningsMonitor() + + +def _warn_once( + msg: LiteralString, /, *, category: type[AltairDeprecationWarning], stacklevel: int +) -> None: + global _warnings_monitor + if msg in _warnings_monitor: + return + else: + _warnings_monitor.hit(msg) + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) diff --git a/altair/utils/plugin_registry.py b/altair/utils/plugin_registry.py index b2723396a..b4505fd9f 100644 --- a/altair/utils/plugin_registry.py +++ b/altair/utils/plugin_registry.py @@ -39,7 +39,7 @@ def __str__(self): return f"No {self.name!r} entry point found in group {self.group!r}" -class PluginEnabler: +class PluginEnabler(Generic[PluginT, R]): """ Context manager for enabling plugins. @@ -51,21 +51,23 @@ class PluginEnabler: # plugins back to original state """ - def __init__(self, registry: PluginRegistry, name: str, **options): - self.registry: PluginRegistry = registry + def __init__( + self, registry: PluginRegistry[PluginT, R], name: str, **options: Any + ) -> None: + self.registry: PluginRegistry[PluginT, R] = registry self.name: str = name self.options: dict[str, Any] = options self.original_state: dict[str, Any] = registry._get_state() self.registry._enable(name, **options) - def __enter__(self) -> PluginEnabler: + def __enter__(self) -> PluginEnabler[PluginT, R]: return self def __exit__(self, typ: type, value: Exception, traceback: TracebackType) -> None: self.registry._set_state(self.original_state) def __repr__(self) -> str: - return f"{self.registry.__class__.__name__}.enable({self.name!r})" + return f"{type(self.registry).__name__}.enable({self.name!r})" class PluginRegistry(Generic[PluginT, R]): @@ -211,7 +213,9 @@ def _enable(self, name: str, **options) -> None: self._global_settings[key] = options.pop(key) self._options = options - def enable(self, name: str | None = None, **options) -> PluginEnabler: + def enable( + self, name: str | None = None, **options: Any + ) -> PluginEnabler[PluginT, R]: """ Enable a plugin by name. diff --git a/altair/utils/theme.py b/altair/utils/theme.py deleted file mode 100644 index bbb7247bc..000000000 --- a/altair/utils/theme.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Utilities for registering and working with themes.""" - -from __future__ import annotations - -import sys -from typing import TYPE_CHECKING - -from altair.utils.plugin_registry import Plugin, PluginRegistry -from altair.vegalite.v5.schema._config import ThemeConfig - -if sys.version_info >= (3, 11): - from typing import LiteralString -else: - from typing_extensions import LiteralString - -if TYPE_CHECKING: - from altair.utils.plugin_registry import PluginEnabler - from altair.vegalite.v5.theme import AltairThemes, VegaThemes - -ThemeType = Plugin[ThemeConfig] - - -# HACK: See for `LiteralString` requirement in `name` -# https://github.com/vega/altair/pull/3526#discussion_r1743350127 -class ThemeRegistry(PluginRegistry[ThemeType, ThemeConfig]): - def enable( - self, name: LiteralString | AltairThemes | VegaThemes | None = None, **options - ) -> PluginEnabler: - """ - Enable a theme by name. - - This can be either called directly, or used as a context manager. - - Parameters - ---------- - name : string (optional) - The name of the theme to enable. If not specified, then use the - current active name. - **options : - Any additional parameters will be passed to the theme as keyword - arguments - - Returns - ------- - PluginEnabler: - An object that allows enable() to be used as a context manager - - Notes - ----- - Default `vega` themes can be previewed at https://vega.github.io/vega-themes/ - """ - return super().enable(name, **options) diff --git a/altair/vegalite/v5/__init__.py b/altair/vegalite/v5/__init__.py index a18be6e11..d8d794b2a 100644 --- a/altair/vegalite/v5/__init__.py +++ b/altair/vegalite/v5/__init__.py @@ -1,9 +1,9 @@ -# ruff: noqa: F401, F403 +# ruff: noqa: F401, F403, F405 from altair.expr.core import datum - -from .api import * -from .compiler import vegalite_compilers -from .data import ( +from altair.vegalite.v5 import api, compiler, schema +from altair.vegalite.v5.api import * +from altair.vegalite.v5.compiler import vegalite_compilers +from altair.vegalite.v5.data import ( MaxRowsError, data_transformers, default_data_transformer, @@ -13,12 +13,640 @@ to_json, to_values, ) -from .display import ( +from altair.vegalite.v5.display import ( VEGA_VERSION, VEGAEMBED_VERSION, VEGALITE_VERSION, VegaLite, renderers, ) -from .schema import * -from .theme import register_theme, themes +from altair.vegalite.v5.schema import * + +# The content of __all__ is automatically written by +# tools/update_init_file.py. Do not modify directly. + +__all__ = [ + "SCHEMA_URL", + "SCHEMA_VERSION", + "TOPLEVEL_ONLY_KEYS", + "URI", + "VEGAEMBED_VERSION", + "VEGALITE_VERSION", + "VEGA_VERSION", + "X2", + "Y2", + "Aggregate", + "AggregateOp", + "AggregateTransform", + "AggregatedFieldDef", + "Align", + "AllSortString", + "Angle", + "AngleDatum", + "AngleValue", + "AnyMark", + "AnyMarkConfig", + "AreaConfig", + "ArgmaxDef", + "ArgminDef", + "AutoSizeParams", + "AutosizeType", + "Axis", + "AxisConfig", + "AxisOrient", + "AxisResolveMap", + "BBox", + "BarConfig", + "BaseTitleNoValueRefs", + "Baseline", + "Bin", + "BinExtent", + "BinParams", + "BinTransform", + "BindCheckbox", + "BindDirect", + "BindInput", + "BindRadioSelect", + "BindRange", + "Binding", + "BinnedTimeUnit", + "Blend", + "BoxPlot", + "BoxPlotConfig", + "BoxPlotDef", + "BrushConfig", + "CalculateTransform", + "Categorical", + "ChainedWhen", + "Chart", + "ChartDataType", + "Color", + "ColorDatum", + "ColorDef", + "ColorName", + "ColorScheme", + "ColorValue", + "Column", + "CompositeMark", + "CompositeMarkDef", + "CompositionConfig", + "ConcatChart", + "ConcatSpecGenericSpec", + "ConditionalAxisColor", + "ConditionalAxisLabelAlign", + "ConditionalAxisLabelBaseline", + "ConditionalAxisLabelFontStyle", + "ConditionalAxisLabelFontWeight", + "ConditionalAxisNumber", + "ConditionalAxisNumberArray", + "ConditionalAxisPropertyAlignnull", + "ConditionalAxisPropertyColornull", + "ConditionalAxisPropertyFontStylenull", + "ConditionalAxisPropertyFontWeightnull", + "ConditionalAxisPropertyTextBaselinenull", + "ConditionalAxisPropertynumberArraynull", + "ConditionalAxisPropertynumbernull", + "ConditionalAxisPropertystringnull", + "ConditionalAxisString", + "ConditionalMarkPropFieldOrDatumDef", + "ConditionalMarkPropFieldOrDatumDefTypeForShape", + "ConditionalParameterMarkPropFieldOrDatumDef", + "ConditionalParameterMarkPropFieldOrDatumDefTypeForShape", + "ConditionalParameterStringFieldDef", + "ConditionalParameterValueDefGradientstringnullExprRef", + "ConditionalParameterValueDefTextExprRef", + "ConditionalParameterValueDefnumber", + "ConditionalParameterValueDefnumberArrayExprRef", + "ConditionalParameterValueDefnumberExprRef", + "ConditionalParameterValueDefstringExprRef", + "ConditionalParameterValueDefstringnullExprRef", + "ConditionalPredicateMarkPropFieldOrDatumDef", + "ConditionalPredicateMarkPropFieldOrDatumDefTypeForShape", + "ConditionalPredicateStringFieldDef", + "ConditionalPredicateValueDefAlignnullExprRef", + "ConditionalPredicateValueDefColornullExprRef", + "ConditionalPredicateValueDefFontStylenullExprRef", + "ConditionalPredicateValueDefFontWeightnullExprRef", + "ConditionalPredicateValueDefGradientstringnullExprRef", + "ConditionalPredicateValueDefTextBaselinenullExprRef", + "ConditionalPredicateValueDefTextExprRef", + "ConditionalPredicateValueDefnumber", + "ConditionalPredicateValueDefnumberArrayExprRef", + "ConditionalPredicateValueDefnumberArraynullExprRef", + "ConditionalPredicateValueDefnumberExprRef", + "ConditionalPredicateValueDefnumbernullExprRef", + "ConditionalPredicateValueDefstringExprRef", + "ConditionalPredicateValueDefstringnullExprRef", + "ConditionalStringFieldDef", + "ConditionalValueDefGradientstringnullExprRef", + "ConditionalValueDefTextExprRef", + "ConditionalValueDefnumber", + "ConditionalValueDefnumberArrayExprRef", + "ConditionalValueDefnumberExprRef", + "ConditionalValueDefstringExprRef", + "ConditionalValueDefstringnullExprRef", + "Config", + "CsvDataFormat", + "Cursor", + "Cyclical", + "Data", + "DataFormat", + "DataSource", + "DataType", + "Datasets", + "DateTime", + "DatumChannelMixin", + "DatumDef", + "Day", + "DensityTransform", + "DerivedStream", + "Description", + "DescriptionValue", + "Detail", + "DictInlineDataset", + "DictSelectionInit", + "DictSelectionInitInterval", + "Diverging", + "DomainUnionWith", + "DsvDataFormat", + "Element", + "Encoding", + "EncodingSortField", + "ErrorBand", + "ErrorBandConfig", + "ErrorBandDef", + "ErrorBar", + "ErrorBarConfig", + "ErrorBarDef", + "ErrorBarExtent", + "EventStream", + "EventType", + "Expr", + "ExprRef", + "ExtentTransform", + "Facet", + "FacetChart", + "FacetEncodingFieldDef", + "FacetFieldDef", + "FacetMapping", + "FacetSpec", + "FacetedEncoding", + "FacetedUnitSpec", + "Feature", + "FeatureCollection", + "FeatureGeometryGeoJsonProperties", + "Field", + "FieldChannelMixin", + "FieldDefWithoutScale", + "FieldEqualPredicate", + "FieldGTEPredicate", + "FieldGTPredicate", + "FieldLTEPredicate", + "FieldLTPredicate", + "FieldName", + "FieldOneOfPredicate", + "FieldOrDatumDefWithConditionDatumDefGradientstringnull", + "FieldOrDatumDefWithConditionDatumDefnumber", + "FieldOrDatumDefWithConditionDatumDefnumberArray", + "FieldOrDatumDefWithConditionDatumDefstringnull", + "FieldOrDatumDefWithConditionMarkPropFieldDefGradientstringnull", + "FieldOrDatumDefWithConditionMarkPropFieldDefTypeForShapestringnull", + "FieldOrDatumDefWithConditionMarkPropFieldDefnumber", + "FieldOrDatumDefWithConditionMarkPropFieldDefnumberArray", + "FieldOrDatumDefWithConditionStringDatumDefText", + "FieldOrDatumDefWithConditionStringFieldDefText", + "FieldOrDatumDefWithConditionStringFieldDefstring", + "FieldRange", + "FieldRangePredicate", + "FieldValidPredicate", + "Fill", + "FillDatum", + "FillOpacity", + "FillOpacityDatum", + "FillOpacityValue", + "FillValue", + "FilterTransform", + "Fit", + "FlattenTransform", + "FoldTransform", + "FontStyle", + "FontWeight", + "FormatConfig", + "Generator", + "GenericUnitSpecEncodingAnyMark", + "GeoJsonFeature", + "GeoJsonFeatureCollection", + "GeoJsonProperties", + "Geometry", + "GeometryCollection", + "Gradient", + "GradientStop", + "GraticuleGenerator", + "GraticuleParams", + "HConcatChart", + "HConcatSpecGenericSpec", + "Header", + "HeaderConfig", + "HexColor", + "Href", + "HrefValue", + "Impute", + "ImputeMethod", + "ImputeParams", + "ImputeSequence", + "ImputeTransform", + "InlineData", + "InlineDataset", + "Interpolate", + "IntervalSelectionConfig", + "IntervalSelectionConfigWithoutType", + "JoinAggregateFieldDef", + "JoinAggregateTransform", + "JsonDataFormat", + "Key", + "LabelOverlap", + "LatLongDef", + "LatLongFieldDef", + "Latitude", + "Latitude2", + "Latitude2Datum", + "Latitude2Value", + "LatitudeDatum", + "LayerChart", + "LayerRepeatMapping", + "LayerRepeatSpec", + "LayerSpec", + "LayoutAlign", + "Legend", + "LegendBinding", + "LegendConfig", + "LegendOrient", + "LegendResolveMap", + "LegendStreamBinding", + "LineConfig", + "LineString", + "LinearGradient", + "LocalMultiTimeUnit", + "LocalSingleTimeUnit", + "Locale", + "LoessTransform", + "LogicalAndPredicate", + "LogicalNotPredicate", + "LogicalOrPredicate", + "Longitude", + "Longitude2", + "Longitude2Datum", + "Longitude2Value", + "LongitudeDatum", + "LookupData", + "LookupSelection", + "LookupTransform", + "Mark", + "MarkConfig", + "MarkDef", + "MarkInvalidDataMode", + "MarkPropDefGradientstringnull", + "MarkPropDefnumber", + "MarkPropDefnumberArray", + "MarkPropDefstringnullTypeForShape", + "MarkType", + "MaxRowsError", + "MergedStream", + "Month", + "MultiLineString", + "MultiPoint", + "MultiPolygon", + "MultiTimeUnit", + "NamedData", + "NonArgAggregateOp", + "NonLayerRepeatSpec", + "NonNormalizedSpec", + "NumberLocale", + "NumericArrayMarkPropDef", + "NumericMarkPropDef", + "OffsetDef", + "Opacity", + "OpacityDatum", + "OpacityValue", + "Order", + "OrderFieldDef", + "OrderOnlyDef", + "OrderValue", + "OrderValueDef", + "Orient", + "Orientation", + "OverlayMarkDef", + "Padding", + "Parameter", + "ParameterExpression", + "ParameterExtent", + "ParameterName", + "ParameterPredicate", + "Parse", + "ParseValue", + "PivotTransform", + "Point", + "PointSelectionConfig", + "PointSelectionConfigWithoutType", + "PolarDef", + "Polygon", + "Position", + "Position2Def", + "PositionDatumDef", + "PositionDatumDefBase", + "PositionDef", + "PositionFieldDef", + "PositionFieldDefBase", + "PositionValueDef", + "Predicate", + "PredicateComposition", + "PrimitiveValue", + "Projection", + "ProjectionConfig", + "ProjectionType", + "QuantileTransform", + "RadialGradient", + "Radius", + "Radius2", + "Radius2Datum", + "Radius2Value", + "RadiusDatum", + "RadiusValue", + "RangeConfig", + "RangeEnum", + "RangeRaw", + "RangeRawArray", + "RangeScheme", + "RectConfig", + "RegressionTransform", + "RelativeBandSize", + "RepeatChart", + "RepeatMapping", + "RepeatRef", + "RepeatSpec", + "Resolve", + "ResolveMode", + "Root", + "Row", + "RowColLayoutAlign", + "RowColboolean", + "RowColnumber", + "RowColumnEncodingFieldDef", + "SampleTransform", + "Scale", + "ScaleBinParams", + "ScaleBins", + "ScaleConfig", + "ScaleDatumDef", + "ScaleFieldDef", + "ScaleInterpolateEnum", + "ScaleInterpolateParams", + "ScaleInvalidDataConfig", + "ScaleInvalidDataShowAsValueangle", + "ScaleInvalidDataShowAsValuecolor", + "ScaleInvalidDataShowAsValuefill", + "ScaleInvalidDataShowAsValuefillOpacity", + "ScaleInvalidDataShowAsValueopacity", + "ScaleInvalidDataShowAsValueradius", + "ScaleInvalidDataShowAsValueshape", + "ScaleInvalidDataShowAsValuesize", + "ScaleInvalidDataShowAsValuestroke", + "ScaleInvalidDataShowAsValuestrokeDash", + "ScaleInvalidDataShowAsValuestrokeOpacity", + "ScaleInvalidDataShowAsValuestrokeWidth", + "ScaleInvalidDataShowAsValuetheta", + "ScaleInvalidDataShowAsValuex", + "ScaleInvalidDataShowAsValuexOffset", + "ScaleInvalidDataShowAsValuey", + "ScaleInvalidDataShowAsValueyOffset", + "ScaleInvalidDataShowAsangle", + "ScaleInvalidDataShowAscolor", + "ScaleInvalidDataShowAsfill", + "ScaleInvalidDataShowAsfillOpacity", + "ScaleInvalidDataShowAsopacity", + "ScaleInvalidDataShowAsradius", + "ScaleInvalidDataShowAsshape", + "ScaleInvalidDataShowAssize", + "ScaleInvalidDataShowAsstroke", + "ScaleInvalidDataShowAsstrokeDash", + "ScaleInvalidDataShowAsstrokeOpacity", + "ScaleInvalidDataShowAsstrokeWidth", + "ScaleInvalidDataShowAstheta", + "ScaleInvalidDataShowAsx", + "ScaleInvalidDataShowAsxOffset", + "ScaleInvalidDataShowAsy", + "ScaleInvalidDataShowAsyOffset", + "ScaleResolveMap", + "ScaleType", + "SchemaBase", + "SchemeParams", + "SecondaryFieldDef", + "SelectionConfig", + "SelectionExpression", + "SelectionInit", + "SelectionInitInterval", + "SelectionInitIntervalMapping", + "SelectionInitMapping", + "SelectionParameter", + "SelectionPredicateComposition", + "SelectionResolution", + "SelectionType", + "SequenceGenerator", + "SequenceParams", + "SequentialMultiHue", + "SequentialSingleHue", + "Shape", + "ShapeDatum", + "ShapeDef", + "ShapeValue", + "SharedEncoding", + "SingleDefUnitChannel", + "SingleTimeUnit", + "Size", + "SizeDatum", + "SizeValue", + "Sort", + "SortArray", + "SortByChannel", + "SortByChannelDesc", + "SortByEncoding", + "SortField", + "SortOrder", + "Spec", + "SphereGenerator", + "StackOffset", + "StackTransform", + "StandardType", + "Step", + "StepFor", + "Stream", + "StringFieldDef", + "StringFieldDefWithCondition", + "StringValueDefWithCondition", + "Stroke", + "StrokeCap", + "StrokeDash", + "StrokeDashDatum", + "StrokeDashValue", + "StrokeDatum", + "StrokeJoin", + "StrokeOpacity", + "StrokeOpacityDatum", + "StrokeOpacityValue", + "StrokeValue", + "StrokeWidth", + "StrokeWidthDatum", + "StrokeWidthValue", + "StyleConfigIndex", + "SymbolShape", + "Text", + "TextBaseline", + "TextDatum", + "TextDef", + "TextDirection", + "TextValue", + "Then", + "Theta", + "Theta2", + "Theta2Datum", + "Theta2Value", + "ThetaDatum", + "ThetaValue", + "TickConfig", + "TickCount", + "TimeInterval", + "TimeIntervalStep", + "TimeLocale", + "TimeUnit", + "TimeUnitParams", + "TimeUnitTransform", + "TimeUnitTransformParams", + "Title", + "TitleAnchor", + "TitleConfig", + "TitleFrame", + "TitleOrient", + "TitleParams", + "Tooltip", + "TooltipContent", + "TooltipValue", + "TopLevelConcatSpec", + "TopLevelFacetSpec", + "TopLevelHConcatSpec", + "TopLevelLayerSpec", + "TopLevelMixin", + "TopLevelParameter", + "TopLevelRepeatSpec", + "TopLevelSelectionParameter", + "TopLevelSpec", + "TopLevelUnitSpec", + "TopLevelVConcatSpec", + "TopoDataFormat", + "Transform", + "Type", + "TypeForShape", + "TypedFieldDef", + "UnitSpec", + "UnitSpecWithFrame", + "Url", + "UrlData", + "UrlValue", + "UtcMultiTimeUnit", + "UtcSingleTimeUnit", + "VConcatChart", + "VConcatSpecGenericSpec", + "ValueChannelMixin", + "ValueDefWithConditionMarkPropFieldOrDatumDefGradientstringnull", + "ValueDefWithConditionMarkPropFieldOrDatumDefTypeForShapestringnull", + "ValueDefWithConditionMarkPropFieldOrDatumDefnumber", + "ValueDefWithConditionMarkPropFieldOrDatumDefnumberArray", + "ValueDefWithConditionMarkPropFieldOrDatumDefstringnull", + "ValueDefWithConditionStringFieldDefText", + "ValueDefnumber", + "ValueDefnumberwidthheightExprRef", + "VariableParameter", + "Vector2DateTime", + "Vector2Vector2number", + "Vector2boolean", + "Vector2number", + "Vector2string", + "Vector3number", + "Vector7string", + "Vector10string", + "Vector12string", + "VegaLite", + "VegaLiteSchema", + "ViewBackground", + "ViewConfig", + "When", + "WindowEventType", + "WindowFieldDef", + "WindowOnlyOp", + "WindowTransform", + "X", + "X2Datum", + "X2Value", + "XDatum", + "XError", + "XError2", + "XError2Value", + "XErrorValue", + "XOffset", + "XOffsetDatum", + "XOffsetValue", + "XValue", + "Y", + "Y2Datum", + "Y2Value", + "YDatum", + "YError", + "YError2", + "YError2Value", + "YErrorValue", + "YOffset", + "YOffsetDatum", + "YOffsetValue", + "YValue", + "api", + "binding", + "binding_checkbox", + "binding_radio", + "binding_range", + "binding_select", + "channels", + "check_fields_and_encodings", + "compiler", + "concat", + "condition", + "core", + "data_transformers", + "datum", + "default_data_transformer", + "graticule", + "hconcat", + "layer", + "limit_rows", + "load_schema", + "mixins", + "param", + "renderers", + "repeat", + "sample", + "schema", + "selection", + "selection_interval", + "selection_multi", + "selection_point", + "selection_single", + "sequence", + "sphere", + "to_csv", + "to_json", + "to_values", + "topo_feature", + "value", + "vconcat", + "vegalite_compilers", + "when", + "with_property_setters", +] diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index 14d47064c..c99e556be 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -16,7 +16,7 @@ import jsonschema import narwhals.stable.v1 as nw -from altair import utils +from altair import theme, utils from altair.expr import core as _expr_core from altair.utils import Optional, SchemaBase, Undefined from altair.utils._vegafusion_data import ( @@ -32,7 +32,6 @@ from .display import VEGA_VERSION, VEGAEMBED_VERSION, VEGALITE_VERSION, renderers from .schema import SCHEMA_URL, channels, core, mixins from .schema._typing import Map, PrimitiveValue_T, SingleDefUnitChannel_T, Temporal -from .theme import themes if sys.version_info >= (3, 14): from typing import TypedDict @@ -2006,9 +2005,8 @@ def to_dict( # noqa: C901 if "$schema" not in vegalite_spec: vegalite_spec["$schema"] = SCHEMA_URL - # apply theme from theme registry - if theme := themes.get(): - vegalite_spec = utils.update_nested(theme(), vegalite_spec, copy=True) + if func := theme.get(): + vegalite_spec = utils.update_nested(func(), vegalite_spec, copy=True) else: msg = ( f"Expected a theme to be set but got {None!r}.\n" diff --git a/altair/vegalite/v5/schema/__init__.py b/altair/vegalite/v5/schema/__init__.py index 1f099eaca..3be8148db 100644 --- a/altair/vegalite/v5/schema/__init__.py +++ b/altair/vegalite/v5/schema/__init__.py @@ -1,8 +1,571 @@ -# ruff: noqa +# ruff: noqa: F403, F405 +# The contents of this file are automatically written by +# tools/generate_schema_wrapper.py. Do not modify directly. -from .core import * -from .channels import * +from altair.vegalite.v5.schema import channels, core +from altair.vegalite.v5.schema.channels import * +from altair.vegalite.v5.schema.core import * SCHEMA_VERSION = "v5.20.1" SCHEMA_URL = "https://vega.github.io/schema/vega-lite/v5.20.1.json" + +__all__ = [ + "SCHEMA_URL", + "SCHEMA_VERSION", + "URI", + "X2", + "Y2", + "Aggregate", + "AggregateOp", + "AggregateTransform", + "AggregatedFieldDef", + "Align", + "AllSortString", + "Angle", + "AngleDatum", + "AngleValue", + "AnyMark", + "AnyMarkConfig", + "AreaConfig", + "ArgmaxDef", + "ArgminDef", + "AutoSizeParams", + "AutosizeType", + "Axis", + "AxisConfig", + "AxisOrient", + "AxisResolveMap", + "BBox", + "BarConfig", + "BaseTitleNoValueRefs", + "Baseline", + "BinExtent", + "BinParams", + "BinTransform", + "BindCheckbox", + "BindDirect", + "BindInput", + "BindRadioSelect", + "BindRange", + "Binding", + "BinnedTimeUnit", + "Blend", + "BoxPlot", + "BoxPlotConfig", + "BoxPlotDef", + "BrushConfig", + "CalculateTransform", + "Categorical", + "Color", + "ColorDatum", + "ColorDef", + "ColorName", + "ColorScheme", + "ColorValue", + "Column", + "CompositeMark", + "CompositeMarkDef", + "CompositionConfig", + "ConcatSpecGenericSpec", + "ConditionalAxisColor", + "ConditionalAxisLabelAlign", + "ConditionalAxisLabelBaseline", + "ConditionalAxisLabelFontStyle", + "ConditionalAxisLabelFontWeight", + "ConditionalAxisNumber", + "ConditionalAxisNumberArray", + "ConditionalAxisPropertyAlignnull", + "ConditionalAxisPropertyColornull", + "ConditionalAxisPropertyFontStylenull", + "ConditionalAxisPropertyFontWeightnull", + "ConditionalAxisPropertyTextBaselinenull", + "ConditionalAxisPropertynumberArraynull", + "ConditionalAxisPropertynumbernull", + "ConditionalAxisPropertystringnull", + "ConditionalAxisString", + "ConditionalMarkPropFieldOrDatumDef", + "ConditionalMarkPropFieldOrDatumDefTypeForShape", + "ConditionalParameterMarkPropFieldOrDatumDef", + "ConditionalParameterMarkPropFieldOrDatumDefTypeForShape", + "ConditionalParameterStringFieldDef", + "ConditionalParameterValueDefGradientstringnullExprRef", + "ConditionalParameterValueDefTextExprRef", + "ConditionalParameterValueDefnumber", + "ConditionalParameterValueDefnumberArrayExprRef", + "ConditionalParameterValueDefnumberExprRef", + "ConditionalParameterValueDefstringExprRef", + "ConditionalParameterValueDefstringnullExprRef", + "ConditionalPredicateMarkPropFieldOrDatumDef", + "ConditionalPredicateMarkPropFieldOrDatumDefTypeForShape", + "ConditionalPredicateStringFieldDef", + "ConditionalPredicateValueDefAlignnullExprRef", + "ConditionalPredicateValueDefColornullExprRef", + "ConditionalPredicateValueDefFontStylenullExprRef", + "ConditionalPredicateValueDefFontWeightnullExprRef", + "ConditionalPredicateValueDefGradientstringnullExprRef", + "ConditionalPredicateValueDefTextBaselinenullExprRef", + "ConditionalPredicateValueDefTextExprRef", + "ConditionalPredicateValueDefnumber", + "ConditionalPredicateValueDefnumberArrayExprRef", + "ConditionalPredicateValueDefnumberArraynullExprRef", + "ConditionalPredicateValueDefnumberExprRef", + "ConditionalPredicateValueDefnumbernullExprRef", + "ConditionalPredicateValueDefstringExprRef", + "ConditionalPredicateValueDefstringnullExprRef", + "ConditionalStringFieldDef", + "ConditionalValueDefGradientstringnullExprRef", + "ConditionalValueDefTextExprRef", + "ConditionalValueDefnumber", + "ConditionalValueDefnumberArrayExprRef", + "ConditionalValueDefnumberExprRef", + "ConditionalValueDefstringExprRef", + "ConditionalValueDefstringnullExprRef", + "Config", + "CsvDataFormat", + "Cursor", + "Cyclical", + "Data", + "DataFormat", + "DataSource", + "Datasets", + "DateTime", + "DatumChannelMixin", + "DatumDef", + "Day", + "DensityTransform", + "DerivedStream", + "Description", + "DescriptionValue", + "Detail", + "DictInlineDataset", + "DictSelectionInit", + "DictSelectionInitInterval", + "Diverging", + "DomainUnionWith", + "DsvDataFormat", + "Element", + "Encoding", + "EncodingSortField", + "ErrorBand", + "ErrorBandConfig", + "ErrorBandDef", + "ErrorBar", + "ErrorBarConfig", + "ErrorBarDef", + "ErrorBarExtent", + "EventStream", + "EventType", + "Expr", + "ExprRef", + "ExtentTransform", + "Facet", + "FacetEncodingFieldDef", + "FacetFieldDef", + "FacetSpec", + "FacetedEncoding", + "FacetedUnitSpec", + "Feature", + "FeatureCollection", + "FeatureGeometryGeoJsonProperties", + "Field", + "FieldChannelMixin", + "FieldDefWithoutScale", + "FieldEqualPredicate", + "FieldGTEPredicate", + "FieldGTPredicate", + "FieldLTEPredicate", + "FieldLTPredicate", + "FieldName", + "FieldOneOfPredicate", + "FieldOrDatumDefWithConditionDatumDefGradientstringnull", + "FieldOrDatumDefWithConditionDatumDefnumber", + "FieldOrDatumDefWithConditionDatumDefnumberArray", + "FieldOrDatumDefWithConditionDatumDefstringnull", + "FieldOrDatumDefWithConditionMarkPropFieldDefGradientstringnull", + "FieldOrDatumDefWithConditionMarkPropFieldDefTypeForShapestringnull", + "FieldOrDatumDefWithConditionMarkPropFieldDefnumber", + "FieldOrDatumDefWithConditionMarkPropFieldDefnumberArray", + "FieldOrDatumDefWithConditionStringDatumDefText", + "FieldOrDatumDefWithConditionStringFieldDefText", + "FieldOrDatumDefWithConditionStringFieldDefstring", + "FieldRange", + "FieldRangePredicate", + "FieldValidPredicate", + "Fill", + "FillDatum", + "FillOpacity", + "FillOpacityDatum", + "FillOpacityValue", + "FillValue", + "FilterTransform", + "Fit", + "FlattenTransform", + "FoldTransform", + "FontStyle", + "FontWeight", + "FormatConfig", + "Generator", + "GenericUnitSpecEncodingAnyMark", + "GeoJsonFeature", + "GeoJsonFeatureCollection", + "GeoJsonProperties", + "Geometry", + "GeometryCollection", + "Gradient", + "GradientStop", + "GraticuleGenerator", + "GraticuleParams", + "HConcatSpecGenericSpec", + "Header", + "HeaderConfig", + "HexColor", + "Href", + "HrefValue", + "ImputeMethod", + "ImputeParams", + "ImputeSequence", + "ImputeTransform", + "InlineData", + "InlineDataset", + "Interpolate", + "IntervalSelectionConfig", + "IntervalSelectionConfigWithoutType", + "JoinAggregateFieldDef", + "JoinAggregateTransform", + "JsonDataFormat", + "Key", + "LabelOverlap", + "LatLongDef", + "LatLongFieldDef", + "Latitude", + "Latitude2", + "Latitude2Datum", + "Latitude2Value", + "LatitudeDatum", + "LayerRepeatMapping", + "LayerRepeatSpec", + "LayerSpec", + "LayoutAlign", + "Legend", + "LegendBinding", + "LegendConfig", + "LegendOrient", + "LegendResolveMap", + "LegendStreamBinding", + "LineConfig", + "LineString", + "LinearGradient", + "LocalMultiTimeUnit", + "LocalSingleTimeUnit", + "Locale", + "LoessTransform", + "LogicalAndPredicate", + "LogicalNotPredicate", + "LogicalOrPredicate", + "Longitude", + "Longitude2", + "Longitude2Datum", + "Longitude2Value", + "LongitudeDatum", + "LookupSelection", + "LookupTransform", + "Mark", + "MarkConfig", + "MarkDef", + "MarkInvalidDataMode", + "MarkPropDefGradientstringnull", + "MarkPropDefnumber", + "MarkPropDefnumberArray", + "MarkPropDefstringnullTypeForShape", + "MarkType", + "MergedStream", + "Month", + "MultiLineString", + "MultiPoint", + "MultiPolygon", + "MultiTimeUnit", + "NamedData", + "NonArgAggregateOp", + "NonLayerRepeatSpec", + "NonNormalizedSpec", + "NumberLocale", + "NumericArrayMarkPropDef", + "NumericMarkPropDef", + "OffsetDef", + "Opacity", + "OpacityDatum", + "OpacityValue", + "Order", + "OrderFieldDef", + "OrderOnlyDef", + "OrderValue", + "OrderValueDef", + "Orient", + "Orientation", + "OverlayMarkDef", + "Padding", + "ParameterExtent", + "ParameterName", + "ParameterPredicate", + "Parse", + "ParseValue", + "PivotTransform", + "Point", + "PointSelectionConfig", + "PointSelectionConfigWithoutType", + "PolarDef", + "Polygon", + "Position", + "Position2Def", + "PositionDatumDef", + "PositionDatumDefBase", + "PositionDef", + "PositionFieldDef", + "PositionFieldDefBase", + "PositionValueDef", + "Predicate", + "PredicateComposition", + "PrimitiveValue", + "Projection", + "ProjectionConfig", + "ProjectionType", + "QuantileTransform", + "RadialGradient", + "Radius", + "Radius2", + "Radius2Datum", + "Radius2Value", + "RadiusDatum", + "RadiusValue", + "RangeConfig", + "RangeEnum", + "RangeRaw", + "RangeRawArray", + "RangeScheme", + "RectConfig", + "RegressionTransform", + "RelativeBandSize", + "RepeatMapping", + "RepeatRef", + "RepeatSpec", + "Resolve", + "ResolveMode", + "Root", + "Row", + "RowColLayoutAlign", + "RowColboolean", + "RowColnumber", + "RowColumnEncodingFieldDef", + "SampleTransform", + "Scale", + "ScaleBinParams", + "ScaleBins", + "ScaleConfig", + "ScaleDatumDef", + "ScaleFieldDef", + "ScaleInterpolateEnum", + "ScaleInterpolateParams", + "ScaleInvalidDataConfig", + "ScaleInvalidDataShowAsValueangle", + "ScaleInvalidDataShowAsValuecolor", + "ScaleInvalidDataShowAsValuefill", + "ScaleInvalidDataShowAsValuefillOpacity", + "ScaleInvalidDataShowAsValueopacity", + "ScaleInvalidDataShowAsValueradius", + "ScaleInvalidDataShowAsValueshape", + "ScaleInvalidDataShowAsValuesize", + "ScaleInvalidDataShowAsValuestroke", + "ScaleInvalidDataShowAsValuestrokeDash", + "ScaleInvalidDataShowAsValuestrokeOpacity", + "ScaleInvalidDataShowAsValuestrokeWidth", + "ScaleInvalidDataShowAsValuetheta", + "ScaleInvalidDataShowAsValuex", + "ScaleInvalidDataShowAsValuexOffset", + "ScaleInvalidDataShowAsValuey", + "ScaleInvalidDataShowAsValueyOffset", + "ScaleInvalidDataShowAsangle", + "ScaleInvalidDataShowAscolor", + "ScaleInvalidDataShowAsfill", + "ScaleInvalidDataShowAsfillOpacity", + "ScaleInvalidDataShowAsopacity", + "ScaleInvalidDataShowAsradius", + "ScaleInvalidDataShowAsshape", + "ScaleInvalidDataShowAssize", + "ScaleInvalidDataShowAsstroke", + "ScaleInvalidDataShowAsstrokeDash", + "ScaleInvalidDataShowAsstrokeOpacity", + "ScaleInvalidDataShowAsstrokeWidth", + "ScaleInvalidDataShowAstheta", + "ScaleInvalidDataShowAsx", + "ScaleInvalidDataShowAsxOffset", + "ScaleInvalidDataShowAsy", + "ScaleInvalidDataShowAsyOffset", + "ScaleResolveMap", + "ScaleType", + "SchemaBase", + "SchemeParams", + "SecondaryFieldDef", + "SelectionConfig", + "SelectionInit", + "SelectionInitInterval", + "SelectionInitIntervalMapping", + "SelectionInitMapping", + "SelectionParameter", + "SelectionResolution", + "SelectionType", + "SequenceGenerator", + "SequenceParams", + "SequentialMultiHue", + "SequentialSingleHue", + "Shape", + "ShapeDatum", + "ShapeDef", + "ShapeValue", + "SharedEncoding", + "SingleDefUnitChannel", + "SingleTimeUnit", + "Size", + "SizeDatum", + "SizeValue", + "Sort", + "SortArray", + "SortByChannel", + "SortByChannelDesc", + "SortByEncoding", + "SortField", + "SortOrder", + "Spec", + "SphereGenerator", + "StackOffset", + "StackTransform", + "StandardType", + "Step", + "StepFor", + "Stream", + "StringFieldDef", + "StringFieldDefWithCondition", + "StringValueDefWithCondition", + "Stroke", + "StrokeCap", + "StrokeDash", + "StrokeDashDatum", + "StrokeDashValue", + "StrokeDatum", + "StrokeJoin", + "StrokeOpacity", + "StrokeOpacityDatum", + "StrokeOpacityValue", + "StrokeValue", + "StrokeWidth", + "StrokeWidthDatum", + "StrokeWidthValue", + "StyleConfigIndex", + "SymbolShape", + "Text", + "TextBaseline", + "TextDatum", + "TextDef", + "TextDirection", + "TextValue", + "Theta", + "Theta2", + "Theta2Datum", + "Theta2Value", + "ThetaDatum", + "ThetaValue", + "TickConfig", + "TickCount", + "TimeInterval", + "TimeIntervalStep", + "TimeLocale", + "TimeUnit", + "TimeUnitParams", + "TimeUnitTransform", + "TimeUnitTransformParams", + "TitleAnchor", + "TitleConfig", + "TitleFrame", + "TitleOrient", + "TitleParams", + "Tooltip", + "TooltipContent", + "TooltipValue", + "TopLevelConcatSpec", + "TopLevelFacetSpec", + "TopLevelHConcatSpec", + "TopLevelLayerSpec", + "TopLevelParameter", + "TopLevelRepeatSpec", + "TopLevelSelectionParameter", + "TopLevelSpec", + "TopLevelUnitSpec", + "TopLevelVConcatSpec", + "TopoDataFormat", + "Transform", + "Type", + "TypeForShape", + "TypedFieldDef", + "UnitSpec", + "UnitSpecWithFrame", + "Url", + "UrlData", + "UrlValue", + "UtcMultiTimeUnit", + "UtcSingleTimeUnit", + "VConcatSpecGenericSpec", + "ValueChannelMixin", + "ValueDefWithConditionMarkPropFieldOrDatumDefGradientstringnull", + "ValueDefWithConditionMarkPropFieldOrDatumDefTypeForShapestringnull", + "ValueDefWithConditionMarkPropFieldOrDatumDefnumber", + "ValueDefWithConditionMarkPropFieldOrDatumDefnumberArray", + "ValueDefWithConditionMarkPropFieldOrDatumDefstringnull", + "ValueDefWithConditionStringFieldDefText", + "ValueDefnumber", + "ValueDefnumberwidthheightExprRef", + "VariableParameter", + "Vector2DateTime", + "Vector2Vector2number", + "Vector2boolean", + "Vector2number", + "Vector2string", + "Vector3number", + "Vector7string", + "Vector10string", + "Vector12string", + "VegaLiteSchema", + "ViewBackground", + "ViewConfig", + "WindowEventType", + "WindowFieldDef", + "WindowOnlyOp", + "WindowTransform", + "X", + "X2Datum", + "X2Value", + "XDatum", + "XError", + "XError2", + "XError2Value", + "XErrorValue", + "XOffset", + "XOffsetDatum", + "XOffsetValue", + "XValue", + "Y", + "Y2Datum", + "Y2Value", + "YDatum", + "YError", + "YError2", + "YError2Value", + "YErrorValue", + "YOffset", + "YOffsetDatum", + "YOffsetValue", + "YValue", + "channels", + "core", + "load_schema", + "with_property_setters", +] diff --git a/altair/vegalite/v5/schema/_config.py b/altair/vegalite/v5/schema/_config.py index 96da254c2..b5d4020ff 100644 --- a/altair/vegalite/v5/schema/_config.py +++ b/altair/vegalite/v5/schema/_config.py @@ -95,7 +95,7 @@ class AreaConfigKwds(TypedDict, total=False): """ - :class:`AreaConfig` ``TypedDict`` wrapper. + :class:`altair.AreaConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -569,7 +569,7 @@ class AreaConfigKwds(TypedDict, total=False): class AutoSizeParamsKwds(TypedDict, total=False): """ - :class:`AutoSizeParams` ``TypedDict`` wrapper. + :class:`altair.AutoSizeParams` ``TypedDict`` wrapper. Parameters ---------- @@ -603,7 +603,7 @@ class AutoSizeParamsKwds(TypedDict, total=False): class AxisConfigKwds(TypedDict, total=False): """ - :class:`AxisConfig` ``TypedDict`` wrapper. + :class:`altair.AxisConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -1059,7 +1059,7 @@ class AxisConfigKwds(TypedDict, total=False): class AxisResolveMapKwds(TypedDict, total=False): """ - :class:`AxisResolveMap` ``TypedDict`` wrapper. + :class:`altair.AxisResolveMap` ``TypedDict`` wrapper. Parameters ---------- @@ -1075,7 +1075,7 @@ class AxisResolveMapKwds(TypedDict, total=False): class BarConfigKwds(TypedDict, total=False): """ - :class:`BarConfig` ``TypedDict`` wrapper. + :class:`altair.BarConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -1546,7 +1546,7 @@ class BarConfigKwds(TypedDict, total=False): class BindCheckboxKwds(TypedDict, total=False): """ - :class:`BindCheckbox` ``TypedDict`` wrapper. + :class:`altair.BindCheckbox` ``TypedDict`` wrapper. Parameters ---------- @@ -1572,7 +1572,7 @@ class BindCheckboxKwds(TypedDict, total=False): class BindDirectKwds(TypedDict, total=False): """ - :class:`BindDirect` ``TypedDict`` wrapper. + :class:`altair.BindDirect` ``TypedDict`` wrapper. Parameters ---------- @@ -1598,7 +1598,7 @@ class BindDirectKwds(TypedDict, total=False): class BindInputKwds(TypedDict, total=False): """ - :class:`BindInput` ``TypedDict`` wrapper. + :class:`altair.BindInput` ``TypedDict`` wrapper. Parameters ---------- @@ -1634,7 +1634,7 @@ class BindInputKwds(TypedDict, total=False): class BindRadioSelectKwds(TypedDict, total=False): """ - :class:`BindRadioSelect` ``TypedDict`` wrapper. + :class:`altair.BindRadioSelect` ``TypedDict`` wrapper. Parameters ---------- @@ -1667,7 +1667,7 @@ class BindRadioSelectKwds(TypedDict, total=False): class BindRangeKwds(TypedDict, total=False): """ - :class:`BindRange` ``TypedDict`` wrapper. + :class:`altair.BindRange` ``TypedDict`` wrapper. Parameters ---------- @@ -1705,7 +1705,7 @@ class BindRangeKwds(TypedDict, total=False): class BoxPlotConfigKwds(TypedDict, total=False): """ - :class:`BoxPlotConfig` ``TypedDict`` wrapper. + :class:`altair.BoxPlotConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -1785,7 +1785,7 @@ class BoxPlotConfigKwds(TypedDict, total=False): class BrushConfigKwds(TypedDict, total=False): """ - :class:`BrushConfig` ``TypedDict`` wrapper. + :class:`altair.BrushConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -1827,7 +1827,7 @@ class BrushConfigKwds(TypedDict, total=False): class CompositionConfigKwds(TypedDict, total=False): """ - :class:`CompositionConfig` ``TypedDict`` wrapper. + :class:`altair.CompositionConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -1860,7 +1860,7 @@ class CompositionConfigKwds(TypedDict, total=False): class ConfigKwds(TypedDict, total=False): """ - :class:`Config` ``TypedDict`` wrapper. + :class:`altair.Config` ``TypedDict`` wrapper. Parameters ---------- @@ -2209,7 +2209,7 @@ class ConfigKwds(TypedDict, total=False): class DateTimeKwds(TypedDict, total=False): """ - :class:`DateTime` ``TypedDict`` wrapper. + :class:`altair.DateTime` ``TypedDict`` wrapper. Parameters ---------- @@ -2257,7 +2257,7 @@ class DateTimeKwds(TypedDict, total=False): class DerivedStreamKwds(TypedDict, total=False): """ - :class:`DerivedStream` ``TypedDict`` wrapper. + :class:`altair.DerivedStream` ``TypedDict`` wrapper. Parameters ---------- @@ -2291,7 +2291,7 @@ class DerivedStreamKwds(TypedDict, total=False): class ErrorBandConfigKwds(TypedDict, total=False): """ - :class:`ErrorBandConfig` ``TypedDict`` wrapper. + :class:`altair.ErrorBandConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -2361,7 +2361,7 @@ class ErrorBandConfigKwds(TypedDict, total=False): class ErrorBarConfigKwds(TypedDict, total=False): """ - :class:`ErrorBarConfig` ``TypedDict`` wrapper. + :class:`altair.ErrorBarConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -2411,7 +2411,7 @@ class ErrorBarConfigKwds(TypedDict, total=False): class FeatureGeometryGeoJsonPropertiesKwds(TypedDict, total=False): """ - :class:`FeatureGeometryGeoJsonProperties` ``TypedDict`` wrapper. + :class:`altair.FeatureGeometryGeoJsonProperties` ``TypedDict`` wrapper. Parameters ---------- @@ -2446,7 +2446,7 @@ class FeatureGeometryGeoJsonPropertiesKwds(TypedDict, total=False): class FormatConfigKwds(TypedDict, total=False): """ - :class:`FormatConfig` ``TypedDict`` wrapper. + :class:`altair.FormatConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -2515,7 +2515,7 @@ class FormatConfigKwds(TypedDict, total=False): class GeoJsonFeatureKwds(TypedDict, total=False): """ - :class:`GeoJsonFeature` ``TypedDict`` wrapper. + :class:`altair.GeoJsonFeature` ``TypedDict`` wrapper. Parameters ---------- @@ -2550,7 +2550,7 @@ class GeoJsonFeatureKwds(TypedDict, total=False): class GeoJsonFeatureCollectionKwds(TypedDict, total=False): """ - :class:`GeoJsonFeatureCollection` ``TypedDict`` wrapper. + :class:`altair.GeoJsonFeatureCollection` ``TypedDict`` wrapper. Parameters ---------- @@ -2570,7 +2570,7 @@ class GeoJsonFeatureCollectionKwds(TypedDict, total=False): class GeometryCollectionKwds(TypedDict, total=False): """ - :class:`GeometryCollection` ``TypedDict`` wrapper. + :class:`altair.GeometryCollection` ``TypedDict`` wrapper. Parameters ---------- @@ -2598,7 +2598,7 @@ class GeometryCollectionKwds(TypedDict, total=False): class GradientStopKwds(TypedDict, total=False): """ - :class:`GradientStop` ``TypedDict`` wrapper. + :class:`altair.GradientStop` ``TypedDict`` wrapper. Parameters ---------- @@ -2614,7 +2614,7 @@ class GradientStopKwds(TypedDict, total=False): class HeaderConfigKwds(TypedDict, total=False): """ - :class:`HeaderConfig` ``TypedDict`` wrapper. + :class:`altair.HeaderConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -2789,7 +2789,7 @@ class HeaderConfigKwds(TypedDict, total=False): class IntervalSelectionConfigKwds(TypedDict, total=False): """ - :class:`IntervalSelectionConfig` ``TypedDict`` wrapper. + :class:`altair.IntervalSelectionConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -2899,7 +2899,7 @@ class IntervalSelectionConfigKwds(TypedDict, total=False): class IntervalSelectionConfigWithoutTypeKwds(TypedDict, total=False): """ - :class:`IntervalSelectionConfigWithoutType` ``TypedDict`` wrapper. + :class:`altair.IntervalSelectionConfigWithoutType` ``TypedDict`` wrapper. Parameters ---------- @@ -3001,7 +3001,7 @@ class IntervalSelectionConfigWithoutTypeKwds(TypedDict, total=False): class LegendConfigKwds(TypedDict, total=False): """ - :class:`LegendConfig` ``TypedDict`` wrapper. + :class:`altair.LegendConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -3356,7 +3356,7 @@ class LegendConfigKwds(TypedDict, total=False): class LegendResolveMapKwds(TypedDict, total=False): """ - :class:`LegendResolveMap` ``TypedDict`` wrapper. + :class:`altair.LegendResolveMap` ``TypedDict`` wrapper. Parameters ---------- @@ -3399,7 +3399,7 @@ class LegendResolveMapKwds(TypedDict, total=False): class LegendStreamBindingKwds(TypedDict, total=False): """ - :class:`LegendStreamBinding` ``TypedDict`` wrapper. + :class:`altair.LegendStreamBinding` ``TypedDict`` wrapper. Parameters ---------- @@ -3412,7 +3412,7 @@ class LegendStreamBindingKwds(TypedDict, total=False): class LineConfigKwds(TypedDict, total=False): """ - :class:`LineConfig` ``TypedDict`` wrapper. + :class:`altair.LineConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -3875,7 +3875,7 @@ class LineConfigKwds(TypedDict, total=False): class LineStringKwds(TypedDict, total=False): """ - :class:`LineString` ``TypedDict`` wrapper. + :class:`altair.LineString` ``TypedDict`` wrapper. Parameters ---------- @@ -3895,7 +3895,7 @@ class LineStringKwds(TypedDict, total=False): class LinearGradientKwds(TypedDict, total=False): """ - :class:`LinearGradient` ``TypedDict`` wrapper. + :class:`altair.LinearGradient` ``TypedDict`` wrapper. Parameters ---------- @@ -3934,7 +3934,7 @@ class LinearGradientKwds(TypedDict, total=False): class LocaleKwds(TypedDict, total=False): """ - :class:`Locale` ``TypedDict`` wrapper. + :class:`altair.Locale` ``TypedDict`` wrapper. Parameters ---------- @@ -3950,7 +3950,7 @@ class LocaleKwds(TypedDict, total=False): class MarkConfigKwds(TypedDict, total=False): """ - :class:`MarkConfig` ``TypedDict`` wrapper. + :class:`altair.MarkConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -4398,7 +4398,7 @@ class MarkConfigKwds(TypedDict, total=False): class MergedStreamKwds(TypedDict, total=False): """ - :class:`MergedStream` ``TypedDict`` wrapper. + :class:`altair.MergedStream` ``TypedDict`` wrapper. Parameters ---------- @@ -4432,7 +4432,7 @@ class MergedStreamKwds(TypedDict, total=False): class MultiLineStringKwds(TypedDict, total=False): """ - :class:`MultiLineString` ``TypedDict`` wrapper. + :class:`altair.MultiLineString` ``TypedDict`` wrapper. Parameters ---------- @@ -4452,7 +4452,7 @@ class MultiLineStringKwds(TypedDict, total=False): class MultiPointKwds(TypedDict, total=False): """ - :class:`MultiPoint` ``TypedDict`` wrapper. + :class:`altair.MultiPoint` ``TypedDict`` wrapper. Parameters ---------- @@ -4472,7 +4472,7 @@ class MultiPointKwds(TypedDict, total=False): class MultiPolygonKwds(TypedDict, total=False): """ - :class:`MultiPolygon` ``TypedDict`` wrapper. + :class:`altair.MultiPolygon` ``TypedDict`` wrapper. Parameters ---------- @@ -4492,7 +4492,7 @@ class MultiPolygonKwds(TypedDict, total=False): class NumberLocaleKwds(TypedDict, total=False): """ - :class:`NumberLocale` ``TypedDict`` wrapper. + :class:`altair.NumberLocale` ``TypedDict`` wrapper. Parameters ---------- @@ -4526,7 +4526,7 @@ class NumberLocaleKwds(TypedDict, total=False): class OverlayMarkDefKwds(TypedDict, total=False): """ - :class:`OverlayMarkDef` ``TypedDict`` wrapper. + :class:`altair.OverlayMarkDef` ``TypedDict`` wrapper. Parameters ---------- @@ -5016,7 +5016,7 @@ class OverlayMarkDefKwds(TypedDict, total=False): class PointKwds(TypedDict, total=False): """ - :class:`Point` ``TypedDict`` wrapper. + :class:`altair.Point` ``TypedDict`` wrapper. Parameters ---------- @@ -5040,7 +5040,7 @@ class PointKwds(TypedDict, total=False): class PointSelectionConfigKwds(TypedDict, total=False): """ - :class:`PointSelectionConfig` ``TypedDict`` wrapper. + :class:`altair.PointSelectionConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -5148,7 +5148,7 @@ class PointSelectionConfigKwds(TypedDict, total=False): class PointSelectionConfigWithoutTypeKwds(TypedDict, total=False): """ - :class:`PointSelectionConfigWithoutType` ``TypedDict`` wrapper. + :class:`altair.PointSelectionConfigWithoutType` ``TypedDict`` wrapper. Parameters ---------- @@ -5248,7 +5248,7 @@ class PointSelectionConfigWithoutTypeKwds(TypedDict, total=False): class PolygonKwds(TypedDict, total=False): """ - :class:`Polygon` ``TypedDict`` wrapper. + :class:`altair.Polygon` ``TypedDict`` wrapper. Parameters ---------- @@ -5268,7 +5268,7 @@ class PolygonKwds(TypedDict, total=False): class ProjectionKwds(TypedDict, total=False): """ - :class:`Projection` ``TypedDict`` wrapper. + :class:`altair.Projection` ``TypedDict`` wrapper. Parameters ---------- @@ -5411,7 +5411,7 @@ class ProjectionKwds(TypedDict, total=False): class ProjectionConfigKwds(TypedDict, total=False): """ - :class:`ProjectionConfig` ``TypedDict`` wrapper. + :class:`altair.ProjectionConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -5554,7 +5554,7 @@ class ProjectionConfigKwds(TypedDict, total=False): class RadialGradientKwds(TypedDict, total=False): """ - :class:`RadialGradient` ``TypedDict`` wrapper. + :class:`altair.RadialGradient` ``TypedDict`` wrapper. Parameters ---------- @@ -5609,7 +5609,7 @@ class RadialGradientKwds(TypedDict, total=False): class RangeConfigKwds(TypedDict, total=False): """ - :class:`RangeConfig` ``TypedDict`` wrapper. + :class:`altair.RangeConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -5663,7 +5663,7 @@ class RangeConfigKwds(TypedDict, total=False): class RectConfigKwds(TypedDict, total=False): """ - :class:`RectConfig` ``TypedDict`` wrapper. + :class:`altair.RectConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -6129,7 +6129,7 @@ class RectConfigKwds(TypedDict, total=False): class ResolveKwds(TypedDict, total=False): """ - :class:`Resolve` ``TypedDict`` wrapper. + :class:`altair.Resolve` ``TypedDict`` wrapper. Parameters ---------- @@ -6148,7 +6148,7 @@ class ResolveKwds(TypedDict, total=False): class ScaleConfigKwds(TypedDict, total=False): """ - :class:`ScaleConfig` ``TypedDict`` wrapper. + :class:`altair.ScaleConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -6331,7 +6331,7 @@ class ScaleConfigKwds(TypedDict, total=False): class ScaleInvalidDataConfigKwds(TypedDict, total=False): """ - :class:`ScaleInvalidDataConfig` ``TypedDict`` wrapper. + :class:`altair.ScaleInvalidDataConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -6401,7 +6401,7 @@ class ScaleInvalidDataConfigKwds(TypedDict, total=False): class ScaleResolveMapKwds(TypedDict, total=False): """ - :class:`ScaleResolveMap` ``TypedDict`` wrapper. + :class:`altair.ScaleResolveMap` ``TypedDict`` wrapper. Parameters ---------- @@ -6462,7 +6462,7 @@ class ScaleResolveMapKwds(TypedDict, total=False): class SelectionConfigKwds(TypedDict, total=False): """ - :class:`SelectionConfig` ``TypedDict`` wrapper. + :class:`altair.SelectionConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -6490,7 +6490,7 @@ class SelectionConfigKwds(TypedDict, total=False): class StepKwds(TypedDict, closed=True, total=False): # type: ignore[call-arg] """ - :class:`Step` ``TypedDict`` wrapper. + :class:`altair.Step` ``TypedDict`` wrapper. Parameters ---------- @@ -6515,7 +6515,7 @@ class StepKwds(TypedDict, closed=True, total=False): # type: ignore[call-arg] class StyleConfigIndexKwds(TypedDict, closed=True, total=False): # type: ignore[call-arg] """ - :class:`StyleConfigIndex` ``TypedDict`` wrapper. + :class:`altair.StyleConfigIndex` ``TypedDict`` wrapper. Parameters ---------- @@ -6582,7 +6582,7 @@ class StyleConfigIndexKwds(TypedDict, closed=True, total=False): # type: ignore class TickConfigKwds(TypedDict, total=False): """ - :class:`TickConfig` ``TypedDict`` wrapper. + :class:`altair.TickConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -7041,7 +7041,7 @@ class TickConfigKwds(TypedDict, total=False): class TimeIntervalStepKwds(TypedDict, total=False): """ - :class:`TimeIntervalStep` ``TypedDict`` wrapper. + :class:`altair.TimeIntervalStep` ``TypedDict`` wrapper. Parameters ---------- @@ -7057,7 +7057,7 @@ class TimeIntervalStepKwds(TypedDict, total=False): class TimeLocaleKwds(TypedDict, total=False): """ - :class:`TimeLocale` ``TypedDict`` wrapper. + :class:`altair.TimeLocale` ``TypedDict`` wrapper. Parameters ---------- @@ -7091,7 +7091,7 @@ class TimeLocaleKwds(TypedDict, total=False): class TitleConfigKwds(TypedDict, total=False): """ - :class:`TitleConfig` ``TypedDict`` wrapper. + :class:`altair.TitleConfig` ``TypedDict`` wrapper. Parameters ---------- @@ -7199,7 +7199,7 @@ class TitleConfigKwds(TypedDict, total=False): class TitleParamsKwds(TypedDict, total=False): """ - :class:`TitleParams` ``TypedDict`` wrapper. + :class:`altair.TitleParams` ``TypedDict`` wrapper. Parameters ---------- @@ -7330,7 +7330,7 @@ class TitleParamsKwds(TypedDict, total=False): class TooltipContentKwds(TypedDict, total=False): """ - :class:`TooltipContent` ``TypedDict`` wrapper. + :class:`altair.TooltipContent` ``TypedDict`` wrapper. Parameters ---------- @@ -7343,7 +7343,7 @@ class TooltipContentKwds(TypedDict, total=False): class TopLevelSelectionParameterKwds(TypedDict, total=False): """ - :class:`TopLevelSelectionParameter` ``TypedDict`` wrapper. + :class:`altair.TopLevelSelectionParameter` ``TypedDict`` wrapper. Parameters ---------- @@ -7405,7 +7405,7 @@ class TopLevelSelectionParameterKwds(TypedDict, total=False): class VariableParameterKwds(TypedDict, total=False): """ - :class:`VariableParameter` ``TypedDict`` wrapper. + :class:`altair.VariableParameter` ``TypedDict`` wrapper. Parameters ---------- @@ -7450,7 +7450,7 @@ class VariableParameterKwds(TypedDict, total=False): class ViewBackgroundKwds(TypedDict, total=False): """ - :class:`ViewBackground` ``TypedDict`` wrapper. + :class:`altair.ViewBackground` ``TypedDict`` wrapper. Parameters ---------- @@ -7528,7 +7528,7 @@ class ViewBackgroundKwds(TypedDict, total=False): class ViewConfigKwds(TypedDict, total=False): """ - :class:`ViewConfig` ``TypedDict`` wrapper. + :class:`altair.ViewConfig` ``TypedDict`` wrapper. Parameters ---------- diff --git a/altair/vegalite/v5/theme.py b/altair/vegalite/v5/theme.py index ba5a5ab9a..77f480829 100644 --- a/altair/vegalite/v5/theme.py +++ b/altair/vegalite/v5/theme.py @@ -2,22 +2,16 @@ from __future__ import annotations -import sys -from functools import wraps -from typing import TYPE_CHECKING, Callable, Final, Literal, get_args +from typing import TYPE_CHECKING, Any, Final, Literal, get_args -from altair.utils.theme import ThemeRegistry +from altair.utils.deprecation import deprecated_static_only +from altair.utils.plugin_registry import Plugin, PluginRegistry from altair.vegalite.v5.schema._config import ThemeConfig from altair.vegalite.v5.schema._typing import VegaThemes -if sys.version_info >= (3, 10): - from typing import ParamSpec -else: - from typing_extensions import ParamSpec - - if TYPE_CHECKING: - from altair.utils.plugin_registry import Plugin + import sys + from functools import partial if sys.version_info >= (3, 11): from typing import LiteralString @@ -28,11 +22,64 @@ else: from typing_extensions import TypeAlias -P = ParamSpec("P") + from altair.utils.plugin_registry import PluginEnabler + + AltairThemes: TypeAlias = Literal["default", "opaque"] VEGA_THEMES: list[LiteralString] = list(get_args(VegaThemes)) +# HACK: See for `LiteralString` requirement in `name` +# https://github.com/vega/altair/pull/3526#discussion_r1743350127 +class ThemeRegistry(PluginRegistry[Plugin[ThemeConfig], ThemeConfig]): + def enable( + self, + name: LiteralString | AltairThemes | VegaThemes | None = None, + **options: Any, + ) -> PluginEnabler[Plugin[ThemeConfig], ThemeConfig]: + """ + Enable a theme by name. + + This can be either called directly, or used as a context manager. + + Parameters + ---------- + name : string (optional) + The name of the theme to enable. If not specified, then use the + current active name. + **options : + Any additional parameters will be passed to the theme as keyword + arguments + + Returns + ------- + PluginEnabler: + An object that allows enable() to be used as a context manager + + Notes + ----- + Default `vega` themes can be previewed at https://vega.github.io/vega-themes/ + """ + return super().enable(name, **options) + + def get(self) -> partial[ThemeConfig] | Plugin[ThemeConfig] | None: + """Return the currently active theme.""" + return super().get() + + def names(self) -> list[str]: + """Return the names of the registered and entry points themes.""" + return super().names() + + @deprecated_static_only( + "Deprecated since `altair=5.5.0`. Use @altair.theme.register instead.", + category=None, + ) + def register( + self, name: str, value: Plugin[ThemeConfig] | None + ) -> Plugin[ThemeConfig] | None: + return super().register(name, value) + + class VegaTheme: """Implementation of a builtin vega theme.""" @@ -53,6 +100,8 @@ def __repr__(self) -> str: # themes that will be auto-detected. Explicit registration is also # allowed by the PluginRegistry API. ENTRY_POINT_GROUP: Final = "altair.vegalite.v5.theme" + +# NOTE: `themes` def has an entry point group themes = ThemeRegistry(entry_point_group=ENTRY_POINT_GROUP) themes.register( @@ -74,75 +123,3 @@ def __repr__(self) -> str: themes.register(theme, VegaTheme(theme)) themes.enable("default") - - -# HACK: See for `LiteralString` requirement in `name` -# https://github.com/vega/altair/pull/3526#discussion_r1743350127 -def register_theme( - name: LiteralString, *, enable: bool -) -> Callable[[Plugin[ThemeConfig]], Plugin[ThemeConfig]]: - """ - Decorator for registering a theme function. - - Parameters - ---------- - name - Unique name assigned in ``alt.themes``. - enable - Auto-enable the wrapped theme. - - Examples - -------- - Register and enable a theme:: - - import altair as alt - from altair.typing import ThemeConfig - - - @alt.register_theme("param_font_size", enable=True) - def custom_theme() -> ThemeConfig: - sizes = 12, 14, 16, 18, 20 - return { - "autosize": {"contains": "content", "resize": True}, - "background": "#F3F2F1", - "config": { - "axisX": {"labelFontSize": sizes[1], "titleFontSize": sizes[1]}, - "axisY": {"labelFontSize": sizes[1], "titleFontSize": sizes[1]}, - "font": "'Lato', 'Segoe UI', Tahoma, Verdana, sans-serif", - "headerColumn": {"labelFontSize": sizes[1]}, - "headerFacet": {"labelFontSize": sizes[1]}, - "headerRow": {"labelFontSize": sizes[1]}, - "legend": {"labelFontSize": sizes[0], "titleFontSize": sizes[1]}, - "text": {"fontSize": sizes[0]}, - "title": {"fontSize": sizes[-1]}, - }, - "height": {"step": 28}, - "width": 350, - } - - Until another theme has been enabled, all charts will use defaults set in ``custom_theme``:: - - from vega_datasets import data - - source = data.stocks() - lines = ( - alt.Chart(source, title=alt.Title("Stocks")) - .mark_line() - .encode(x="date:T", y="price:Q", color="symbol:N") - ) - lines.interactive(bind_y=False) - - """ - - def decorate(func: Plugin[ThemeConfig], /) -> Plugin[ThemeConfig]: - themes.register(name, func) - if enable: - themes.enable(name) - - @wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> ThemeConfig: - return func(*args, **kwargs) - - return wrapper - - return decorate diff --git a/doc/user_guide/api.rst b/doc/user_guide/api.rst index 9000bce99..d3c08f547 100644 --- a/doc/user_guide/api.rst +++ b/doc/user_guide/api.rst @@ -9,6 +9,8 @@ Please refer to the `full user guide `_ for further details, as this low-level documentation may not be enough to give full guidelines on their use. +.. _api-toplevel: + Top-Level Objects ----------------- .. currentmodule:: altair @@ -26,6 +28,8 @@ Top-Level Objects TopLevelMixin VConcatChart +.. _api-channels: + Encoding Channels ----------------- .. currentmodule:: altair @@ -134,6 +138,8 @@ Encoding Channels YOffsetValue YValue +.. _api-functions: + API Functions ------------- .. currentmodule:: altair @@ -164,6 +170,96 @@ API Functions vconcat when +.. _api-theme: + +Theme +----- +.. currentmodule:: altair.theme + +.. autosummary:: + :toctree: generated/theme/ + :nosignatures: + + active + enable + get + names + options + register + unregister + ThemeConfig + AreaConfigKwds + AutoSizeParamsKwds + AxisConfigKwds + AxisResolveMapKwds + BarConfigKwds + BindCheckboxKwds + BindDirectKwds + BindInputKwds + BindRadioSelectKwds + BindRangeKwds + BoxPlotConfigKwds + BrushConfigKwds + CompositionConfigKwds + ConfigKwds + DateTimeKwds + DerivedStreamKwds + ErrorBandConfigKwds + ErrorBarConfigKwds + FeatureGeometryGeoJsonPropertiesKwds + FormatConfigKwds + GeoJsonFeatureCollectionKwds + GeoJsonFeatureKwds + GeometryCollectionKwds + GradientStopKwds + HeaderConfigKwds + IntervalSelectionConfigKwds + IntervalSelectionConfigWithoutTypeKwds + LegendConfigKwds + LegendResolveMapKwds + LegendStreamBindingKwds + LineConfigKwds + LineStringKwds + LinearGradientKwds + LocaleKwds + MarkConfigKwds + MergedStreamKwds + MultiLineStringKwds + MultiPointKwds + MultiPolygonKwds + NumberLocaleKwds + OverlayMarkDefKwds + PaddingKwds + PointKwds + PointSelectionConfigKwds + PointSelectionConfigWithoutTypeKwds + PolygonKwds + ProjectionConfigKwds + ProjectionKwds + RadialGradientKwds + RangeConfigKwds + RectConfigKwds + ResolveKwds + RowColKwds + ScaleConfigKwds + ScaleInvalidDataConfigKwds + ScaleResolveMapKwds + SelectionConfigKwds + StepKwds + StyleConfigIndexKwds + TickConfigKwds + TimeIntervalStepKwds + TimeLocaleKwds + TitleConfigKwds + TitleParamsKwds + TooltipContentKwds + TopLevelSelectionParameterKwds + VariableParameterKwds + ViewBackgroundKwds + ViewConfigKwds + +.. _api-core: + Low-Level Schema Wrappers ------------------------- .. currentmodule:: altair @@ -625,6 +721,8 @@ Low-Level Schema Wrappers WindowOnlyOp WindowTransform +.. _api-cls: + API Utility Classes ------------------- .. currentmodule:: altair @@ -638,6 +736,8 @@ API Utility Classes Then ChainedWhen +.. _api-typing: + Typing ------ .. currentmodule:: altair.typing @@ -689,7 +789,5 @@ Typing ChartType EncodeKwds Optional - ThemeConfig is_chart_type - theme diff --git a/doc/user_guide/customization.rst b/doc/user_guide/customization.rst index b95ac1c35..8c3781865 100644 --- a/doc/user_guide/customization.rst +++ b/doc/user_guide/customization.rst @@ -703,15 +703,20 @@ outside the chart itself; For example, the container may be a ``
`` element Chart Themes ------------ +.. note:: + + This material was changed considerably with the release of Altair ``5.5.0``. + Altair makes available a theme registry that lets users apply chart configurations -globally within any Python session. This is done via the ``alt.themes`` object. +globally within any Python session. +The :mod:`altair.theme` module provides :ref:`helper functions ` to interact with the registry. -The themes registry consists of functions which define a specification dictionary +Each theme in the registry is a function which define a specification dictionary that will be added to every created chart. For example, the default theme configures the default size of a single chart: >>> import altair as alt - >>> default = alt.themes.get() + >>> default = alt.theme.get() >>> default() {'config': {'view': {'continuousWidth': 300, 'continuousHeight': 300}}} @@ -740,14 +745,14 @@ The rendered chart will then reflect these configurations: Changing the Theme ~~~~~~~~~~~~~~~~~~ If you would like to enable any other theme for the length of your Python session, -you can call ``alt.themes.enable(theme_name)``. +you can call :func:`altair.theme.enable`. For example, Altair includes a theme in which the chart background is opaque rather than transparent: .. altair-plot:: :output: repr - alt.themes.enable('opaque') + alt.theme.enable('opaque') chart.to_dict() .. altair-plot:: @@ -761,7 +766,7 @@ theme named ``'none'``: .. altair-plot:: :output: repr - alt.themes.enable('none') + alt.theme.enable('none') chart.to_dict() .. altair-plot:: @@ -777,9 +782,14 @@ If you would like to use any theme just for a single chart, you can use the .. altair-plot:: :output: none - with alt.themes.enable('default'): + with alt.theme.enable('default'): spec = chart.to_json() +.. note:: + The above requires that a conversion/saving operation occurs during the ``with`` block, + such as :meth:`~Chart.to_dict`, :meth:`~Chart.to_json`, :meth:`~Chart.save`. + See https://github.com/vega/altair/issues/3586 + Built-in Themes ~~~~~~~~~~~~~~~ Currently Altair does not offer many built-in themes, but we plan to add @@ -793,10 +803,11 @@ You can get a feel for the themes inherited from `Vega Themes`_ via *Vega-Altair Defining a Custom Theme ~~~~~~~~~~~~~~~~~~~~~~~ -The theme registry also allows defining and registering custom themes. A theme is simply a function that returns a dictionary of default values -to be added to the chart specification at rendering time, which is then -registered and activated. +to be added to the chart specification at rendering time. + +Using :func:`altair.theme.register`, we can both register and enable a theme +at the site of the function definition. For example, here we define a theme in which all marks are drawn with black fill unless otherwise specified: @@ -806,26 +817,17 @@ fill unless otherwise specified: import altair as alt from vega_datasets import data - # define the theme by returning the dictionary of configurations - def black_marks(): + # define, register and enable theme + + @alt.theme.register("black_marks", enable=True) + def black_marks() -> alt.theme.ThemeConfig: return { - 'config': { - 'view': { - 'height': 300, - 'width': 300, - }, - 'mark': { - 'color': 'black', - 'fill': 'black' - } + "config": { + "view": {"continuousWidth": 300, "continuousHeight": 300}, + "mark": {"color": "black", "fill": "black"}, } } - # register the custom theme under a chosen name - alt.themes.register('black_marks', black_marks) - - # enable the newly registered theme - alt.themes.enable('black_marks') # draw the chart cars = data.cars.url diff --git a/tests/utils/test_deprecation.py b/tests/utils/test_deprecation.py index 3970f4794..3449142aa 100644 --- a/tests/utils/test_deprecation.py +++ b/tests/utils/test_deprecation.py @@ -1,9 +1,11 @@ +# ruff: noqa: B018 import re import pytest from altair.utils.deprecation import ( AltairDeprecationWarning, + _warnings_monitor, deprecated, deprecated_warn, ) @@ -38,3 +40,25 @@ def test_deprecation_warn(): match=re.compile(r"altair=3321.+this code path is a noop", flags=re.DOTALL), ): deprecated_warn("this code path is a noop", version="3321", stacklevel=1) + + +def test_deprecated_import(): + import altair as alt + + pattern = re.compile( + r"altair=5\.5\.0.+\.theme instead.+user.guide", + flags=re.DOTALL | re.IGNORECASE, + ) + with pytest.warns(AltairDeprecationWarning, match=pattern): + alt.themes + + # NOTE: Tests that second access does not trigger a warning + assert alt.themes + # Then reset cache + _warnings_monitor.clear() + + with pytest.warns(AltairDeprecationWarning, match=pattern): + from altair import themes # noqa: F401 + + assert alt.themes == alt.theme._themes + _warnings_monitor.clear() diff --git a/tests/vegalite/test_common.py b/tests/vegalite/test_common.py index 3e8760ad7..404328886 100644 --- a/tests/vegalite/test_common.py +++ b/tests/vegalite/test_common.py @@ -20,7 +20,10 @@ def basic_spec(): def make_final_spec(alt, basic_spec): - theme = alt.themes.get() + from altair.theme import _themes + + theme = _themes.get() + assert theme spec = theme() spec.update(basic_spec) return spec @@ -67,10 +70,12 @@ def test_basic_chart_from_dict(alt, basic_spec): @pytest.mark.parametrize("alt", [v5]) def test_theme_enable(alt, basic_spec): - active_theme = alt.themes.active + from altair.theme import _themes + + active_theme = _themes.active try: - alt.themes.enable("none") + _themes.enable("none") chart = alt.Chart.from_dict(basic_spec) dct = chart.to_dict() @@ -83,7 +88,7 @@ def test_theme_enable(alt, basic_spec): assert dct == basic_spec finally: # reset the theme to its initial value - alt.themes.enable(active_theme) + _themes.enable(active_theme) # pyright: ignore[reportArgumentType] @pytest.mark.parametrize("alt", [v5]) diff --git a/tests/vegalite/v5/test_api.py b/tests/vegalite/v5/test_api.py index 98b30a9b6..0f60d185a 100644 --- a/tests/vegalite/v5/test_api.py +++ b/tests/vegalite/v5/test_api.py @@ -1347,20 +1347,22 @@ def test_LookupData(): def test_themes(): + from altair import theme + chart = alt.Chart("foo.txt").mark_point() - with alt.themes.enable("default"): + with theme.enable("default"): assert chart.to_dict()["config"] == { "view": {"continuousWidth": 300, "continuousHeight": 300} } - with alt.themes.enable("opaque"): + with theme.enable("opaque"): assert chart.to_dict()["config"] == { "background": "white", "view": {"continuousWidth": 300, "continuousHeight": 300}, } - with alt.themes.enable("none"): + with theme.enable("none"): assert "config" not in chart.to_dict() diff --git a/tests/vegalite/v5/test_theme.py b/tests/vegalite/v5/test_theme.py index 97d2fb42e..da7c134ba 100644 --- a/tests/vegalite/v5/test_theme.py +++ b/tests/vegalite/v5/test_theme.py @@ -5,10 +5,11 @@ import pytest import altair.vegalite.v5 as alt -from altair.typing import ThemeConfig -from altair.vegalite.v5.schema._config import ConfigKwds +from altair import theme +from altair.theme import ConfigKwds, ThemeConfig from altair.vegalite.v5.schema._typing import is_color_hex -from altair.vegalite.v5.theme import VEGA_THEMES, register_theme, themes +from altair.vegalite.v5.theme import VEGA_THEMES +from tests import slow if TYPE_CHECKING: import sys @@ -25,26 +26,83 @@ def chart() -> alt.Chart: def test_vega_themes(chart) -> None: - for theme in VEGA_THEMES: - with alt.themes.enable(theme): + for theme_name in VEGA_THEMES: + with theme.enable(theme_name): dct = chart.to_dict() - assert dct["usermeta"] == {"embedOptions": {"theme": theme}} + assert dct["usermeta"] == {"embedOptions": {"theme": theme_name}} assert dct["config"] == { "view": {"continuousWidth": 300, "continuousHeight": 300} } -def test_register_theme_decorator() -> None: - @register_theme("unique name", enable=True) +@slow +def test_theme_remote_lambda() -> None: + """ + Compatibility test for ``lambda`` usage in `dash-vega-components`_. + + A ``lambda`` here is to fetch the remote resource **once**, wrapping the result in a function. + + .. _dash-vega-components: + https://github.com/vega/dash-vega-components/blob/c3e8cae873580bc7a52bc01daea1f27a7df02b8b/example_app.py#L13-L17 + """ + import altair as alt # noqa: I001 + from urllib.request import urlopen + import json + + URL = "https://gist.githubusercontent.com/binste/b4042fa76a89d72d45cbbb9355ec6906/raw/e36f79d722bcd9dd954389b1753a2d4a18113227/altair_theme.json" + with urlopen(URL) as response: + custom_theme = json.load(response) + + alt.theme.register("remote_binste", enable=True)(lambda: custom_theme) + assert alt.theme.active == "remote_binste" + + # NOTE: A decorator-compatible way to define an "anonymous" function + @alt.theme.register("remote_binste_2", enable=True) + def _(): + return custom_theme + + assert alt.theme.active == "remote_binste_2" + + decorated_theme = alt.theme.get() + alt.theme.enable("remote_binste") + assert alt.theme.active == "remote_binste" + lambda_theme = alt.theme.get() + + assert decorated_theme + assert lambda_theme + assert decorated_theme() == lambda_theme() + + +def test_theme_register_decorator() -> None: + @theme.register("unique name", enable=True) def custom_theme() -> ThemeConfig: return {"height": 400, "width": 700} - assert themes.active == "unique name" - registered = themes.get() + assert theme._themes.active == "unique name" == theme.active + registered = theme._themes.get() assert registered is not None + assert registered == theme.get() assert registered() == {"height": 400, "width": 700} == custom_theme() +def test_theme_unregister() -> None: + @theme.register("big square", enable=True) + def custom_theme() -> ThemeConfig: + return {"height": 1000, "width": 1000} + + assert theme.active == "big square" + fn = theme.unregister("big square") + assert fn() == custom_theme() + assert theme.active == theme._themes.active + # BUG: https://github.com/vega/altair/issues/3619 + # assert theme.active != "big square" + + with pytest.raises( + TypeError, match=r"Found no theme named 'big square' in registry." + ): + theme.unregister("big square") + + @pytest.mark.parametrize( ("color_code", "valid"), [ @@ -982,5 +1040,6 @@ def test_theme_config(theme_func: Callable[[], ThemeConfig], chart) -> None: See ``(test_vega_themes|test_register_theme_decorator)`` for comprehensive suite. """ name = cast("LiteralString", theme_func.__qualname__) - register_theme(name, enable=True) + theme.register(name, enable=True)(theme_func) assert chart.to_dict(validate=True) + assert theme.get() == theme_func diff --git a/tools/generate_api_docs.py b/tools/generate_api_docs.py index 489375786..b9c2a1a2a 100644 --- a/tools/generate_api_docs.py +++ b/tools/generate_api_docs.py @@ -26,6 +26,8 @@ further details, as this low-level documentation may not be enough to give full guidelines on their use. +.. _api-toplevel: + Top-Level Objects ----------------- .. currentmodule:: altair @@ -36,6 +38,8 @@ {toplevel_charts} +.. _api-channels: + Encoding Channels ----------------- .. currentmodule:: altair @@ -46,6 +50,8 @@ {encoding_wrappers} +.. _api-functions: + API Functions ------------- .. currentmodule:: altair @@ -56,6 +62,20 @@ {api_functions} +.. _api-theme: + +Theme +----- +.. currentmodule:: altair.theme + +.. autosummary:: + :toctree: generated/theme/ + :nosignatures: + + {theme_objects} + +.. _api-core: + Low-Level Schema Wrappers ------------------------- .. currentmodule:: altair @@ -66,6 +86,8 @@ {lowlevel_wrappers} +.. _api-cls: + API Utility Classes ------------------- .. currentmodule:: altair @@ -76,6 +98,8 @@ {api_classes} +.. _api-typing: + Typing ------ .. currentmodule:: altair.typing @@ -111,19 +135,19 @@ def iter_objects( def toplevel_charts() -> list[str]: - return sorted(iter_objects(alt.api, restrict_to_subclass=alt.TopLevelMixin)) # type: ignore[attr-defined] + return sorted(iter_objects(alt.api, restrict_to_subclass=alt.TopLevelMixin)) def encoding_wrappers() -> list[str]: - return sorted(iter_objects(alt.channels, restrict_to_subclass=alt.SchemaBase)) # type: ignore[attr-defined] + return sorted(iter_objects(alt.channels, restrict_to_subclass=alt.SchemaBase)) def api_functions() -> list[str]: # Exclude `typing` functions/SpecialForm(s) - KEEP = set(alt.api.__all__) - set(alt.typing.__all__) # type: ignore[attr-defined] + KEEP = set(alt.api.__all__) - set(alt.typing.__all__) return sorted( name - for name in iter_objects(alt.api, restrict_to_type=types.FunctionType) # type: ignore[attr-defined] + for name in iter_objects(alt.api, restrict_to_type=types.FunctionType) if name in KEEP ) @@ -137,8 +161,16 @@ def type_hints() -> list[str]: return sorted(s for s in iter_objects(alt.typing) if s in alt.typing.__all__) +def theme() -> list[str]: + sort_1 = sorted(s for s in iter_objects(alt.theme) if s in alt.theme.__all__) + # Display functions before `TypedDict`, but show `ThemeConfig` before `Kwds` + sort_2 = sorted(sort_1, key=lambda s: s.endswith("Kwds")) + sort_3 = sorted(sort_2, key=lambda s: not s.islower()) + return sort_3 + + def lowlevel_wrappers() -> list[str]: - objects = sorted(iter_objects(alt.schema.core, restrict_to_subclass=alt.SchemaBase)) # type: ignore[attr-defined] + objects = sorted(iter_objects(alt.schema.core, restrict_to_subclass=alt.SchemaBase)) # The names of these two classes are also used for classes in alt.channels. Due to # how imports are set up, these channel classes overwrite the two low-level classes # in the top-level Altair namespace. Therefore, they cannot be imported as e.g. @@ -159,6 +191,7 @@ def write_api_file() -> None: lowlevel_wrappers=sep.join(lowlevel_wrappers()), api_classes=sep.join(api_classes()), typing_objects=sep.join(type_hints()), + theme_objects=sep.join(theme()), ), encoding="utf-8", ) diff --git a/tools/generate_schema_wrapper.py b/tools/generate_schema_wrapper.py index 278606551..e024c2ca1 100644 --- a/tools/generate_schema_wrapper.py +++ b/tools/generate_schema_wrapper.py @@ -7,11 +7,12 @@ import json import sys import textwrap +from collections.abc import Iterable, Iterator from dataclasses import dataclass from itertools import chain from operator import attrgetter from pathlib import Path -from typing import TYPE_CHECKING, Any, Final, Generic, Literal, TypedDict, TypeVar +from typing import TYPE_CHECKING, Any, Final, Generic, Literal, TypeVar from urllib import request if sys.version_info >= (3, 14): @@ -47,6 +48,7 @@ from tools.schemapi.codegen import ArgInfo, AttrGetter from vl_convert import VegaThemes +T = TypeVar("T", bound="str | Iterable[str]") SCHEMA_VERSION: Final = "v5.20.1" @@ -530,6 +532,12 @@ class {classname}(DatumChannelMixin, core.{basename}): haspropsetters = True +class ModuleDef(Generic[T]): + def __init__(self, contents: T, all: Iterable[str], /) -> None: + self.contents: T = contents + self.all: list[str] = list(all) + + def schema_class(*args, **kwargs) -> str: return SchemaGenerator(*args, **kwargs).schema_class() @@ -692,7 +700,7 @@ def visit(nodes): return stack -def generate_vegalite_schema_wrapper(fp: Path, /) -> str: +def generate_vegalite_schema_wrapper(fp: Path, /) -> ModuleDef[str]: """Generate a schema wrapper at the given path.""" # TODO: generate simple tests for each wrapper basename = "VegaLiteSchema" @@ -766,7 +774,7 @@ def generate_vegalite_schema_wrapper(fp: Path, /) -> str: contents.append(definitions[name].schema_class()) contents.append("") # end with newline - return "\n".join(contents) + return ModuleDef("\n".join(contents), all_) @dataclass @@ -798,7 +806,7 @@ def non_field_names(self) -> Iterator[str]: yield self.value_class_name -def generate_vegalite_channel_wrappers(fp: Path, /) -> str: +def generate_vegalite_channel_wrappers(fp: Path, /) -> ModuleDef[list[str]]: schema = load_schema_with_shorthand_properties(fp) encoding_def = "FacetedEncoding" encoding = SchemaInfo(schema["definitions"][encoding_def], rootschema=schema) @@ -854,7 +862,7 @@ def generate_vegalite_channel_wrappers(fp: Path, /) -> str: "with_property_setters", ) it = chain.from_iterable(info.all_names for info in channel_infos.values()) - all_ = list(chain(it, COMPAT_EXPORTS)) + all_ = sorted(chain(it, COMPAT_EXPORTS)) imports = [ "import sys", "from typing import Any, overload, Sequence, List, Literal, Union, TYPE_CHECKING, TypedDict", @@ -877,7 +885,7 @@ def generate_vegalite_channel_wrappers(fp: Path, /) -> str: ENCODING_SORT_FIELD, ) TYPING_API = INTO_CONDITION, BIN, IMPUTE - contents = [ + contents: list[str] = [ HEADER, CHANNEL_MYPY_IGNORE_STATEMENTS, *imports, @@ -889,14 +897,14 @@ def generate_vegalite_channel_wrappers(fp: Path, /) -> str: f"from altair.vegalite.v5.api import {', '.join(TYPING_API)}", textwrap.indent(import_typing_extensions((3, 11), "Self"), " "), ), - "\n" f"__all__ = {sorted(all_)}\n", + "\n" f"__all__ = {all_}\n", CHANNEL_MIXINS, *class_defs, *generate_encoding_artifacts( channel_infos, ENCODE_METHOD, facet_encoding=encoding ), ] - return "\n".join(contents) + return ModuleDef(contents, all_) def generate_vegalite_mark_mixin(fp: Path, /, markdefs: dict[str, str]) -> str: @@ -1008,7 +1016,7 @@ def generate_typed_dict( name=name, metaclass_kwds=metaclass_kwds, comment=comment, - summary=summary or f"{rst_syntax_for_class(info.title)} ``TypedDict`` wrapper.", + summary=(summary or f":class:`altair.{info.title}` ``TypedDict`` wrapper."), doc=doc, td_args=args, ) @@ -1092,6 +1100,72 @@ def generate_vegalite_config_mixin(fp: Path, /) -> str: return "\n".join(code) +def generate_schema__init__( + *modules: str, + package: str, + expand: dict[Path, ModuleDef[Any]] | None = None, +) -> Iterator[str]: + """ + Generate schema subpackage init contents. + + Parameters + ---------- + *modules + Module names to expose, in addition to their members:: + + ...schema.__init__.__all__ = [ + ..., + module_1.__name__, + module_1.__all__, + module_2.__name__, + module_2.__all__, + ..., + ] + package + Absolute, dotted path for `schema`, e.g:: + + "altair.vegalite.v5.schema" + expand + Required for 2nd-pass, which explicitly defines the new ``__all__``, using newly generated names. + + .. note:: + The default `import idiom`_ works at runtime, and for ``pyright`` - but not ``mypy``. + See `issue`_. + + .. _import idiom: + https://typing.readthedocs.io/en/latest/spec/distributing.html#library-interface-public-and-private-symbols + .. _issue: + https://github.com/python/mypy/issues/15300 + """ + yield f"# ruff: noqa: F403, F405\n{HEADER_COMMENT}" + yield f"from {package} import {', '.join(modules)}" + yield from (f"from {package}.{mod} import *" for mod in modules) + yield f"SCHEMA_VERSION = {SCHEMA_VERSION!r}\n" + yield f"SCHEMA_URL = {schema_url()!r}\n" + base_all: list[str] = ["SCHEMA_URL", "SCHEMA_VERSION", *modules] + if expand: + base_all.extend( + chain.from_iterable(v.all for k, v in expand.items() if k.stem in modules) + ) + yield f"__all__ = {base_all}" + else: + yield f"__all__ = {base_all}" + yield from (f"__all__ += {mod}.__all__" for mod in modules) + + +def path_to_module_str( + fp: Path, + /, + root: Literal["altair", "doc", "sphinxext", "tests", "tools"] = "altair", +) -> str: + # NOTE: GH runner has 3x altair, local is 2x + # - Needs to be the last occurence + idx = fp.parts.index(root) + start = idx + fp.parts.count(root) - 1 if root == "altair" else idx + parents = fp.parts[start:-1] + return ".".join(parents if fp.stem == "__init__" else (*parents, fp.stem)) + + def vegalite_main(skip_download: bool = False) -> None: version = SCHEMA_VERSION vn = version.split(".")[0] @@ -1109,23 +1183,22 @@ def vegalite_main(skip_download: bool = False) -> None: # Generate __init__.py file outfile = schemapath / "__init__.py" + pkg_schema = path_to_module_str(outfile) print(f"Writing {outfile!s}") - content = [ - "# ruff: noqa\n", - "from .core import *\nfrom .channels import *\n", - f"SCHEMA_VERSION = '{version}'\n", - f"SCHEMA_URL = {schema_url(version)!r}\n", - ] - ruff.write_lint_format(outfile, content) + ruff.write_lint_format( + outfile, generate_schema__init__("channels", "core", package=pkg_schema) + ) TypeAliasTracer.update_aliases(("Map", "Mapping[str, Any]")) files: dict[Path, str | Iterable[str]] = {} + modules: dict[Path, ModuleDef[Any]] = {} # Generate the core schema wrappers fp_core = schemapath / "core.py" print(f"Generating\n {schemafile!s}\n ->{fp_core!s}") - files[fp_core] = generate_vegalite_schema_wrapper(schemafile) + modules[fp_core] = generate_vegalite_schema_wrapper(schemafile) + files[fp_core] = modules[fp_core].contents # Generate the channel wrappers fp_channels = schemapath / "channels.py" @@ -1133,7 +1206,14 @@ def vegalite_main(skip_download: bool = False) -> None: with RemapContext( {DATETIME: (TEMPORAL, DATETIME), BIN_PARAMS: (BIN,), IMPUTE_PARAMS: (IMPUTE,)} ): - files[fp_channels] = generate_vegalite_channel_wrappers(schemafile) + modules[fp_channels] = generate_vegalite_channel_wrappers(schemafile) + files[fp_channels] = modules[fp_channels].contents + + # Expand `schema.__init__.__all__` with new classes + ruff.write_lint_format( + outfile, + generate_schema__init__("channels", "core", package=pkg_schema, expand=modules), + ) # generate the mark mixin markdefs = {k: f"{k}Def" for k in ["Mark", "BoxPlot", "ErrorBar", "ErrorBand"]} diff --git a/tools/update_init_file.py b/tools/update_init_file.py index f795667b7..5ac9c7cb4 100644 --- a/tools/update_init_file.py +++ b/tools/update_init_file.py @@ -4,13 +4,18 @@ import typing as t import typing_extensions as te +from importlib import import_module as _import_module +from importlib.util import find_spec as _find_spec from inspect import getattr_static, ismodule from pathlib import Path from typing import TYPE_CHECKING from tools.codemod import ruff -_TYPING_CONSTRUCTS = { +if TYPE_CHECKING: + from collections.abc import Iterable, Iterator + +_TYPING_CONSTRUCTS: set[t.Any] = { te.TypeAlias, t.TypeVar, t.cast, @@ -36,6 +41,8 @@ te.TypeAliasType, } +DYNAMIC_ALL: tuple[te.LiteralString, ...] = ("altair.vegalite.v5",) + def update__all__variable() -> None: """ @@ -47,11 +54,8 @@ def update__all__variable() -> None: # Read existing file content import altair as alt - encoding = "utf-8" - init_path = Path(alt.__file__) - with init_path.open(encoding=encoding) as f: - lines = f.readlines() - lines = [line.strip("\n") for line in lines] + init_path = normalize_source("altair") + lines = extract_lines(init_path, strip_chars="\n") # Find first and last line of the definition of __all__ first_definition_line = None @@ -76,6 +80,10 @@ def update__all__variable() -> None: # Format file content with ruff ruff.write_lint_format(init_path, new_lines) + for source in DYNAMIC_ALL: + print(f"Updating `__all__`\n " f"{source!r}\n ->{normalize_source(source)!s}") + update_dynamic__all__(source) + def relevant_attributes(namespace: dict[str, t.Any], /) -> list[str]: """ @@ -134,5 +142,104 @@ def _is_relevant(attr: t.Any, name: str, /) -> bool: return True +def _retrieve_all(name: str, /) -> list[str]: + """Import `name` and return a defined ``__all__``.""" + found = _import_module(name).__all__ + if not found: + msg = ( + f"Expected to find a populated `__all__` for {name!r},\n" + f"but got: {found!r}" + ) + raise AttributeError(msg) + return found + + +def normalize_source(src: str | Path, /) -> Path: + """ + Return the ``Path`` representation of a module/package. + + Returned unchanged if already a ``Path``. + """ + if isinstance(src, str): + if src == "altair" or src.startswith("altair."): + if (spec := _find_spec(src)) and (origin := spec.origin): + src = origin + else: + raise ModuleNotFoundError(src, spec) + return Path(src) + else: + return src + + +def extract_lines(fp: Path, /, strip_chars: str | None = None) -> list[str]: + """Return all lines in ``fp`` with whitespace stripped.""" + with Path(fp).open(encoding="utf-8") as f: + lines = f.readlines() + if not lines: + msg = f"Found no content when reading lines for:\n{lines!r}" + raise NotImplementedError(msg) + return [line.strip(strip_chars) for line in lines] + + +def _normalize_import_lines(lines: Iterable[str]) -> Iterator[str]: + """ + Collapses file content to contain one line per import source. + + Preserves only lines **before** an existing ``__all__``. + """ + it: Iterator[str] = iter(lines) + for line in it: + if line.endswith("("): + line = line.rstrip("( ") + for s_line in it: + if s_line.endswith(","): + line = f"{line} {s_line}" + elif s_line.endswith(")"): + break + else: + NotImplementedError(f"Unexpected line:\n{s_line!r}") + yield line.rstrip(",") + elif line.startswith("__all__"): + break + else: + yield line + + +def process_lines(lines: Iterable[str], /) -> Iterator[str]: + """Normalize imports, follow ``*``(s), reconstruct `__all__``.""" + _all: set[str] = set() + for line in _normalize_import_lines(lines): + if line.startswith("#") or line == "": + yield line + elif "import" in line: + origin_stmt, members = line.split(" import ", maxsplit=1) + if members == "*": + _, origin = origin_stmt.split("from ") + targets = _retrieve_all(origin) + else: + targets = members.split(", ") + _all.update(targets) + yield line + else: + msg = f"Unexpected line:\n{line!r}" + raise NotImplementedError(msg) + yield f"__all__ = {sorted(_all)}" + + +def update_dynamic__all__(source: str | Path, /) -> None: + """ + ## Relies on all `*` imports leading to an `__all__`. + + Acceptable `source`: + + "altair.package.subpackage.etc" + Path(...) + + """ + fp = normalize_source(source) + content = process_lines(extract_lines(fp)) + ruff.write_lint_format(fp, content) + + if __name__ == "__main__": update__all__variable()