From 5207768b6e533c0509218376942309d1c7bac22f Mon Sep 17 00:00:00 2001 From: Dan Redding <125183946+dangotbanned@users.noreply.github.com> Date: Wed, 4 Sep 2024 09:58:29 +0100 Subject: [PATCH] refactor: Remove `channels` parameter in `infer_encoding_types` (#3564) * test: Monkeypatch channels global Removes the dependency in `test_infer_encoding_types` * refactor: Remove `channels` parameter in `infer_encoding_types` Was kept, but only needed for tests since #3444. As `infer_encoding_types` is not public API - this is a safe remove, no need for deprecation --- altair/utils/core.py | 29 ++--------------------------- tests/utils/test_core.py | 35 +++++++++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/altair/utils/core.py b/altair/utils/core.py index f5ef659b1..c8b47f9f8 100644 --- a/altair/utils/core.py +++ b/altair/utils/core.py @@ -33,7 +33,6 @@ if TYPE_CHECKING: import typing as t - from types import ModuleType import pandas as pd from narwhals.typing import IntoExpr @@ -812,22 +811,6 @@ class _ChannelCache: channel_to_name: dict[type[SchemaBase], str] name_to_channel: dict[str, dict[_ChannelType, type[SchemaBase]]] - @classmethod - def from_channels(cls, channels: ModuleType, /) -> _ChannelCache: - # - This branch is only kept for tests that depend on mocking `channels`. - # - No longer needs to pass around `channels` reference and rebuild every call. - c_to_n = { - c: c._encoding_name - for c in channels.__dict__.values() - if isinstance(c, type) - and issubclass(c, SchemaBase) - and hasattr(c, "_encoding_name") - } - self = cls.__new__(cls) - self.channel_to_name = c_to_n - self.name_to_channel = _invert_group_channels(c_to_n) - return self - @classmethod def from_cache(cls) -> _ChannelCache: global _CHANNEL_CACHE @@ -925,9 +908,7 @@ def _reduce(it: Iterator[tuple[type[Any], str]]) -> Any: return {k: _reduce(chans) for k, chans in grouper} -def infer_encoding_types( - args: tuple[Any, ...], kwargs: dict[str, Any], channels: ModuleType | None = None -): +def infer_encoding_types(args: tuple[Any, ...], kwargs: dict[str, Any]): """ Infer typed keyword arguments for args and kwargs. @@ -937,8 +918,6 @@ def infer_encoding_types( Sequence of function args kwargs : MutableMapping Dict of function kwargs - channels : ModuleType - The module containing all altair encoding channel classes. Returns ------- @@ -946,11 +925,7 @@ def infer_encoding_types( All args and kwargs in a single dict, with keys and types based on the channels mapping. """ - cache = ( - _ChannelCache.from_channels(channels) - if channels - else _ChannelCache.from_cache() - ) + cache = _ChannelCache.from_cache() # First use the mapping to convert args to kwargs based on their types. for arg in args: el = next(iter(arg), None) if isinstance(arg, (list, tuple)) else arg diff --git a/tests/utils/test_core.py b/tests/utils/test_core.py index 295e2b38f..d43f88dd8 100644 --- a/tests/utils/test_core.py +++ b/tests/utils/test_core.py @@ -11,6 +11,7 @@ from pandas.api.types import infer_dtype import altair as alt +from altair.utils import core from altair.utils.core import infer_encoding_types, parse_shorthand, update_nested from tests import skip_requires_pyarrow @@ -267,18 +268,40 @@ def test_update_nested(): @pytest.fixture -def channels(): +def channels() -> types.ModuleType: channels = types.ModuleType("channels") exec(FAKE_CHANNELS_MODULE, channels.__dict__) return channels +@pytest.fixture +def channels_cached(channels) -> core._ChannelCache: + """Previously ``_ChannelCache.from_channels``.""" + cached = core._ChannelCache.__new__(core._ChannelCache) + cached.channel_to_name = { + c: c._encoding_name + for c in channels.__dict__.values() + if isinstance(c, type) + and issubclass(c, alt.SchemaBase) + and hasattr(c, "_encoding_name") + } + cached.name_to_channel = core._invert_group_channels(cached.channel_to_name) + return cached + + def _getargs(*args, **kwargs): return args, kwargs -# NOTE: Dependent on a no longer needed implementation detail -def test_infer_encoding_types(channels): +def test_infer_encoding_types( + monkeypatch: pytest.MonkeyPatch, channels, channels_cached +): + # Indirectly initialize `_CHANNEL_CACHE` + infer_encoding_types((), {}) + # Replace with contents of `FAKE_CHANNELS_MODULE` + # Scoped to only this test + monkeypatch.setattr(core, "_CHANNEL_CACHE", channels_cached) + expected = { "x": channels.X("xval"), "y": channels.YValue("yval"), @@ -289,17 +312,17 @@ def test_infer_encoding_types(channels): args, kwds = _getargs( channels.X("xval"), channels.YValue("yval"), channels.StrokeWidthValue(4) ) - assert infer_encoding_types(args, kwds, channels) == expected + assert infer_encoding_types(args, kwds) == expected # All keyword args args, kwds = _getargs(x="xval", y=alt.value("yval"), strokeWidth=alt.value(4)) - assert infer_encoding_types(args, kwds, channels) == expected + assert infer_encoding_types(args, kwds) == expected # Mixed positional & keyword args, kwds = _getargs( channels.X("xval"), channels.YValue("yval"), strokeWidth=alt.value(4) ) - assert infer_encoding_types(args, kwds, channels) == expected + assert infer_encoding_types(args, kwds) == expected def test_infer_encoding_types_with_condition():