Skip to content

Commit d0784f8

Browse files
committed
refactor: str_as_lit -> str_as, invert default
- Suggested in #3427 (comment) - New default is less likely to cause errors in spec - See also #3427 (comment)
1 parent 31bad3f commit d0784f8

File tree

2 files changed

+55
-50
lines changed

2 files changed

+55
-50
lines changed

altair/vegalite/v5/api.py

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@
131131
For restricting inputs passed to `alt.value`.
132132
"""
133133

134+
_StrAsType = Literal["shorthand", "value"]
135+
134136

135137
# ------------------------------------------------------------------------
136138
# Data Utilities
@@ -636,13 +638,13 @@ def _is_extra(*objs: Any, kwargs: Map) -> Iterator[bool]:
636638

637639

638640
def _is_condition_extra(
639-
obj: Any, *objs: Any, kwargs: Map, lit: bool
641+
obj: Any, *objs: Any, kwargs: Map, str_as: _StrAsType
640642
) -> TypeIs[_Condition]:
641643
# NOTE: Short circuits on the first conflict.
642-
# 1 - originated from parse_shorthand
644+
# 1 - Originated from parse_shorthand
643645
# 2 - Used a wrapper or `dict` directly, including `extra_keys`
644646
if isinstance(obj, str):
645-
return not lit
647+
return str_as == "shorthand"
646648
else:
647649
return any(_is_extra(obj, *objs, kwargs=kwargs))
648650

@@ -731,38 +733,33 @@ def _parse_when(
731733
return _predicate_to_condition(composed, empty=empty)
732734

733735

734-
def _parse_literal(val: Any, *, str_as_lit: bool) -> dict[str, Any]:
736+
def _parse_literal(val: Any, str_as: _StrAsType, /) -> dict[str, Any]:
735737
if isinstance(val, str):
736-
return _str_as(val, "value" if str_as_lit else "field")
738+
return _str_as(val, str_as)
737739
elif _is_one_or_seq_literal_value(val):
738740
return {"value": val}
739741
else:
740742
msg = f"Expected one or more literal values, but got: {type(val).__name__!r}"
741743
raise TypeError(msg)
742744

743745

744-
def _str_as(val: str, to: Literal["datum", "field", "value"], /):
746+
def _str_as(val: str, to: _StrAsType, /):
745747
if to == "value":
746748
return value(val)
747-
elif to == "field":
749+
elif to == "shorthand":
748750
return utils.parse_shorthand(val)
749-
elif to == "datum":
750-
return _ConditionClosed(test=_expr_core.GetAttrExpression(to, val))
751751
else:
752752
msg = f"Unsupported str_as={to!r}"
753753
raise NotImplementedError(msg)
754754

755755

756756
def _parse_then(
757-
statement: _StatementOrLiteralType,
758-
kwargs: dict[str, Any],
759-
*,
760-
lit: bool,
757+
statement: _StatementOrLiteralType, kwargs: dict[str, Any], str_as: _StrAsType
761758
) -> dict[str, Any]:
762759
if isinstance(statement, core.SchemaBase):
763760
statement = statement.to_dict()
764761
elif not isinstance(statement, dict):
765-
statement = _parse_literal(statement, str_as_lit=lit)
762+
statement = _parse_literal(statement, str_as)
766763
statement.update(kwargs)
767764
return statement
768765

@@ -771,8 +768,7 @@ def _parse_otherwise(
771768
statement: _StatementOrLiteralType,
772769
conditions: _Conditional[Any],
773770
kwargs: dict[str, Any],
774-
*,
775-
lit: bool,
771+
str_as: _StrAsType,
776772
) -> SchemaBase | _Conditional[Any]:
777773
selection: SchemaBase | _Conditional[Any]
778774
if isinstance(statement, core.SchemaBase):
@@ -781,7 +777,7 @@ def _parse_otherwise(
781777
selection.condition = conditions["condition"]
782778
else:
783779
if not isinstance(statement, t.Mapping):
784-
statement = _parse_literal(statement, str_as_lit=lit)
780+
statement = _parse_literal(statement, str_as)
785781
selection = conditions
786782
selection.update(**statement, **kwargs) # type: ignore[call-arg]
787783
return selection
@@ -795,11 +791,10 @@ def _when_then(
795791
self,
796792
statement: _StatementOrLiteralType,
797793
kwargs: dict[str, Any],
798-
*,
799-
lit: bool,
794+
str_as: _StrAsType,
800795
) -> _ConditionClosed | _Condition:
801796
condition: Any = _deepcopy(self._condition)
802-
then = _parse_then(statement, kwargs, lit=lit)
797+
then = _parse_then(statement, kwargs, str_as)
803798
condition.update(then)
804799
return condition
805800

@@ -821,12 +816,12 @@ def __init__(self, condition: _ConditionType, /) -> None:
821816

822817
@overload
823818
def then(
824-
self, statement: str, *, str_as_lit: Literal[False] = ..., **kwargs: Any
825-
) -> Then[_Condition]: ...
819+
self, statement: str, *, str_as: Literal["value"] = ..., **kwargs: Any
820+
) -> Then[_Conditions]: ...
826821
@overload
827822
def then(
828-
self, statement: str, *, str_as_lit: Literal[True], **kwargs: Any
829-
) -> Then[_Conditions]: ...
823+
self, statement: str, *, str_as: Literal["shorthand"], **kwargs: Any
824+
) -> Then[_Condition]: ...
830825
@overload
831826
def then(
832827
self,
@@ -841,7 +836,7 @@ def then(
841836
self,
842837
statement: _StatementOrLiteralType,
843838
*,
844-
str_as_lit: bool = False,
839+
str_as: _StrAsType = "value",
845840
**kwargs: Any,
846841
) -> Then[Any]:
847842
"""Attach a statement to this predicate.
@@ -850,17 +845,20 @@ def then(
850845
----------
851846
statement
852847
A spec or value to use when the preceding :func:`.when()` clause is true.
853-
str_as_lit
854-
Wrap ``str`` in :func:`.value()` instead of encoding as `shorthand<https://altair-viz.github.io/user_guide/encodings/index.html#encoding-shorthands>`__.
848+
str_as
849+
Wrap strings in :func:`.value()` or encode as `shorthand<https://altair-viz.github.io/user_guide/encodings/index.html#encoding-shorthands>`__.
850+
851+
.. note::
852+
``str_as="shorthand"`` is the behavior used in :func:`.condition()`.
855853
**kwargs
856854
Additional keyword args are added to the resulting ``dict``.
857855
858856
Returns
859857
-------
860858
:class:`Then`
861859
"""
862-
condition = self._when_then(statement, kwargs, lit=str_as_lit)
863-
if _is_condition_extra(condition, statement, kwargs=kwargs, lit=str_as_lit):
860+
condition = self._when_then(statement, kwargs, str_as)
861+
if _is_condition_extra(condition, statement, kwargs=kwargs, str_as=str_as):
864862
return Then(_Conditional(condition=condition))
865863
else:
866864
return Then(_Conditional(condition=[condition]))
@@ -889,7 +887,7 @@ def __init__(self, conditions: _Conditional[_C], /) -> None:
889887

890888
@overload
891889
def otherwise(
892-
self, statement: _TSchemaBase, *, str_as_lit: bool = ..., **kwargs: Any
890+
self, statement: _TSchemaBase, *, str_as: _StrAsType = ..., **kwargs: Any
893891
) -> _TSchemaBase: ...
894892
@overload
895893
def otherwise(
@@ -902,14 +900,14 @@ def otherwise(
902900
self,
903901
statement: Map | _OneOrSeqLiteralValue,
904902
*,
905-
str_as_lit: bool = ...,
903+
str_as: _StrAsType = ...,
906904
**kwargs: Any,
907905
) -> _Conditional[Any]: ...
908906
def otherwise(
909907
self,
910908
statement: _StatementOrLiteralType,
911909
*,
912-
str_as_lit: bool = False,
910+
str_as: _StrAsType = "value",
913911
**kwargs: Any,
914912
) -> SchemaBase | _Conditional[Any]:
915913
"""Finalize the condition with a default value.
@@ -921,13 +919,16 @@ def otherwise(
921919
922920
.. note::
923921
Roughly equivalent to an ``else`` clause.
924-
str_as_lit
925-
Wrap ``str`` in :func:`.value()` instead of encoding as `shorthand<https://altair-viz.github.io/user_guide/encodings/index.html#encoding-shorthands>`__.
922+
str_as
923+
Wrap strings in :func:`.value()` or encode as `shorthand<https://altair-viz.github.io/user_guide/encodings/index.html#encoding-shorthands>`__.
924+
925+
.. note::
926+
``str_as="shorthand"`` is the behavior used in :func:`.condition()`.
926927
**kwargs
927928
Additional keyword args are added to the resulting ``dict``.
928929
"""
929930
conditions: _Conditional[Any]
930-
is_extra = functools.partial(_is_condition_extra, kwargs=kwargs, lit=str_as_lit)
931+
is_extra = functools.partial(_is_condition_extra, kwargs=kwargs, str_as=str_as)
931932
if is_extra(self.condition, statement):
932933
current = self.condition
933934
if isinstance(current, list) and len(current) == 1:
@@ -942,7 +943,7 @@ def otherwise(
942943
msg = (
943944
f"Only one field may be used within a condition.\n"
944945
f"Shorthand {statement!r} would conflict with {cond!r}\n\n"
945-
f"Pass `str_as_lit=True` if {statement!r} is not shorthand."
946+
f"Pass `str_as='value'` if {statement!r} is not shorthand."
946947
)
947948
raise TypeError(msg)
948949
else:
@@ -954,7 +955,7 @@ def otherwise(
954955
raise TypeError(msg)
955956
else:
956957
conditions = self.to_dict()
957-
return _parse_otherwise(statement, conditions, kwargs, lit=str_as_lit)
958+
return _parse_otherwise(statement, conditions, kwargs, str_as)
958959

959960
def when(
960961
self,
@@ -1040,7 +1041,7 @@ def then(
10401041
self,
10411042
statement: _StatementOrLiteralType,
10421043
*,
1043-
str_as_lit: bool = False,
1044+
str_as: _StrAsType = "value",
10441045
**kwargs: Any,
10451046
) -> Then[_Conditions]:
10461047
"""Attach a statement to this predicate.
@@ -1049,15 +1050,19 @@ def then(
10491050
----------
10501051
statement
10511052
A spec or value to use when the preceding :meth:`Then.when()` clause is true.
1052-
str_as_lit
1053-
Wrap ``str`` in :func:`.value()` instead of encoding as `shorthand<https://altair-viz.github.io/user_guide/encodings/index.html#encoding-shorthands>`__.
1053+
str_as
1054+
Wrap strings in :func:`.value()` or encode as `shorthand<https://altair-viz.github.io/user_guide/encodings/index.html#encoding-shorthands>`__.
1055+
1056+
.. note::
1057+
``str_as="shorthand"`` is the behavior used in :func:`.condition()`.
10541058
**kwargs
10551059
Additional keyword args are added to the resulting ``dict``.
1060+
10561061
Returns
10571062
-------
10581063
:class:`Then`
10591064
"""
1060-
condition = self._when_then(statement, kwargs, lit=str_as_lit)
1065+
condition = self._when_then(statement, kwargs, str_as)
10611066
conditions = self._conditions.copy()
10621067
conditions["condition"].append(condition)
10631068
return Then(conditions)

tests/vegalite/v5/test_api.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -498,11 +498,7 @@ def test_when_labels_position_based_on_condition() -> None:
498498
param_color_py_expr = alt.param(
499499
expr=alt.expr.if_(param_width_lt_200, "red", "black")
500500
)
501-
when = (
502-
alt.when(param_width_lt_200)
503-
.then(alt.value("red"))
504-
.otherwise("black", str_as_lit=True)
505-
)
501+
when = alt.when(param_width_lt_200).then(alt.value("red")).otherwise("black")
506502
cond = when["condition"][0]
507503
otherwise = when["value"]
508504
param_color_py_when = alt.param(
@@ -563,7 +559,7 @@ def test_when_stress():
563559
when = alt.when(brush)
564560
reveal_msg = re.compile(r"Only one field.+Shorthand 'max\(\)'", flags=re.DOTALL)
565561
with pytest.raises(TypeError, match=reveal_msg):
566-
when.then("count()").otherwise("max()")
562+
when.then("count()", str_as="shorthand").otherwise("max()", str_as="shorthand")
567563

568564
chain_mixed_msg = re.compile(
569565
r"Chained.+mixed.+conflict.+\{'field': 'field_1', 'type': 'quantitative'\}.+otherwise",
@@ -575,7 +571,7 @@ def test_when_stress():
575571
)
576572

577573
with pytest.raises(TypeError, match=chain_mixed_msg):
578-
when.then("field_1:Q").when(Genre="pop")
574+
when.then("field_1:Q", str_as="shorthand").when(Genre="pop")
579575

580576
chain_otherwise_msg = re.compile(
581577
r"Chained.+mixed.+field.+AggregatedFieldDef.+'this_field_here'",
@@ -584,7 +580,7 @@ def test_when_stress():
584580
with pytest.raises(TypeError, match=chain_otherwise_msg):
585581
when.then(5).when(
586582
alt.selection_point(fields=["b"]) | brush, empty=False, b=63812
587-
).then("min(foo):Q").otherwise(
583+
).then("min(foo):Q", str_as="shorthand").otherwise(
588584
alt.AggregatedFieldDef(
589585
"argmax", field="field_9", **{"as": "this_field_here"}
590586
)
@@ -630,7 +626,11 @@ def test_when_condition_parity(
630626
.to_dict()
631627
)
632628

633-
input_when = alt.when(when, empty=empty).then(then).otherwise(otherwise)
629+
input_when = (
630+
alt.when(when, empty=empty)
631+
.then(then, str_as="shorthand")
632+
.otherwise(otherwise, str_as="shorthand")
633+
)
634634
chart_when = (
635635
alt.Chart(cars)
636636
.mark_rect()

0 commit comments

Comments
 (0)