Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: alt.themes -> alt.theme #3618

Open
wants to merge 68 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
54e7e48
chore: Add notes for change targets
dangotbanned Sep 28, 2024
66c51c3
chore: Add TODO in `update_init_file.py`
dangotbanned Sep 28, 2024
754f665
refactor: Remove `altair.vegalite.v5.theme` from `alt.__all__`
dangotbanned Sep 28, 2024
3dd9100
refactor: Add `alt.theme.py`
dangotbanned Sep 28, 2024
b1be633
build(typing): Generate `altair.vegalite.v5.schema.__init__.__all__`
dangotbanned Sep 29, 2024
0bd8f75
build: Generate `altair.vegalite.v5.__init__.__all__`
dangotbanned Sep 29, 2024
c996c95
refactor: `utils.theme` -> `vegalite.v5.theme`
dangotbanned Sep 29, 2024
c286c38
refactor: Remove `alt.typing.theme`
dangotbanned Sep 29, 2024
ef9b846
refactor: Adds `alt.theme` implementation
dangotbanned Sep 29, 2024
e8d3062
test: Fix `test_common`
dangotbanned Sep 29, 2024
f7fe38b
chore(typing): Remove now fix ignore comments
dangotbanned Sep 29, 2024
0a1c8d3
fix(typing): Use a bounded `TypeVar` to support `list[str]`
dangotbanned Sep 29, 2024
05eca89
refactor: Add deprecation handling `alt.__init__.__getattr__`
dangotbanned Sep 29, 2024
217d7fb
docs: Adds `alt.theme` to API Reference
dangotbanned Sep 29, 2024
5078f88
docs: Add missing `altair` qualifier for `TypedDict`(s)
dangotbanned Sep 29, 2024
2339116
fix(typing): Preserve generics in `PluginEnabler`
dangotbanned Sep 29, 2024
48e44c0
refactor: Refine and fully document `generate_schema__init__`
dangotbanned Sep 30, 2024
a7dbaca
refactor: Use new functions for part of `update__all__variable`
dangotbanned Sep 30, 2024
a0947f8
fix: Don't consider suffix a part in `path_to_module_str`
dangotbanned Sep 30, 2024
1e9f639
fix: Account for GH runner path
dangotbanned Sep 30, 2024
d895447
docs: Add link targets for API Reference sections
dangotbanned Sep 30, 2024
a9538bc
docs: Customize theme toctree order
dangotbanned Sep 30, 2024
e114493
docs: Update User Guide section
dangotbanned Sep 30, 2024
c8033b2
docs: Provide API ref link in warning
dangotbanned Sep 30, 2024
ef0f263
refactor: Remove unused `__future__` import
dangotbanned Sep 30, 2024
58da996
feat: Support `alt.theme.(active|options)`
dangotbanned Oct 1, 2024
bc507a9
fix(typing): Partial resolve `mypy` `__getattr__`
dangotbanned Oct 1, 2024
e0f4721
test: Rename test to `test_theme_register_decorator`
dangotbanned Oct 1, 2024
4e8def0
test: Add some more `(theme|themes)` equal checks
dangotbanned Oct 1, 2024
9bd0316
test: Add `test_theme_unregister`
dangotbanned Oct 1, 2024
0d95b44
feat: Raise instead of returning `None` in `unregister`
dangotbanned Oct 1, 2024
3e6bde8
docs: Update remaining `ThemeRegistry` methods
dangotbanned Oct 1, 2024
6141c7c
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 2, 2024
d1ae98e
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 3, 2024
2ca4b11
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 5, 2024
d3dccab
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 6, 2024
7c49b51
test: Adds `test_theme_remote_lambda`
dangotbanned Oct 11, 2024
a3e2d28
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 12, 2024
a018165
docs: Suggest `alt.theme` instead of `alt.theme.themes`
dangotbanned Oct 12, 2024
98f4ed3
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 13, 2024
4fe4321
Merge remote-tracking branch 'upstream/main' into refac-alt-theme
dangotbanned Oct 17, 2024
2b3eb35
refactor: Rename `theme.themes` -> `theme._themes`
dangotbanned Oct 18, 2024
10872b6
docs: Update `@theme.register`
dangotbanned Oct 18, 2024
1127534
docs: Update `customization.rst`
dangotbanned Oct 18, 2024
cb56c82
feat: Add support displaying warning once
dangotbanned Oct 18, 2024
0977228
docs: Adds `utils.deprecation.__all__`
dangotbanned Oct 18, 2024
5a1dc68
docs: Add examples to deprecation message
dangotbanned Oct 19, 2024
22d7df2
docs: Prefix `"
dangotbanned Oct 19, 2024
25cbdc8
docs(typing): Adds static-only deprecation for `themes.register`
dangotbanned Oct 19, 2024
e29a2f7
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 20, 2024
a0497e0
docs: "Deprecated in" -> "Deprecated since"
dangotbanned Oct 20, 2024
b84fd5f
docs: Add more specific constraints to `@deprecated_static_only`
dangotbanned Oct 20, 2024
bf11ec1
Merge remote-tracking branch 'upstream/main' into refac-alt-theme
dangotbanned Oct 21, 2024
afcd1b9
Merge remote-tracking branch 'upstream/HEAD' into refac-alt-theme
dangotbanned Oct 23, 2024
d68cd49
Merge remote-tracking branch 'upstream/main' into refac-alt-theme
dangotbanned Oct 25, 2024
acebc1c
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 26, 2024
31da4ae
Merge remote-tracking branch 'upstream/main' into refac-alt-theme
dangotbanned Oct 29, 2024
1700af5
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 30, 2024
867c11a
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 30, 2024
367dcc2
Merge branch 'main' into refac-alt-theme
dangotbanned Oct 31, 2024
e4356d0
Merge branch 'main' into refac-alt-theme
dangotbanned Nov 2, 2024
85cb6be
Merge branch 'main' into refac-alt-theme
dangotbanned Nov 3, 2024
565f271
Merge remote-tracking branch 'upstream/main' into refac-alt-theme
dangotbanned Nov 3, 2024
32ea9e3
Merge remote-tracking branch 'upstream/main' into refac-alt-theme
dangotbanned Nov 3, 2024
2444e03
Merge branch 'main' into refac-alt-theme
dangotbanned Nov 4, 2024
fd4a139
Merge branch 'main' into refac-alt-theme
dangotbanned Nov 5, 2024
cf4a043
Merge branch 'main' into refac-alt-theme
dangotbanned Nov 5, 2024
4d100ae
Merge remote-tracking branch 'upstream/main' into refac-alt-theme
dangotbanned Nov 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions altair/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,6 @@
"mixins",
"param",
"parse_shorthand",
"register_theme",
"renderers",
"repeat",
"sample",
Expand All @@ -627,7 +626,6 @@
"sequence",
"sphere",
"theme",
"themes",
"to_csv",
"to_json",
"to_values",
Expand All @@ -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":
mattijn marked this conversation as resolved.
Show resolved Hide resolved
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,
dangotbanned marked this conversation as resolved.
Show resolved Hide resolved
action="once",
)
return theme._themes
else:
msg = f"module {__name__!r} has no attribute {name!r}"
raise AttributeError(msg)
321 changes: 321 additions & 0 deletions altair/theme.py
Original file line number Diff line number Diff line change
@@ -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,
dangotbanned marked this conversation as resolved.
Show resolved Hide resolved
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)
4 changes: 0 additions & 4 deletions altair/typing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
1 change: 0 additions & 1 deletion altair/typing/theme.py

This file was deleted.

Loading