diff --git a/altair/utils/__init__.py b/altair/utils/__init__.py index 2b3cd6e85..4befd99a3 100644 --- a/altair/utils/__init__.py +++ b/altair/utils/__init__.py @@ -13,7 +13,7 @@ from .html import spec_to_html from .plugin_registry import PluginRegistry from .deprecation import AltairDeprecationWarning, deprecated, deprecated_warn -from .schemapi import Undefined, Optional +from .schemapi import Undefined, Optional, is_undefined __all__ = ( @@ -28,6 +28,7 @@ "display_traceback", "infer_encoding_types", "infer_vegalite_type_for_pandas", + "is_undefined", "parse_shorthand", "sanitize_narwhals_dataframe", "sanitize_pandas_dataframe", diff --git a/altair/utils/schemapi.py b/altair/utils/schemapi.py index a7278d115..c24090aa0 100644 --- a/altair/utils/schemapi.py +++ b/altair/utils/schemapi.py @@ -787,6 +787,19 @@ def func_2( """ +def is_undefined(obj: Any) -> TypeIs[UndefinedType]: + """Type-safe singleton check for `UndefinedType`. + + Notes + ----- + - Using `obj is Undefined` does not narrow from `UndefinedType` in a union. + - Due to the assumption that other `UndefinedType`'s could exist. + - Current [typing spec advises](https://typing.readthedocs.io/en/latest/spec/concepts.html#support-for-singleton-types-in-unions) using an `Enum`. + - Otherwise, requires an explicit guard to inform the type checker. + """ + return obj is Undefined + + class SchemaBase: """Base class for schema wrappers. diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index 7f2ab4a04..2320f2b94 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -107,7 +107,6 @@ TopLevelSelectionParameter, SelectionParameter, InlineDataset, - UndefinedType, ) from altair.expr.core import ( BinaryExpression, @@ -181,7 +180,7 @@ def _consolidate_data(data: Any, context: Any) -> Any: kwds = {} if isinstance(data, core.InlineData): - if _is_undefined(data.name) and not _is_undefined(data.values): + if utils.is_undefined(data.name) and not utils.is_undefined(data.values): if isinstance(data.values, core.InlineDataset): values = data.to_dict()["values"] else: @@ -192,7 +191,7 @@ def _consolidate_data(data: Any, context: Any) -> Any: values = data["values"] kwds = {k: v for k, v in data.items() if k != "values"} - if not _is_undefined(values): + if not utils.is_undefined(values): name = _dataset_name(values) data = core.NamedData(name=name, **kwds) context.setdefault("datasets", {})[name] = values @@ -416,7 +415,7 @@ def _from_expr(self, expr) -> SelectionExpression: def check_fields_and_encodings(parameter: Parameter, field_name: str) -> bool: param = parameter.param - if _is_undefined(param) or isinstance(param, core.VariableParameter): + if utils.is_undefined(param) or isinstance(param, core.VariableParameter): return False for prop in ["fields", "encodings"]: try: @@ -485,19 +484,6 @@ def _is_test_predicate(obj: Any) -> TypeIs[_TestPredicateType]: return isinstance(obj, (str, _expr_core.Expression, core.PredicateComposition)) -def _is_undefined(obj: Any) -> TypeIs[UndefinedType]: - """Type-safe singleton check for `UndefinedType`. - - Notes - ----- - - Using `obj is Undefined` does not narrow from `UndefinedType` in a union. - - Due to the assumption that other `UndefinedType`'s could exist. - - Current [typing spec advises](https://typing.readthedocs.io/en/latest/spec/concepts.html#support-for-singleton-types-in-unions) using an `Enum`. - - Otherwise, requires an explicit guard to inform the type checker. - """ - return obj is Undefined - - def _get_predicate_expr(p: Parameter) -> Optional[str | SchemaBase]: # https://vega.github.io/vega-lite/docs/predicate.html return getattr(p.param, "expr", Undefined) @@ -509,7 +495,7 @@ def _predicate_to_condition( condition: _ConditionType if isinstance(predicate, Parameter): predicate_expr = _get_predicate_expr(predicate) - if predicate.param_type == "selection" or _is_undefined(predicate_expr): + if predicate.param_type == "selection" or utils.is_undefined(predicate_expr): condition = {"param": predicate.name} if isinstance(empty, bool): condition["empty"] = empty @@ -696,7 +682,7 @@ def _parse_when( **constraints: _FieldEqualType, ) -> _ConditionType: composed: _PredicateType - if _is_undefined(predicate): + if utils.is_undefined(predicate): if more_predicates or constraints: composed = _parse_when_compose(more_predicates, constraints) else: @@ -1106,7 +1092,7 @@ def param( empty_remap = {"none": False, "all": True} parameter = Parameter(name) - if not _is_undefined(empty): + if not utils.is_undefined(empty): if isinstance(empty, bool) and not isinstance(empty, str): parameter.empty = empty elif empty in empty_remap: @@ -1604,7 +1590,7 @@ def to_dict( copy = _top_schema_base(self).copy(deep=False) original_data = getattr(copy, "data", Undefined) - if not _is_undefined(original_data): + if not utils.is_undefined(original_data): try: data = _to_eager_narwhals_dataframe(original_data) except TypeError: diff --git a/tools/schemapi/schemapi.py b/tools/schemapi/schemapi.py index b55a4390a..5bfe39053 100644 --- a/tools/schemapi/schemapi.py +++ b/tools/schemapi/schemapi.py @@ -785,6 +785,19 @@ def func_2( """ +def is_undefined(obj: Any) -> TypeIs[UndefinedType]: + """Type-safe singleton check for `UndefinedType`. + + Notes + ----- + - Using `obj is Undefined` does not narrow from `UndefinedType` in a union. + - Due to the assumption that other `UndefinedType`'s could exist. + - Current [typing spec advises](https://typing.readthedocs.io/en/latest/spec/concepts.html#support-for-singleton-types-in-unions) using an `Enum`. + - Otherwise, requires an explicit guard to inform the type checker. + """ + return obj is Undefined + + class SchemaBase: """Base class for schema wrappers.