Skip to content

Commit

Permalink
feat: Support a wider range of iterables in SchemaBase.to_dict (#3501)
Browse files Browse the repository at this point in the history
* feat: Support a wider range of iterables in `SchemaBase.to_dict`

* test: Update validation errors to use non-iterable element type

Previously `list[set[str]]`

* fix: Prevent `to_dict` method being called on `pd.Series`

AFAIK, this is the intended case for `Parameter | Expression` not for converting arbitrary objects

* test: Add tests for iterables and ranges

* fix(typing): Ignore type errors for tests

* refactor: Use `narwhals.stable.v1`

#3501 (comment)

* test: Increase coverage in `test_to_dict_iterables`

The original test obscured the fact that this change applies anywhere a `Sequence` is annotated.

* docs: Add a doc for `test_to_dict_iterables`

* revert: Change `test_chart_validation_errors` back to demonstrate failures to @joelostblom

Reverting 1f3dfd3

#3501 (comment)

* docs: Update User Guide to use `Sequence`

#3501 (comment)

* test: Fix `test_chart_validation_errors` failure verbosity

#3501 (comment)

* refactor: Move `inspect.cleandoc` inside of `test_chart_validation_errors`

All 18 cases use this, saves 30 lines

* revert: Restore original fix to `test_chart_validation_errors`

#3501 (comment)
#3501 (comment)
#3501 (comment)

* docs: Remove "ordered" descriptor from `Sequence`

Co-authored-by: Joel Ostblom <[email protected]>

* test: Remove missed `inspect.cleandoc`

#3501 (comment)

* test: Only modify message, not input to `test_chart_validation_errors`

#3501 (comment)

* style: fix oddly formatted `test_multiple_field_strings_in_condition`

---------

Co-authored-by: Joel Ostblom <[email protected]>
  • Loading branch information
dangotbanned and joelostblom authored Aug 9, 2024
1 parent 3e9faaa commit f0c1e0a
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 139 deletions.
23 changes: 22 additions & 1 deletion altair/utils/schemapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import jsonschema
import jsonschema.exceptions
import jsonschema.validators
import narwhals.stable.v1 as nw
from packaging.version import Version

# This leads to circular imports with the vegalite module. Currently, this works
Expand Down Expand Up @@ -488,6 +489,14 @@ def _subclasses(cls: type[Any]) -> Iterator[type[Any]]:
yield cls


def _from_array_like(obj: Iterable[Any], /) -> list[Any]:
try:
ser = nw.from_native(obj, strict=True, series_only=True)
return ser.to_list()
except TypeError:
return list(obj)


def _todict(obj: Any, context: dict[str, Any] | None, np_opt: Any, pd_opt: Any) -> Any:
"""Convert an object to a dict representation."""
if np_opt is not None:
Expand All @@ -512,10 +521,16 @@ def _todict(obj: Any, context: dict[str, Any] | None, np_opt: Any, pd_opt: Any)
for k, v in obj.items()
if v is not Undefined
}
elif hasattr(obj, "to_dict"):
elif (
hasattr(obj, "to_dict")
and (module_name := obj.__module__)
and module_name.startswith("altair")
):
return obj.to_dict()
elif pd_opt is not None and isinstance(obj, pd_opt.Timestamp):
return pd_opt.Timestamp(obj).isoformat()
elif _is_iterable(obj, exclude=(str, bytes)):
return _todict(_from_array_like(obj), context, np_opt, pd_opt)
else:
return obj

Expand Down Expand Up @@ -1232,6 +1247,12 @@ def _is_list(obj: Any | list[Any]) -> TypeIs[list[Any]]:
return isinstance(obj, list)


def _is_iterable(
obj: Any, *, exclude: type | tuple[type, ...] = (str, bytes)
) -> TypeIs[Iterable[Any]]:
return not isinstance(obj, exclude) and isinstance(obj, Iterable)


def _passthrough(*args: Any, **kwds: Any) -> Any | dict[str, Any]:
return args[0] if args else kwds

Expand Down
2 changes: 1 addition & 1 deletion doc/user_guide/encodings/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ options available to change the sort order:
- Passing the name of an encoding channel to ``sort``, such as ``"x"`` or ``"y"``, allows for
sorting by that channel. An optional minus prefix can be used for a descending
sort. For example ``sort='-x'`` would sort by the x channel in descending order.
- Passing a list to ``sort`` allows you to explicitly set the order in which
- Passing a `Sequence <https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range>`_ to ``sort`` allows you to explicitly set the order in which
you would like the encoding to appear
- Using the ``field`` and ``op`` parameters to specify a field and aggregation operation to sort by.

Expand Down
Loading

0 comments on commit f0c1e0a

Please sign in to comment.