Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
331a52e
fix(DRAFT): Add examples for `SchemaLike`
dangotbanned Sep 4, 2024
633c0c2
chore(ruff): Ignore unused variables
dangotbanned Sep 4, 2024
f59a290
Merge remote-tracking branch 'upstream/main' into schema-like-proto
dangotbanned Sep 14, 2024
f34abe9
style: Reorder `_Condition...` definitions
dangotbanned Sep 14, 2024
2dc1f21
chore: Remove example code from `test_api`
dangotbanned Sep 14, 2024
c86a107
test: Extend `test_when_typing`
dangotbanned Sep 14, 2024
5ab0616
feat(typing): Adds `SchemaLike`, `ConditionLike` protocols
dangotbanned Sep 15, 2024
adf3e56
refactor: Redefine `Then` as a `ConditionLike`
dangotbanned Sep 15, 2024
7315582
feat: Convert `SchemaLike` in `Chart.encode`
dangotbanned Sep 15, 2024
d169a1b
feat(typing): Add `IntoCondition` alias
dangotbanned Sep 15, 2024
c5632f4
feat(typing): Use `IntoCondition` in `Chart.encode` annotations
dangotbanned Sep 15, 2024
2f74e8e
fix: Add more exclusions to `generate_api_docs`
dangotbanned Sep 15, 2024
160b16c
build: run `generate-schema-wrapper`
dangotbanned Sep 15, 2024
e470a98
fix: Make `Parameter`, `(Parameter|Selection)Expression` `SchemaLike`…
dangotbanned Sep 15, 2024
f49c9f5
test: Update `test_when_typing`
dangotbanned Sep 15, 2024
a773c97
chore: Remove now-fixed type ignore
dangotbanned Sep 15, 2024
9f49ef6
fix: Update `TypedDict` version bound
dangotbanned Sep 15, 2024
c420a1c
test: Remove proposal demo
dangotbanned Sep 15, 2024
7925d90
fix: Confirm `ChainedWhen.then` structural soundness
dangotbanned Sep 15, 2024
960e134
test: Update `test_when_multiple_fields`
dangotbanned Sep 15, 2024
5ee39c2
chore(typing): Add a temporary ignore
dangotbanned Sep 15, 2024
969e0e3
docs: Add summary for `_is_condition_closed`
dangotbanned Sep 15, 2024
8cad6af
feat: Add repr for `Then`
dangotbanned Sep 15, 2024
2d9f248
docs: Add `IntoCondition`, `_Conditional` docs
dangotbanned Sep 15, 2024
da9784e
docs: Add `SchemaLike` doc
dangotbanned Sep 15, 2024
1971daa
feat: Use `module.__all__` to simplify `generate_api_docs`
dangotbanned Sep 15, 2024
3e51d1e
Merge branch 'main' into schema-like-proto
dangotbanned Sep 15, 2024
d20109e
docs: Add `ConditionLike` doc, standardize w/ `SchemaLike`
dangotbanned Sep 16, 2024
0a6d599
refactor(typing): Factor out `_ConditionType`, use `_Condition` (`Typ…
dangotbanned Sep 16, 2024
7865c91
test(typing): Wrap returned "immutable" `TypedDict` before modifying
dangotbanned Sep 16, 2024
0a93309
docs: Update (`_Condition|Conditions`) aliases
dangotbanned Sep 16, 2024
08bf16e
feat(typing): Adds `Any...` aliases in `channels.py`
dangotbanned Sep 16, 2024
19966c1
Merge branch 'main' into schema-like-proto
dangotbanned Sep 17, 2024
7351395
Merge branch 'main' into schema-like-proto
dangotbanned Sep 21, 2024
ec5e415
Merge branch 'main' into schema-like-proto
dangotbanned Sep 22, 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
3 changes: 2 additions & 1 deletion altair/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
from .deprecation import AltairDeprecationWarning, deprecated, deprecated_warn
from .html import spec_to_html
from .plugin_registry import PluginRegistry
from .schemapi import Optional, SchemaBase, Undefined, is_undefined
from .schemapi import Optional, SchemaBase, SchemaLike, Undefined, is_undefined

__all__ = (
"SHORTHAND_KEYS",
"AltairDeprecationWarning",
"Optional",
"PluginRegistry",
"SchemaBase",
"SchemaLike",
"Undefined",
"deprecated",
"deprecated_warn",
Expand Down
4 changes: 3 additions & 1 deletion altair/utils/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from narwhals.dependencies import get_polars, is_pandas_dataframe
from narwhals.typing import IntoDataFrame

from altair.utils.schemapi import SchemaBase, Undefined
from altair.utils.schemapi import SchemaBase, SchemaLike, Undefined

if sys.version_info >= (3, 12):
from typing import Protocol, TypeAliasType, runtime_checkable
Expand Down Expand Up @@ -869,6 +869,8 @@ def _wrap_in_channel(self, obj: Any, encoding: str, /):
obj = {"shorthand": obj}
elif isinstance(obj, (list, tuple)):
return [self._wrap_in_channel(el, encoding) for el in obj]
elif isinstance(obj, SchemaLike):
obj = obj.to_dict()
if channel := self.name_to_channel.get(encoding):
tp = channel["value" if "value" in obj else "field"]
try:
Expand Down
102 changes: 97 additions & 5 deletions altair/utils/schemapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
Any,
Dict,
Final,
Generic,
Iterable,
Iterator,
List,
Literal,
Mapping,
Sequence,
TypeVar,
Union,
Expand All @@ -41,6 +43,11 @@
# not yet be fully instantiated in case your code is being executed during import time
from altair import vegalite

if sys.version_info >= (3, 12):
from typing import Protocol, TypeAliasType, runtime_checkable
else:
from typing_extensions import Protocol, TypeAliasType, runtime_checkable

if TYPE_CHECKING:
from types import ModuleType
from typing import ClassVar
Expand Down Expand Up @@ -524,11 +531,7 @@ 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")
and (module_name := obj.__module__)
and module_name.startswith("altair")
):
elif isinstance(obj, SchemaLike):
return obj.to_dict()
elif pd_opt is not None and isinstance(obj, pd_opt.Timestamp):
return pd_opt.Timestamp(obj).isoformat()
Expand Down Expand Up @@ -789,6 +792,95 @@ def _get_default_error_message(
return message


_JSON_VT_co = TypeVar(
"_JSON_VT_co",
Literal["string"],
Literal["object"],
Literal["array"],
covariant=True,
)
"""
One of a subset of JSON Schema `primitive types`_:

["string", "object", "array"]

.. _primitive types:
https://json-schema.org/draft-07/json-schema-validation#rfc.section.6.1.1
"""

_TypeMap = TypeAliasType(
"_TypeMap", Mapping[Literal["type"], _JSON_VT_co], type_params=(_JSON_VT_co,)
)
"""
A single item JSON Schema using the `type`_ keyword.

This may represent **one of**:

{"type": "string"}
{"type": "object"}
{"type": "array"}

.. _type:
https://json-schema.org/understanding-json-schema/reference/type
"""

# NOTE: Type checkers want opposing things:
# - `mypy` : Covariant type variable "_JSON_VT_co" used in protocol where invariant one is expected [misc]
# - `pyright`: Type variable "_JSON_VT_co" used in generic protocol "SchemaLike" should be covariant [reportInvalidTypeVarUse]
# Siding with `pyright` as this is consistent with https://github.com/python/typeshed/blob/9e506eb5e8fc2823db8c60ad561b1145ff114947/stdlib/typing.pyi#L690


@runtime_checkable
class SchemaLike(Generic[_JSON_VT_co], Protocol): # type: ignore[misc]
"""
Represents ``altair`` classes which *may* not derive ``SchemaBase``.

Attributes
----------
_schema
A single item JSON Schema using the `type`_ keyword.

Notes
-----
Should be kept tightly defined to the **minimum** requirements for:
- Converting into a form that can be validated by `jsonschema`_.
- Avoiding calling ``.to_dict()`` on a class external to ``altair``.
- ``_schema`` is more accurately described as a ``ClassVar``
- See `discussion`_ for blocking issue.

.. _jsonschema:
https://github.com/python-jsonschema/jsonschema
.. _type:
https://json-schema.org/understanding-json-schema/reference/type
.. _discussion:
https://github.com/python/typing/discussions/1424
"""

_schema: _TypeMap[_JSON_VT_co]

def to_dict(self, *args, **kwds) -> Any: ...


@runtime_checkable
class ConditionLike(SchemaLike[Literal["object"]], Protocol):
"""
Represents the wrapped state of a conditional encoding or property.

Attributes
----------
condition
One or more (predicate, statement) pairs which each form a condition.

Notes
-----
- Can be extended with additional conditions.
- *Does not* define a default value, but can be finalized with one.
"""

condition: Any
_schema: _TypeMap[Literal["object"]] = {"type": "object"}


class UndefinedType:
"""A singleton object for marking undefined parameters."""

Expand Down
Loading